From 8ba7cec15bf2a6b62d2936ca922c89dba596e368 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 8 Jul 2025 23:22:58 -0400 Subject: [PATCH 001/252] devel release --- CMakeLists.txt | 2 +- Changes | 8 ++++++++ configure.ac | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 570968aea..502871301 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.038 + VERSION 5.039 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index e2b00d973..42ab0c2b3 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,14 @@ The changes in each Verilator version are described below. The contributors that suggested or implemented a given issue are shown in []. Thanks! +Verilator 5.039 devel +========================== + +**Other:** + +* TBD + + Verilator 5.038 2025-07-08 ========================== diff --git a/configure.ac b/configure.ac index 5dae8392e..05757edbb 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.038 2025-07-08], +AC_INIT([Verilator],[5.039 devel], [https://verilator.org], [verilator],[https://verilator.org]) From 8b3a6ba5427700e0c28ea2ec05f9bf8b08f6c78e Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 9 Jul 2025 22:59:26 +0200 Subject: [PATCH 002/252] Support disable dotted references (#6154) --- src/V3AstNodeOther.h | 17 ++++--- src/V3AstNodes.cpp | 14 ++++++ src/V3LinkDot.cpp | 54 ++++++++++++++++++++--- src/V3LinkJump.cpp | 48 +++++++++++--------- src/verilog.y | 6 +-- test_regress/t/t_disable_bad.out | 8 ++++ test_regress/t/t_disable_bad.py | 16 +++++++ test_regress/t/t_disable_bad.v | 11 +++++ test_regress/t/t_disable_empty.out | 5 +++ test_regress/t/t_disable_empty.py | 16 +++++++ test_regress/t/t_disable_empty.v | 17 +++++++ test_regress/t/t_disable_func_bad.out | 5 +++ test_regress/t/t_disable_func_bad.py | 18 ++++++++ test_regress/t/t_disable_func_bad.v | 23 ++++++++++ test_regress/t/t_disable_genfor2.py | 18 ++++++++ test_regress/t/t_disable_genfor2.v | 24 ++++++++++ test_regress/t/t_disable_genfor_unsup.out | 8 ++++ test_regress/t/t_disable_genfor_unsup.py | 16 +++++++ test_regress/t/t_disable_genfor_unsup.v | 25 +++++++++++ test_regress/t/t_disable_task_unsup.out | 5 +++ test_regress/t/t_disable_task_unsup.py | 16 +++++++ test_regress/t/t_disable_task_unsup.v | 27 ++++++++++++ test_regress/t/t_dump.v | 3 ++ test_regress/t/t_dump_json.out | 23 ++++++---- test_regress/t/t_for_disable_dot.out | 5 --- test_regress/t/t_for_disable_dot.py | 6 ++- 26 files changed, 384 insertions(+), 50 deletions(-) create mode 100644 test_regress/t/t_disable_bad.out create mode 100755 test_regress/t/t_disable_bad.py create mode 100644 test_regress/t/t_disable_bad.v create mode 100644 test_regress/t/t_disable_empty.out create mode 100755 test_regress/t/t_disable_empty.py create mode 100644 test_regress/t/t_disable_empty.v create mode 100644 test_regress/t/t_disable_func_bad.out create mode 100755 test_regress/t/t_disable_func_bad.py create mode 100644 test_regress/t/t_disable_func_bad.v create mode 100755 test_regress/t/t_disable_genfor2.py create mode 100644 test_regress/t/t_disable_genfor2.v create mode 100644 test_regress/t/t_disable_genfor_unsup.out create mode 100755 test_regress/t/t_disable_genfor_unsup.py create mode 100644 test_regress/t/t_disable_genfor_unsup.v create mode 100644 test_regress/t/t_disable_task_unsup.out create mode 100755 test_regress/t/t_disable_task_unsup.py create mode 100644 test_regress/t/t_disable_task_unsup.v delete mode 100644 test_regress/t/t_for_disable_dot.out diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 6502a8838..5d2ac8b3c 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -46,6 +46,7 @@ protected: public: ASTGEN_MEMBERS_AstNodeBlock; + bool maybePointedTo() const override VL_MT_SAFE { return true; } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; string name() const override VL_MT_STABLE { return m_name; } // * = Block name @@ -3096,14 +3097,18 @@ public: bool isCycleDelay() const { return m_isCycle; } }; class AstDisable final : public AstNodeStmt { - string m_name; // Name of block + // @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot + // @astgen ptr := m_targetp : Optional[AstNode] // Task or block after V3LinkDot public: - AstDisable(FileLine* fl, const string& name) - : ASTGEN_SUPER_Disable(fl) - , m_name{name} {} + AstDisable(FileLine* fl, AstNodeExpr* targetRefp) + : ASTGEN_SUPER_Disable(fl) { + this->targetRefp(targetRefp); + } ASTGEN_MEMBERS_AstDisable; - string name() const override VL_MT_STABLE { return m_name; } // * = Block name - void name(const string& flag) override { m_name = flag; } + const char* broken() const override; + void dump(std::ostream& str) const override; + void targetp(AstNode* nodep) { m_targetp = nodep; } + AstNode* targetp() const { return m_targetp; } bool isBrancher() const override { return true; // SPECIAL: We don't process code after breaks } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 5c073487b..acdbd9256 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -3177,6 +3177,20 @@ void AstDelay::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, isCycleDelay); dumpJsonGen(str); } + +const char* AstDisable::broken() const { + BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp()))); + return nullptr; +} +void AstDisable::dump(std::ostream& str) const { + this->AstNodeStmt::dump(str); + str << " -> "; + if (targetp()) { + targetp()->dump(str); + } else { + str << "UNLINKED"; + } +} const char* AstAnd::widthMismatch() const VL_MT_STABLE { BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin()); BROKEN_RTN(lhsp()->widthMin() != widthMin()); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 313307919..85da123c9 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2365,6 +2365,7 @@ class LinkDotResolveVisitor final : public VNVisitor { bool m_unresolvedClass; // Unresolved class reference, needs help from V3Param bool m_genBlk; // Contains gen block reference AstNode* m_unlinkedScopep; // Unresolved scope, needs corresponding VarXRef + AstDisable* m_disablep; // Disable statement under which the reference is bool m_dotErr; // Error found in dotted resolution, ignore upwards string m_dotText; // String of dotted names found in below parseref DotStates() { init(nullptr); } @@ -2380,6 +2381,7 @@ class LinkDotResolveVisitor final : public VNVisitor { m_unresolvedClass = false; m_genBlk = false; m_unlinkedScopep = nullptr; + m_disablep = nullptr; } string ascii() const { static const char* const names[] @@ -3084,7 +3086,11 @@ class LinkDotResolveVisitor final : public VNVisitor { bool allowVar = false; bool allowFTask = false; bool staticAccess = false; - if (m_ds.m_dotPos == DP_PACKAGE) { + if (m_ds.m_disablep) { + allowScope = true; + allowFTask = true; + expectWhat = "block/task"; + } else if (m_ds.m_dotPos == DP_PACKAGE) { // {package-or-class}::{a} AstNodeModule* classOrPackagep = nullptr; expectWhat = "scope/variable/func"; @@ -3185,7 +3191,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } } if (!foundp) { - } else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), Begin) + } else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), NodeBlock) || VN_IS(foundp->nodep(), Netlist) // for $root || VN_IS(foundp->nodep(), Module)) { // if top if (allowScope) { @@ -3193,8 +3199,20 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name()); m_ds.m_dotSymp = foundp; m_ds.m_dotPos = DP_SCOPE; + if (m_ds.m_disablep && VN_IS(foundp->nodep(), NodeBlock)) { + // Possibly it is not the final link. If we are under dot and not in its + // last component, `targetp()` field will be overwritten by next components + m_ds.m_disablep->targetp(foundp->nodep()); + } if (const AstBegin* const beginp = VN_CAST(foundp->nodep(), Begin)) { - if (beginp->generate()) m_ds.m_genBlk = true; + if (beginp->generate()) { + m_ds.m_genBlk = true; + if (m_ds.m_disablep) { + m_ds.m_disablep->v3warn( + E_UNSUPPORTED, + "Unsupported: Generate block referenced by disable"); + } + } } // Upper AstDot visitor will handle it from here } else if (VN_IS(foundp->nodep(), Cell) && allowVar) { @@ -3438,7 +3456,8 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.m_dotPos = DP_MEMBER; } else { // Cells/interfaces can't be implicit - const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !foundp); + const bool checkImplicit + = (!m_ds.m_dotp && m_ds.m_dotText == "" && !m_ds.m_disablep && !foundp); const bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); if (err) { @@ -3845,7 +3864,8 @@ class LinkDotResolveVisitor final : public VNVisitor { // HERE function() . method_called_on_function_return_value() m_ds.m_dotPos = DP_MEMBER; m_ds.m_dotText = ""; - } else { + } else if (!m_ds.m_disablep) { + // visit(AstDisable*) setup the dot handling checkNoDot(nodep); } if (nodep->classOrPackagep() && nodep->taskp()) { @@ -4510,6 +4530,30 @@ class LinkDotResolveVisitor final : public VNVisitor { } VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } + void visit(AstDisable* nodep) override { + LINKDOT_VISIT_START(); + checkNoDot(nodep); + VL_RESTORER(m_ds); + m_ds.init(m_curSymp); + m_ds.m_dotPos = DP_FIRST; + m_ds.m_disablep = nodep; + iterateChildren(nodep); + if (nodep->targetRefp()) { + if (AstTaskRef* const taskRefp = VN_CAST(nodep->targetRefp(), TaskRef)) { + nodep->targetp(taskRefp->taskp()); + } else if (!VN_IS(nodep->targetRefp(), ParseRef)) { + // If it is a ParseRef, either it couldn't be linked or it is linked to a block + nodep->v3warn(E_UNSUPPORTED, "Node of type " + << nodep->targetRefp()->prettyTypeName() + << " referenced by disable"); + pushDeletep(nodep->unlinkFrBack()); + } + if (nodep->targetp()) { + // If the target is already linked, there is no need to store reference as child + VL_DO_DANGLING(nodep->targetRefp()->unlinkFrBack()->deleteTree(), nodep); + } + } + } void visit(AstPackageImport* nodep) override { // No longer needed LINKDOT_VISIT_START(); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index a97d11f58..3cfb7afab 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -337,33 +337,41 @@ class LinkJumpVisitor final : public VNVisitor { } void visit(AstDisable* nodep) override { UINFO(8, " DISABLE " << nodep); - iterateChildren(nodep); - AstNodeBlock* blockp = nullptr; - for (AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) { - UINFO(9, " UNDERBLK " << stackp); - if (stackp->name() == nodep->name()) { - blockp = stackp; - break; + AstNode* const targetp = nodep->targetp(); + UASSERT_OBJ(targetp, nodep, "Unlinked disable statement"); + if (VN_IS(targetp, Task)) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name"); + } else if (VN_IS(targetp, Fork)) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name"); + } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { + const std::string targetName = beginp->name(); + bool aboveBlock = false; + for (AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) { + UINFO(9, " UNDERBLK " << stackp); + if (stackp->name() == targetName) { + aboveBlock = true; + break; + } } - } - // if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); } - if (!blockp) { - nodep->v3warn(E_UNSUPPORTED, - "disable isn't underneath a begin with name: " << nodep->prettyNameQ()); - } else if (AstBegin* const beginp = VN_CAST(blockp, Begin)) { - if (beginp->user3()) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling block that contains a fork"); + if (aboveBlock) { + if (beginp->user3()) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: disabling block that contains a fork"); + } else { + // Jump to the end of the named block + AstJumpLabel* const labelp = findAddLabel(beginp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + } } else { - // Jump to the end of the named block - AstJumpLabel* const labelp = findAddLabel(beginp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" + << targetName << "'"); } } else { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name"); + nodep->v3fatalSrc("Disable linked with node of unhandled type " + << targetp->prettyTypeName()); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); - // if (debug() >= 9) { UINFO(0, "\n"); beginp->dumpTree("- labelo: "); } } void visit(AstVarRef* nodep) override { if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true); diff --git a/src/verilog.y b/src/verilog.y index dfdcf3e80..f82f79ed3 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3753,10 +3753,8 @@ statement_item: // IEEE: statement_item // // // IEEE: disable_statement | yDISABLE yFORK ';' { $$ = new AstDisableFork{$1}; } - | yDISABLE idAny/*UNSUP: hierarchical_identifier-task_or_block*/ ';' - { $$ = new AstDisable{$1, *$2}; } - | yDISABLE idAny '.' idDottedSel ';' - { $$ = nullptr; BBUNSUP($4, "Unsupported: disable with '.'"); } + | yDISABLE idDottedSel ';' + { $$ = new AstDisable{$1, $2}; } // // IEEE: event_trigger | yP_MINUSGT expr ';' { $$ = new AstFireEvent{$1, $2, false}; } diff --git a/test_regress/t/t_disable_bad.out b/test_regress/t/t_disable_bad.out new file mode 100644 index 000000000..35f743a9b --- /dev/null +++ b/test_regress/t/t_disable_bad.out @@ -0,0 +1,8 @@ +%Error: t/t_disable_bad.v:9:15: Can't find definition of block/task: 'abcd' + 9 | disable abcd; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Internal Error: t/t_disable_bad.v:9:7: ../V3LinkJump.cpp:#: Unlinked disable statement + 9 | disable abcd; + | ^~~~~~~ + ... This fatal error may be caused by the earlier error(s); resolve those first. diff --git a/test_regress/t/t_disable_bad.py b/test_regress/t/t_disable_bad.py new file mode 100755 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_disable_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_bad.v b/test_regress/t/t_disable_bad.v new file mode 100644 index 000000000..b056b1b7f --- /dev/null +++ b/test_regress/t/t_disable_bad.v @@ -0,0 +1,11 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + initial begin + disable abcd; + end +endmodule: t diff --git a/test_regress/t/t_disable_empty.out b/test_regress/t/t_disable_empty.out new file mode 100644 index 000000000..48d8fd58b --- /dev/null +++ b/test_regress/t/t_disable_empty.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_disable_empty.v:12:7: disable isn't underneath a begin with name: 'block' + 12 | disable block; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_disable_empty.py b/test_regress/t/t_disable_empty.py new file mode 100755 index 000000000..44c75cc0f --- /dev/null +++ b/test_regress/t/t_disable_empty.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('simulator') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_empty.v b/test_regress/t/t_disable_empty.v new file mode 100644 index 000000000..aac889913 --- /dev/null +++ b/test_regress/t/t_disable_empty.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, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/); + + initial begin + if (0) begin : block + end + disable block; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_disable_func_bad.out b/test_regress/t/t_disable_func_bad.out new file mode 100644 index 000000000..0b3a8de88 --- /dev/null +++ b/test_regress/t/t_disable_func_bad.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_disable_func_bad.v:19:13: Node of type FUNCREF 'increment_x' referenced by disable + 19 | #1 disable increment_x; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_disable_func_bad.py b/test_regress/t/t_disable_func_bad.py new file mode 100755 index 000000000..cf9cba66e --- /dev/null +++ b/test_regress/t/t_disable_func_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 --timing'], + fails=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_func_bad.v b/test_regress/t/t_disable_func_bad.v new file mode 100644 index 000000000..587987cd0 --- /dev/null +++ b/test_regress/t/t_disable_func_bad.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +int x = 0; + +function int increment_x; + x++; + return x; +endfunction + +module t(/*AUTOARG*/); + + initial begin + fork + increment_x(); + #1 disable increment_x; + join + end + +endmodule diff --git a/test_regress/t/t_disable_genfor2.py b/test_regress/t/t_disable_genfor2.py new file mode 100755 index 000000000..a4e75e7ad --- /dev/null +++ b/test_regress/t/t_disable_genfor2.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=["--exe --main --timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_genfor2.v b/test_regress/t/t_disable_genfor2.v new file mode 100644 index 000000000..eb9382f99 --- /dev/null +++ b/test_regress/t/t_disable_genfor2.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t ( /*AUTOARG*/); + for (genvar j = 0; j < 3; j++) begin : genblk + initial begin : init + int i; + begin : named + for (i = 0; i < 10; ++i) begin : loop + if (i == 5) disable named; + end + end + if (i != 5) $stop; + end + end + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_disable_genfor_unsup.out b/test_regress/t/t_disable_genfor_unsup.out new file mode 100644 index 000000000..a1e2054c9 --- /dev/null +++ b/test_regress/t/t_disable_genfor_unsup.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_disable_genfor_unsup.v:13:23: Unsupported: Generate block referenced by disable + 13 | if (i == 5) disable t.genblk[0].init.named; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_disable_genfor_unsup.v:13:23: Node of type CELLREF referenced by disable + 13 | if (i == 5) disable t.genblk[0].init.named; + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_disable_genfor_unsup.py b/test_regress/t/t_disable_genfor_unsup.py new file mode 100755 index 000000000..44c75cc0f --- /dev/null +++ b/test_regress/t/t_disable_genfor_unsup.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('simulator') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_genfor_unsup.v b/test_regress/t/t_disable_genfor_unsup.v new file mode 100644 index 000000000..0f8e05c16 --- /dev/null +++ b/test_regress/t/t_disable_genfor_unsup.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t ( /*AUTOARG*/); + for (genvar j = 0; j < 3; j++) begin : genblk + initial begin : init + int i; + begin : named + for (i = 0; i < 10; ++i) begin : loop + if (i == 5) disable t.genblk[0].init.named; + end + end + if (j == 0 && i != 5) $stop; + if (j != 0 && i != 10) $stop; + end + end + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_disable_task_unsup.out b/test_regress/t/t_disable_task_unsup.out new file mode 100644 index 000000000..e5628d22a --- /dev/null +++ b/test_regress/t/t_disable_task_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_disable_task_unsup.v:20:13: Unsupported: disabling task by name + 20 | #1 disable increment_x; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_disable_task_unsup.py b/test_regress/t/t_disable_task_unsup.py new file mode 100755 index 000000000..44c75cc0f --- /dev/null +++ b/test_regress/t/t_disable_task_unsup.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('simulator') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_task_unsup.v b/test_regress/t/t_disable_task_unsup.v new file mode 100644 index 000000000..0ab943842 --- /dev/null +++ b/test_regress/t/t_disable_task_unsup.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +int x = 0; + +task increment_x; + x++; + #2; + x++; +endtask + +module t(/*AUTOARG*/); + + initial begin + fork + increment_x(); + #1 disable increment_x; + join + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_dump.v b/test_regress/t/t_dump.v index 4f706d700..dca21e9d8 100644 --- a/test_regress/t/t_dump.v +++ b/test_regress/t/t_dump.v @@ -88,5 +88,8 @@ module Test(/*AUTOARG*/ assert(0); $asserton; $assertcontrol(3, 8); + begin : blk + disable blk; + end end endmodule diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index fa903003a..36d58e850 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -510,22 +510,29 @@ ], "assertTypesp": [ {"type":"CONST","name":"?32?sh8","addr":"(GI)","loc":"e,90:25,90:26","dtypep":"(JF)"} - ],"directiveTypesp": []} + ],"directiveTypesp": []}, + {"type":"BEGIN","name":"blk","addr":"(HI)","loc":"e,91:15,91:18","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], + "stmtsp": [ + {"type":"DISABLE","name":"","addr":"(II)","loc":"e,92:10,92:17", + "targetRefp": [ + {"type":"PARSEREF","name":"blk","addr":"(JI)","loc":"e,92:18,92:21","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ]} ]} ]} ]} ]} ],"filesp": [], "miscsp": [ - {"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(HI)", + {"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(KI)", "typesp": [ - {"type":"BASICDTYPE","name":"integer","addr":"(II)","loc":"d,34:27,34:28","dtypep":"(II)","keyword":"integer","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"integer","addr":"(LI)","loc":"d,34:27,34:28","dtypep":"(LI)","keyword":"integer","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(L)","loc":"d,36:32,36:33","dtypep":"(L)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(UE)","loc":"d,53:22,53:24","dtypep":"(UE)","keyword":"logic","generic":true,"rangep": []}, - {"type":"VOIDDTYPE","name":"","addr":"(HI)","loc":"d,54:21,54:30","dtypep":"(HI)","generic":false}, + {"type":"VOIDDTYPE","name":"","addr":"(KI)","loc":"d,54:21,54:30","dtypep":"(KI)","generic":false}, {"type":"BASICDTYPE","name":"logic","addr":"(QD)","loc":"d,128:22,128:23","dtypep":"(QD)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(JI)","loc":"d,130:22,130:23","dtypep":"(JI)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(KI)","loc":"d,165:17,165:56","dtypep":"(KI)","keyword":"logic","range":"295:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(MI)","loc":"d,130:22,130:23","dtypep":"(MI)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(NI)","loc":"d,165:17,165:56","dtypep":"(NI)","keyword":"logic","range":"295:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"string","addr":"(BG)","loc":"d,165:10,165:16","dtypep":"(BG)","keyword":"string","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(Q)","loc":"e,14:9,14:11","dtypep":"(Q)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(BB)","loc":"e,18:10,18:12","dtypep":"(BB)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, @@ -540,9 +547,9 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(LI)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(OI)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ - {"type":"SCOPE","name":"@CONST-POOL@","addr":"(MI)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(LI)","varsp": [],"blocksp": [],"inlinesp": []} + {"type":"SCOPE","name":"@CONST-POOL@","addr":"(PI)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(OI)","varsp": [],"blocksp": [],"inlinesp": []} ]} ]} ]} diff --git a/test_regress/t/t_for_disable_dot.out b/test_regress/t/t_for_disable_dot.out deleted file mode 100644 index fe6edd7f7..000000000 --- a/test_regress/t/t_for_disable_dot.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_for_disable_dot.v:14:35: Unsupported: disable with '.' - 14 | if (i == 5) disable t.named; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_for_disable_dot.py b/test_regress/t/t_for_disable_dot.py index 30c3d4f77..d4f986441 100755 --- a/test_regress/t/t_for_disable_dot.py +++ b/test_regress/t/t_for_disable_dot.py @@ -9,8 +9,10 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() From dbfbc657e16fb7281763e83342a1d280f5ae94d4 Mon Sep 17 00:00:00 2001 From: Igor Zaworski <84542280+igorosky@users.noreply.github.com> Date: Wed, 9 Jul 2025 23:12:11 +0200 Subject: [PATCH 003/252] Fix class extends dotted error (#6162) --- src/V3LinkDot.cpp | 1 + ...m_extends_static_member_function_access.py | 18 +++++++++++++ ...am_extends_static_member_function_access.v | 26 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100755 test_regress/t/t_class_param_extends_static_member_function_access.py create mode 100644 test_regress/t/t_class_param_extends_static_member_function_access.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 85da123c9..b84b8ba6c 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -4233,6 +4233,7 @@ class LinkDotResolveVisitor final : public VNVisitor { if (nodep->classOrNullp()) return; LINKDOT_VISIT_START(); if (m_statep->forPrimary()) { + if (nodep->childDTypep()) return; AstNode* cprp = nodep->classOrPkgsp(); VSymEnt* lookSymp = m_curSymp; if (AstDot* const dotp = VN_CAST(cprp, Dot)) { diff --git a/test_regress/t/t_class_param_extends_static_member_function_access.py b/test_regress/t/t_class_param_extends_static_member_function_access.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_param_extends_static_member_function_access.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_param_extends_static_member_function_access.v b/test_regress/t/t_class_param_extends_static_member_function_access.v new file mode 100644 index 000000000..09cf19ee2 --- /dev/null +++ b/test_regress/t/t_class_param_extends_static_member_function_access.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1 #(type T); + static function int get_p(); + return 7; + endfunction +endclass + +class Class2 #(type T) extends Class1 #(T); + static function int get_p2; + return T::get_p(); + endfunction +endclass + +module t; + initial begin + typedef Class2#(Class1#(int)) Class; + if (Class::get_p2() != Class1#(int)::get_p()) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From a84c5d201035e46718ce26a7a170079384d32dec Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 9 Jul 2025 18:51:49 -0400 Subject: [PATCH 004/252] Commentary: Changes update --- Changes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 42ab0c2b3..d7b06fea8 100644 --- a/Changes +++ b/Changes @@ -13,7 +13,8 @@ Verilator 5.039 devel **Other:** -* TBD +* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] +* Fix class extends dotted error (#6162). [Igor Zaworski] Verilator 5.038 2025-07-08 From 597b973f7b8077829ab407601012ea677f90d873 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 9 Jul 2025 18:52:26 -0400 Subject: [PATCH 005/252] Internals: Fix some style issues. No functional change. --- src/V3Control.cpp | 18 +++++++++--------- src/V3EmitCSyms.cpp | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 69647d346..ee3f49d33 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -724,34 +724,34 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta void V3Control::applyCase(AstCase* nodep) { const string& filename = nodep->fileline()->filename(); - V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename); + V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); if (filep) filep->applyCase(nodep); } void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) { const string& filename = nodep->fileline()->filename(); - V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename); + V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); if (filep) filep->applyBlock(nodep); const string& modname = modulep->name(); - V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname); + V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (modp) modp->applyBlock(nodep); } void V3Control::applyIgnores(FileLine* filelinep) { const string& filename = filelinep->filename(); - V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename); + V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); if (filep) filep->applyIgnores(filelinep); } void V3Control::applyModule(AstNodeModule* modulep) { const string& modname = modulep->origName(); - V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname); + V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (modp) modp->apply(modulep); } void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) { const string& modname = modulep->name(); - V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname); + V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (!modp) return; const V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->name()); if (ftp) ftp->apply(ftaskp); @@ -759,10 +759,10 @@ void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) { void V3Control::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp) { V3ControlVar* vp; - V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modulep->name()); + V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modulep->name()); if (!modp) return; if (ftaskp) { - V3ControlFTask* ftp = modp->ftasks().resolve(ftaskp->name()); + V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->name()); if (!ftp) return; vp = ftp->vars().resolve(varp->name()); } else { @@ -797,7 +797,7 @@ bool V3Control::containsMTaskProfileData() { } bool V3Control::waive(FileLine* filelinep, V3ErrorCode code, const string& message) { - V3ControlFile* filep = V3ControlResolver::s().files().resolve(filelinep->filename()); + V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filelinep->filename()); if (!filep) return false; return filep->waive(code, message); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index bde94b017..a07b431c6 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -743,13 +743,13 @@ void EmitCSyms::emitSymImp() { if (!optSystemC()) { puts("\nvoid " + symClassName() + "::_traceDump() {\n"); // Caller checked for __Vm_dumperp non-nullptr - puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumperp->dump(VL_TIME_Q());\n"); puts("}\n"); } puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n"); - puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n"); puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n"); @@ -760,7 +760,7 @@ void EmitCSyms::emitSymImp() { puts("}\n"); puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n"); - puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumping = false;\n"); puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n"); puts("}\n"); From 9fc7143fcef124d522520b95b1f42c290841a3db Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 9 Jul 2025 19:11:48 -0400 Subject: [PATCH 006/252] Fix genvar error with `-O0` (#6165). --- Changes | 1 + src/V3EmitCBase.cpp | 2 +- src/V3EmitCHeaders.cpp | 3 ++- test_regress/t/t_opt_0.py | 16 ++++++++++++++++ test_regress/t/t_opt_0.v | 14 ++++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_opt_0.py create mode 100644 test_regress/t/t_opt_0.v diff --git a/Changes b/Changes index d7b06fea8..7e26899a3 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.039 devel * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Fix class extends dotted error (#6162). [Igor Zaworski] +* Fix genvar error with `-O0` (#6165). [Max Wipfli] Verilator 5.038 2025-07-08 diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index a84980080..ed88656af 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -181,7 +181,7 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { const AstBasicDType* const basicp = nodep->basicp(); - bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); + const bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); const auto emitDeclArrayBrackets = [this](const AstVar* nodep) -> void { // This isn't very robust and may need cleanup for other data types diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 16c6f6314..f8ae0d50a 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -109,7 +109,8 @@ class EmitCHeader final : public EmitCConstInit { // Emit variables in consecutive anon and non-anon batches for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { - if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() + || varp->isGenVar()) { const bool anon = isAnonOk(varp); if (anon != lastAnon) emitCurrentList(); lastAnon = anon; diff --git a/test_regress/t/t_opt_0.py b/test_regress/t/t_opt_0.py new file mode 100755 index 000000000..38bb29595 --- /dev/null +++ b/test_regress/t/t_opt_0.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(verilator_flags2=['-O0']) + +test.passes() diff --git a/test_regress/t/t_opt_0.v b/test_regress/t/t_opt_0.v new file mode 100644 index 000000000..225e16609 --- /dev/null +++ b/test_regress/t/t_opt_0.v @@ -0,0 +1,14 @@ +// 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; + for (genvar k = 0; k < 1; k++) begin : gen_empty + // empty + end + initial for (int i = 0; i < 1; i++) begin : gen_i + // empty + end +endmodule From d1462f31200a8e5e0d63fc052a87adbefe653830 Mon Sep 17 00:00:00 2001 From: Martin Stadler Date: Thu, 10 Jul 2025 01:31:42 +0200 Subject: [PATCH 007/252] Fix cmake `-Wno` compiler flag testing (#6145) --- verilator-config.cmake.in | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/verilator-config.cmake.in b/verilator-config.cmake.in index 13e386511..6214c02d4 100644 --- a/verilator-config.cmake.in +++ b/verilator-config.cmake.in @@ -68,15 +68,45 @@ function(_verilator_check_cxx_libraries LIBRARIES RESVAR) set("${RESVAR}" "${${RESVAR}}" PARENT_SCOPE) endfunction() +function(_verilator_check_cxx_compiler_flag FLAG RESVAR) + # Check whether the compiler understands the flag FLAG + set(_VERILATOR_CHECK_CXX_COMPILER_FLAG_SRC + " + #warning warning + int main() { return 0; } + " + ) + set(CMAKE_REQUIRED_FLAGS ${FLAG}) + # Make sure the compiler warning is not turned into an error + string(APPEND CMAKE_REQUIRED_FLAGS " -Wno-error=cpp") + set(CMAKE_REQUIRED_DEFINITIONS) + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LINK_OPTIONS) + set(CMAKE_REQUIRED_LIBRARIES) + set(CMAKE_REQUIRED_QUIET) + + check_compiler_flag_common_patterns(_common_patterns) + check_cxx_source_compiles( + "${_VERILATOR_CHECK_CXX_COMPILER_FLAG_SRC}" + "${RESVAR}" + FAIL_REGEX + "command[ -]line option .* is valid for .* but not for C[+][+]" + FAIL_REGEX + "-Werror=.* argument .* is not valid for C[+][+]" + ${_common_patterns} + ) + set("${RESVAR}" "${${RESVAR}}" PARENT_SCOPE) +endfunction() + # Check compiler flag support. Skip on MSVC, these are all GCC flags. if(NOT CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(NOT DEFINED VERILATOR_CFLAGS OR NOT DEFINED VERILATOR_MT_CFLAGS) include(CheckCXXCompilerFlag) foreach(FLAG @CFG_CXX_FLAGS_CMAKE@) string(MAKE_C_IDENTIFIER ${FLAG} FLAGNAME) - check_cxx_compiler_flag(${FLAG} ${FLAGNAME}) + _verilator_check_cxx_compiler_flag(${FLAG} ${FLAGNAME}) if(${FLAGNAME}) - list(APPEND VERILATOR_CFLAGS ${FLAG}) + list(APPEND VERILATOR_CFLAGS $<$:${FLAG}>) endif() endforeach() foreach(FLAG @CFG_LDFLAGS_THREADS_CMAKE@) From d89df33fcd6de49b30a4d42a469e1f9de2da8aba Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 9 Jul 2025 20:48:00 -0400 Subject: [PATCH 008/252] Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). --- Changes | 1 + src/V3Ast.h | 3 +++ src/V3AstNodes.cpp | 2 ++ src/V3Control.cpp | 19 +++++++------- src/verilog.y | 6 +++-- test_regress/t/t_dpi_var_vlt.py | 13 +++++----- test_regress/t/t_vpi_escape.cpp | 46 +++++++++++++++++++++++---------- test_regress/t/t_vpi_escape.py | 2 +- test_regress/t/t_vpi_escape.v | 39 +++++++++++++++------------- test_regress/t/t_vpi_escape.vlt | 9 +++++++ 10 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 test_regress/t/t_vpi_escape.vlt diff --git a/Changes b/Changes index 7e26899a3..d6e49d708 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.039 devel **Other:** * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] +* Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Fix class extends dotted error (#6162). [Igor Zaworski] * Fix genvar error with `-O0` (#6165). [Max Wipfli] diff --git a/src/V3Ast.h b/src/V3Ast.h index dd350519f..a7161e2a1 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2222,6 +2222,9 @@ public: // ACCESSORS virtual string name() const VL_MT_STABLE { return ""; } virtual string origName() const { return ""; } + string prettyOrigOrName() const { + return prettyName(origName().empty() ? name() : origName()); + } virtual void name(const string& name) { this->v3fatalSrc("name() called on object without name() method"); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index acdbd9256..3307eedfc 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2604,6 +2604,8 @@ void AstVar::dump(std::ostream& str) const { if (isPulldown()) str << " [PULLDOWN]"; if (isUsedClock()) str << " [CLK]"; if (isSigPublic()) str << " [P]"; + if (isSigUserRdPublic()) str << " [PRD]"; + if (isSigUserRWPublic()) str << " [PWR]"; if (isInternal()) str << " [INTERNAL]"; if (isLatched()) str << " [LATCHED]"; if (isUsedLoopIdx()) str << " [LOOP]"; diff --git a/src/V3Control.cpp b/src/V3Control.cpp index ee3f49d33..36402417b 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -276,7 +276,7 @@ public: const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF; if (!nodep->unnamed()) { for (const string& i : m_coverageOffBlocks) { - if (VString::wildmatch(nodep->name(), i)) { + if (VString::wildmatch(nodep->prettyOrigOrName(), i)) { nodep->addStmtsp(new AstPragma{nodep->fileline(), pragma}); } } @@ -732,7 +732,7 @@ void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) { const string& filename = nodep->fileline()->filename(); V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); if (filep) filep->applyBlock(nodep); - const string& modname = modulep->name(); + const string& modname = modulep->prettyOrigOrName(); V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (modp) modp->applyBlock(nodep); } @@ -744,29 +744,30 @@ void V3Control::applyIgnores(FileLine* filelinep) { } void V3Control::applyModule(AstNodeModule* modulep) { - const string& modname = modulep->origName(); + const string& modname = modulep->prettyOrigOrName(); V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (modp) modp->apply(modulep); } void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) { - const string& modname = modulep->name(); + const string& modname = modulep->prettyOrigOrName(); V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); if (!modp) return; - const V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->name()); + const V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->prettyOrigOrName()); if (ftp) ftp->apply(ftaskp); } void V3Control::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp) { V3ControlVar* vp; - V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modulep->name()); + V3ControlModule* const modp + = V3ControlResolver::s().modules().resolve(modulep->prettyOrigOrName()); if (!modp) return; if (ftaskp) { - V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->name()); + V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->prettyOrigOrName()); if (!ftp) return; - vp = ftp->vars().resolve(varp->name()); + vp = ftp->vars().resolve(varp->prettyOrigOrName()); } else { - vp = modp->vars().resolve(varp->name()); + vp = modp->vars().resolve(varp->prettyOrigOrName()); } if (vp) vp->apply(varp); } diff --git a/src/verilog.y b/src/verilog.y index f82f79ed3..085182f07 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -8087,8 +8087,10 @@ vltDModule: // --module ; vltDModuleE: // [--module ] - /* empty */ { static string unit = "__024unit"; $$ = &unit; } - | vltDModule { $$ = $1; } + /* empty */ + { static string unit = "$unit"; $$ = &unit; } // .vlt uses prettyName + | vltDModule + { $$ = $1; } ; vltDScope: // --scope diff --git a/test_regress/t/t_dpi_var_vlt.py b/test_regress/t/t_dpi_var_vlt.py index ad82e250d..c43dca9c4 100755 --- a/test_regress/t/t_dpi_var_vlt.py +++ b/test_regress/t/t_dpi_var_vlt.py @@ -25,19 +25,18 @@ test.compile(make_top_shell=False, if test.vlt_all: test.file_grep( out_filename, - r'{"type":"VAR","name":"formatted","addr":"[^"]*","loc":"\w,58:[^"]*",.*"origName":"formatted",.*"direction":"INPUT",.*"dtypeName":"string",.*"attrSFormat":true' + r'{"type":"VAR","name":"formatted",.*"origName":"formatted",.*"direction":"INPUT",.*"attrSFormat":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.in","addr":"[^"]*","loc":"\w,81:[^"]*",.*"origName":"in",.*"dtypeName":"int",.*"isSigUserRdPublic":true' + r'{"type":"VAR","name":"t.sub.in",.*"origName":"in",.*"isSigUserRdPublic":true') + test.file_grep( + out_filename, + r'{"type":"VAR","name":"t.sub.fr_a",.*"origName":"fr_a",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.fr_a","addr":"[^"]*","loc":"\w,82:[^"]*",.*"origName":"fr_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' - ) - test.file_grep( - out_filename, - r'{"type":"VAR","name":"t.sub.fr_b","addr":"[^"]*","loc":"\w,83:[^"]*",.*"origName":"fr_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + r'{"type":"VAR","name":"t.sub.fr_b",.*"origName":"fr_b",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.execute() diff --git a/test_regress/t/t_vpi_escape.cpp b/test_regress/t/t_vpi_escape.cpp index 73b7875fd..0ba0c24bc 100644 --- a/test_regress/t/t_vpi_escape.cpp +++ b/test_regress/t/t_vpi_escape.cpp @@ -58,7 +58,13 @@ const char* _my_rooted(const char* obj) { return buf.c_str(); } -#define MY_VPI_HANDLE(signal) vpi_handle_by_name(const_cast(_my_rooted(signal)), NULL); +TestVpiHandle my_vpi_handle(const char* signal) { +#ifdef TEST_VERBOSE + printf("-my_vpi_handle(\"%s\")\n", _my_rooted(signal)); +#endif + TestVpiHandle vh = vpi_handle_by_name(const_cast(_my_rooted(signal)), NULL); + return vh; +} int _mon_check_var() { #ifdef TEST_VERBOSE @@ -67,7 +73,7 @@ int _mon_check_var() { TestVpiHandle vh1 = vpi_handle_by_name(const_cast(_sim_top()), NULL); TEST_CHECK_NZ(vh1); - TestVpiHandle vh2 = MY_VPI_HANDLE("\\check;alias "); + TestVpiHandle vh2 = my_vpi_handle("\\check;alias "); TEST_CHECK_NZ(vh2); // scope attributes @@ -107,15 +113,29 @@ int _mon_check_var() { TEST_CHECK_CSTR(p, "vpiNet"); } - TestVpiHandle vh4 = MY_VPI_HANDLE("\\x.y "); + TestVpiHandle vh4 = my_vpi_handle("\\x.y "); TEST_CHECK_NZ(vh4); // Test that the toplevel TOP.xxxxx search is skipped // when the path to the scope has more than one level. - TestVpiHandle vh5 = MY_VPI_HANDLE("\\mod.with_dot .\\b.c "); - TEST_CHECK_NZ(vh5); - p = vpi_get_str(vpiFullName, vh5); - TEST_CHECK_CSTR(p, "\\t.has.dots .\\mod.with_dot .\\b.c "); + { + TestVpiHandle vh5 = my_vpi_handle("\\mod.with_dot .\\b.c "); + TEST_CHECK_NZ(vh5); + p = vpi_get_str(vpiFullName, vh5); + TEST_CHECK_CSTR(p, "\\t.has.dots .\\mod.with_dot .\\b.c "); + } + { + TestVpiHandle vh5 = my_vpi_handle("double__underscore"); + TEST_CHECK_NZ(vh5); + p = vpi_get_str(vpiFullName, vh5); + TEST_CHECK_CSTR(p, "TOP.double__underscore"); + } + { + TestVpiHandle vh5 = my_vpi_handle("double__underscore__vlt"); + TEST_CHECK_NZ(vh5); + p = vpi_get_str(vpiFullName, vh5); + TEST_CHECK_CSTR(p, "TOP.double__underscore__vlt"); + } return errors; } @@ -126,7 +146,7 @@ int _mon_check_iter() { #endif const char* p; - TestVpiHandle vh2 = MY_VPI_HANDLE("\\mod.with_dot "); + TestVpiHandle vh2 = my_vpi_handle("\\mod.with_dot "); TEST_CHECK_NZ(vh2); p = vpi_get_str(vpiName, vh2); TEST_CHECK_CSTR(p, "\\mod.with_dot "); @@ -135,12 +155,12 @@ int _mon_check_iter() { TEST_CHECK_CSTR(p, "sub"); } - TestVpiHandle vh_null_name = MY_VPI_HANDLE("___0_"); + TestVpiHandle vh_null_name = my_vpi_handle("___0_"); TEST_CHECK_NZ(vh_null_name); p = vpi_get_str(vpiName, vh_null_name); TEST_CHECK_CSTR(p, "___0_"); - TestVpiHandle vh_hex_name = MY_VPI_HANDLE("___0F_"); + TestVpiHandle vh_hex_name = my_vpi_handle("___0F_"); TEST_CHECK_NZ(vh_hex_name); p = vpi_get_str(vpiName, vh_hex_name); TEST_CHECK_CSTR(p, "___0F_"); @@ -198,7 +218,7 @@ int _mon_check_ports() { printf("-mon_check_ports()\n"); #endif // test writing to input port - TestVpiHandle vh1 = MY_VPI_HANDLE("a"); + TestVpiHandle vh1 = my_vpi_handle("a"); TEST_CHECK_NZ(vh1); PLI_INT32 d; @@ -236,7 +256,7 @@ int _mon_check_ports() { TEST_CHECK_EQ(v.value.integer, 2); // get handle of toplevel module - TestVpiHandle vht = MY_VPI_HANDLE(""); + TestVpiHandle vht = my_vpi_handle(""); TEST_CHECK_NZ(vht); d = vpi_get(vpiType, vht); @@ -263,7 +283,7 @@ int _mon_check_ports() { TEST_CHECK_EQ(handleName1, handleName2); - TestVpiHandle vh2 = MY_VPI_HANDLE("\\b.c "); + TestVpiHandle vh2 = my_vpi_handle("\\b.c "); TEST_CHECK_NZ(vh2); if (TestSimulator::is_verilator()) { diff --git a/test_regress/t/t_vpi_escape.py b/test_regress/t/t_vpi_escape.py index e216188d9..dcc91956c 100755 --- a/test_regress/t/t_vpi_escape.py +++ b/test_regress/t/t_vpi_escape.py @@ -18,7 +18,7 @@ test.compile(make_top_shell=False, sim_time=100, iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI -DWAVES"], v_flags2=["+define+USE_VPI_NOT_DPI"], - verilator_flags2=["--exe --vpi --no-l2name --public-flat-rw", test.pli_filename]) + verilator_flags2=["--exe --vpi --no-l2name t/t_vpi_escape.vlt", test.pli_filename]) test.execute( # run_env = "VPI_TRACE=" . Cwd::getcwd() . "/" + test.obj_dir + "/" + test.name + "_vpi.log", diff --git a/test_regress/t/t_vpi_escape.v b/test_regress/t/t_vpi_escape.v index a912c1096..4ac951067 100644 --- a/test_regress/t/t_vpi_escape.v +++ b/test_regress/t/t_vpi_escape.v @@ -13,11 +13,12 @@ import "DPI-C" context function int mon_check(); `endif module \t.has.dots (/*AUTOARG*/ - // Outputs - \escaped_normal , double__underscore, \9num , \bra[ket]slash/dash-colon:9backslash\done , \x.y , - // Inputs - clk, \b.c , a - ); + // Outputs + \escaped_normal , double__underscore, double__underscore__vlt, \9num , + \bra[ket]slash/dash-colon:9backslash\done , \x.y , + // Inputs + clk, a, \b.c + ); `ifdef VERILATOR `systemc_header @@ -26,16 +27,18 @@ extern "C" int mon_check(); `endif input clk; - input [7:0] a; - input \b.c ; + input [7:0] a /*verilator public_flat_rw*/; + input \b.c /*verilator public_flat_rw*/; - integer cyc; initial cyc=1; + int cyc /*verilator public_flat_rd*/; - output \escaped_normal ; + output \escaped_normal /*verilator public_flat_rd*/; wire \escaped_normal = cyc[0]; - output double__underscore ; + output double__underscore /*verilator public_flat_rd*/; wire double__underscore = cyc[0]; + output double__underscore__vlt; // public in .vlt + wire double__underscore__vlt = cyc[0]; // C doesn't allow leading non-alpha, so must escape output \9num ; @@ -44,14 +47,14 @@ extern "C" int mon_check(); output \bra[ket]slash/dash-colon:9backslash\done ; wire \bra[ket]slash/dash-colon:9backslash\done = cyc[0]; - output \x.y ; + output \x.y /*verilator public_flat_rd*/; wire \x.y = cyc[0]; wire \wire = cyc[0]; - wire \check_alias = cyc[0]; - wire \check:alias = cyc[0]; - wire \check;alias = !cyc[0]; + wire \check_alias /*verilator public_flat_rd*/ = cyc[0]; + wire \check:alias /*verilator public_flat_rd*/ = cyc[0]; + wire \check;alias /*verilator public_flat_rd*/ = !cyc[0]; // These are *different entities*, bug83 wire [31:0] \a0.cyc = ~a0.cyc; @@ -110,11 +113,11 @@ extern "C" int mon_check(); endmodule module sub ( - input [31:0] cyc + input [31:0] cyc /*verilator public_flat_rd*/ ); - reg \b.c ; - reg subsig1; - reg subsig2; + reg \b.c /*verilator public_flat_rw*/; + reg subsig1 /*verilator public_flat_rd*/; + reg subsig2 /*verilator public_flat_rd*/; `ifdef IVERILOG // stop icarus optimizing signals away wire redundant = subsig1 | subsig2 | \b.c ; diff --git a/test_regress/t/t_vpi_escape.vlt b/test_regress/t/t_vpi_escape.vlt new file mode 100644 index 000000000..bced08f31 --- /dev/null +++ b/test_regress/t/t_vpi_escape.vlt @@ -0,0 +1,9 @@ +// 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 + +`verilator_config + +public_flat_rd -module "*" -var "double__underscore__vlt" From 9ad0de1efd0c76f42be3299971d7ec9c300807f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Thu, 10 Jul 2025 10:56:14 +0200 Subject: [PATCH 009/252] Fix uninitialized thread PGO counters (#6167) --- include/verilated_profiler.h | 2 +- test_regress/t/t_pgo_threads_hier_nested.py | 3 +++ test_regress/t/t_pgo_threads_hier_nested.vlt | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/verilated_profiler.h b/include/verilated_profiler.h index b46ea49b6..abc5ba600 100644 --- a/include/verilated_profiler.h +++ b/include/verilated_profiler.h @@ -218,7 +218,7 @@ class VlPgoProfiler final { }; // Counters are stored packed, all together to reduce cache effects - std::array m_counters; // Time spent on this record + std::array m_counters{}; // Time spent on this record std::vector m_records; // Record information public: diff --git a/test_regress/t/t_pgo_threads_hier_nested.py b/test_regress/t/t_pgo_threads_hier_nested.py index b253d41f2..55eaa6461 100755 --- a/test_regress/t/t_pgo_threads_hier_nested.py +++ b/test_regress/t/t_pgo_threads_hier_nested.py @@ -28,6 +28,9 @@ test.execute(all_run_flags=[ test.file_grep(test.obj_dir + "/profile.vlt", r'profile_data -model "VTest"') test.file_grep(test.obj_dir + "/profile.vlt", r'profile_data -model "V' + test.name + '"') +# Check for cost rollovers +test.file_grep_not(test.obj_dir + "/profile.vlt", r'.*cost 64\'d\d{18}.*') + # Differentiate benchmarksim results test.name = test.name + "_optimized" test.compile( diff --git a/test_regress/t/t_pgo_threads_hier_nested.vlt b/test_regress/t/t_pgo_threads_hier_nested.vlt index 9447e6fce..3dedcb17f 100644 --- a/test_regress/t/t_pgo_threads_hier_nested.vlt +++ b/test_regress/t/t_pgo_threads_hier_nested.vlt @@ -6,3 +6,5 @@ `verilator_config hier_workers -module "Test" -workers 2 +hier_block -module "Check" +hier_workers -module "Check" -workers 2 From 31c279a7b3e6d7556d014c04d8dc1f5a6d7e085b Mon Sep 17 00:00:00 2001 From: Igor Zaworski <84542280+igorosky@users.noreply.github.com> Date: Thu, 10 Jul 2025 10:59:05 +0200 Subject: [PATCH 010/252] Support randomize() on class member selects (#6161) --- src/V3LinkDot.cpp | 53 ++++++++++++---- test_regress/t/t_randomize_complex.py | 21 +++++++ test_regress/t/t_randomize_complex.v | 53 ++++++++++++++++ test_regress/t/t_randomize_complex_arrays.py | 21 +++++++ test_regress/t/t_randomize_complex_arrays.v | 55 +++++++++++++++++ .../t_randomize_complex_associative_arrays.py | 21 +++++++ .../t_randomize_complex_associative_arrays.v | 55 +++++++++++++++++ .../t/t_randomize_complex_dynamic_arrays.py | 21 +++++++ .../t/t_randomize_complex_dynamic_arrays.v | 56 +++++++++++++++++ .../t/t_randomize_complex_member_bad.out | 12 ++++ .../t/t_randomize_complex_member_bad.py | 19 ++++++ .../t/t_randomize_complex_member_bad.v | 40 ++++++++++++ test_regress/t/t_randomize_complex_queue.py | 21 +++++++ test_regress/t/t_randomize_complex_queue.v | 56 +++++++++++++++++ test_regress/t/t_randomize_complex_typedef.py | 21 +++++++ test_regress/t/t_randomize_complex_typedef.v | 61 +++++++++++++++++++ .../t/t_randomize_method_complex_bad.out | 9 +-- 17 files changed, 576 insertions(+), 19 deletions(-) create mode 100755 test_regress/t/t_randomize_complex.py create mode 100644 test_regress/t/t_randomize_complex.v create mode 100755 test_regress/t/t_randomize_complex_arrays.py create mode 100644 test_regress/t/t_randomize_complex_arrays.v create mode 100755 test_regress/t/t_randomize_complex_associative_arrays.py create mode 100644 test_regress/t/t_randomize_complex_associative_arrays.v create mode 100755 test_regress/t/t_randomize_complex_dynamic_arrays.py create mode 100644 test_regress/t/t_randomize_complex_dynamic_arrays.v create mode 100644 test_regress/t/t_randomize_complex_member_bad.out create mode 100755 test_regress/t/t_randomize_complex_member_bad.py create mode 100644 test_regress/t/t_randomize_complex_member_bad.v create mode 100755 test_regress/t/t_randomize_complex_queue.py create mode 100644 test_regress/t/t_randomize_complex_queue.v create mode 100755 test_regress/t/t_randomize_complex_typedef.py create mode 100644 test_regress/t/t_randomize_complex_typedef.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index b84b8ba6c..d67934d68 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2648,6 +2648,46 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.init(m_curSymp); iterateNull(nodep); } + static const AstNodeDType* getElemDTypep(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + while (true) { + if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else { + break; + } + } + return dtypep; + } + static const AstNodeDType* getExprDTypep(const AstNodeExpr* selp) { + while (const AstNodePreSel* const sp = VN_CAST(selp, NodePreSel)) selp = sp->fromp(); + if (const AstMemberSel* const sp = VN_CAST(selp, MemberSel)) { + if (const AstNodeDType* dtypep = getExprDTypep(sp->fromp())) { + if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) { + const AstClass* const classp = classRefp->classp(); + const bool found = classp->existsMember( + [&dtypep, name = selp->name()](const AstClass*, const AstVar* nodep) { + dtypep = nodep->childDTypep(); + return nodep->name() == name; + }); + if (found) return getElemDTypep(dtypep); + selp->v3error("Class " << classRefp->prettyNameQ() + << " does not contain field " << selp->prettyNameQ()); + } else { + selp->v3fatalSrc("Member selection on expression of type " + << dtypep->prettyDTypeNameQ() + << ", which is not a class type"); + } + } + } else if (const AstNodeVarRef* const varRefp = VN_CAST(selp, NodeVarRef)) { + return getElemDTypep(varRefp->varp()->childDTypep()); + } + return nullptr; + } #define LINKDOT_VISIT_START() \ VL_RESTORER(m_indent); \ @@ -3746,17 +3786,8 @@ class LinkDotResolveVisitor final : public VNVisitor { if (!fromDtp) { if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) { fromDtp = varRefp->varp()->subDTypep(); - } else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) { - if (const AstNodeVarRef* const varRefp - = VN_CAST(selp->fromp(), NodeVarRef)) { - fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep(); - } - } else if (const AstNodePreSel* const selp - = VN_CAST(nodep->fromp(), NodePreSel)) { - if (const AstNodeVarRef* const varRefp - = VN_CAST(selp->fromp(), NodeVarRef)) { - fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep(); - } + } else { + fromDtp = getExprDTypep(nodep->fromp()); } if (!fromDtp) { if (VN_IS(nodep->pinsp(), With)) { diff --git a/test_regress/t/t_randomize_complex.py b/test_regress/t/t_randomize_complex.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_complex.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex.v b/test_regress/t/t_randomize_complex.v new file mode 100644 index 000000000..37c04e5b7 --- /dev/null +++ b/test_regress/t/t_randomize_complex.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2; + function new (); + sc_inst2 = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst = new; + MyClass cl_inst2 = new; + repeat(10) begin + if (cl_inst.sc_inst.sc_inst1.sc_inst2.randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst.sc_inst.sc_inst1.sc_inst2.field < 1 || cl_inst.sc_inst.sc_inst1.sc_inst2.field > 3) begin + $stop; + end + if (cl_inst2.sc_inst2.randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2.sc_inst2.field < 1 || cl_inst2.sc_inst2.field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_arrays.py b/test_regress/t/t_randomize_complex_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_arrays.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex_arrays.v b/test_regress/t/t_randomize_complex_arrays.v new file mode 100644 index 000000000..f7963ba5a --- /dev/null +++ b/test_regress/t/t_randomize_complex_arrays.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[100]; + MyClass cl_inst2[2]; + cl_inst[1] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_associative_arrays.py b/test_regress/t/t_randomize_complex_associative_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_associative_arrays.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex_associative_arrays.v b/test_regress/t/t_randomize_complex_associative_arrays.v new file mode 100644 index 000000000..69d74ce80 --- /dev/null +++ b/test_regress/t/t_randomize_complex_associative_arrays.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[int]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[string]; + MyClass cl_inst2[int]; + cl_inst["val1"] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_dynamic_arrays.py b/test_regress/t/t_randomize_complex_dynamic_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_dynamic_arrays.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex_dynamic_arrays.v b/test_regress/t/t_randomize_complex_dynamic_arrays.v new file mode 100644 index 000000000..0b2ee25a3 --- /dev/null +++ b/test_regress/t/t_randomize_complex_dynamic_arrays.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[]; + function new (); + sc_inst2 = new [7]; + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[]; + MyClass cl_inst2[]; + cl_inst = new [3]; + cl_inst2 = new [5]; + cl_inst[1] = new; + cl_inst2[0] = new; + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_member_bad.out b/test_regress/t/t_randomize_complex_member_bad.out new file mode 100644 index 000000000..15f2d826f --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.out @@ -0,0 +1,12 @@ +%Error: t/t_randomize_complex_member_bad.v:36:28: Class 'Deep' does not contain field 'sc_inst2' + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_randomize_complex_member_bad.v:36:49: Unsupported: 'randomize() with' on complex expressions + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_randomize_complex_member_bad.v:36:67: Can't find definition of variable: 'field' + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_complex_member_bad.py b/test_regress/t/t_randomize_complex_member_bad.py new file mode 100755 index 000000000..830a90560 --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.py @@ -0,0 +1,19 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.lint(verilator_flags2=["--lint-only"], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_complex_member_bad.v b/test_regress/t/t_randomize_complex_member_bad.v new file mode 100644 index 000000000..c521a3638 --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[100]; + cl_inst[1] = new; + if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + end +endmodule diff --git a/test_regress/t/t_randomize_complex_queue.py b/test_regress/t/t_randomize_complex_queue.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_queue.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex_queue.v b/test_regress/t/t_randomize_complex_queue.v new file mode 100644 index 000000000..1d9e3eef6 --- /dev/null +++ b/test_regress/t/t_randomize_complex_queue.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[$]; + function new (); + SubClass inst = new; + sc_inst2 = { inst }; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper inst = new; + MyClass inst2 = new; + WeNeedToGoDeeper cl_inst[$] = { inst }; + MyClass cl_inst2[$] = { inst2 }; + repeat(10) begin + if (cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].field < 1 || cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[0].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[0].field < 1 || cl_inst2[0].sc_inst2[0].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_typedef.py b/test_regress/t/t_randomize_complex_typedef.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_complex_typedef.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_complex_typedef.v b/test_regress/t/t_randomize_complex_typedef.v new file mode 100644 index 000000000..4d8e94589 --- /dev/null +++ b/test_regress/t/t_randomize_complex_typedef.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +typedef SubClass Sc_t; +class MyClass; + Sc_t sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +typedef MyClass Mc_t; +class Deep; + Mc_t sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +typedef Deep D_t; +class WeNeedToGoDeeper; + D_t sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +typedef WeNeedToGoDeeper WNTGDA_t[100]; +typedef MyClass MCA_t[2]; + +module t; + initial begin + WNTGDA_t cl_inst; + MCA_t cl_inst2; + cl_inst[1] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_method_complex_bad.out b/test_regress/t/t_randomize_method_complex_bad.out index 38c236cd2..01851ca9c 100644 --- a/test_regress/t/t_randomize_method_complex_bad.out +++ b/test_regress/t/t_randomize_method_complex_bad.out @@ -1,14 +1,7 @@ -%Error-UNSUPPORTED: t/t_randomize_method_complex_bad.v:16:11: Unsupported: 'randomize() with' on complex expressions - 16 | x.f.randomize() with { r < 5; }, - | ^~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_randomize_method_complex_bad.v:16:30: Can't find definition of variable: 'r' - 16 | x.f.randomize() with { r < 5; }, - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_randomize_method_complex_bad.v:17:9: 'randomize() with' on a non-class-instance 'int' 17 | i.randomize() with { v < 5; }); | ^~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_randomize_method_complex_bad.v:17:28: Can't find definition of variable: 'v' 17 | i.randomize() with { v < 5; }); | ^ From ce77bac99a890ad8039c7e44437686dd5e662205 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 10 Jul 2025 18:46:45 +0100 Subject: [PATCH 011/252] Break some combinational cycles in DFG (#6168) Added an algorithm that can break some combinational cycles in DFG, by attempting to trace driver logic until we escape the cycle. This can eliminate a decent portion of UNOPTFLAT warnings. E.g. due to this code: ```systemverilog assign a[0] = .....; assign a[1] = ~a[0]; ``` --- docs/guide/exe_verilator.rst | 4 + src/CMakeLists.txt | 1 + src/Makefile_obj.in | 1 + src/V3Dfg.cpp | 125 ++++++ src/V3Dfg.h | 3 + src/V3DfgBreakCycles.cpp | 489 +++++++++++++++++++++ src/V3DfgOptimizer.cpp | 24 +- src/V3DfgPasses.cpp | 10 + src/V3DfgPasses.h | 25 ++ src/V3Options.cpp | 1 + src/V3Options.h | 2 + src/astgen | 24 + test_regress/t/t_dfg_break_cycles.cpp | 60 +++ test_regress/t/t_dfg_break_cycles.py | 107 +++++ test_regress/t/t_dfg_break_cycles.v | 46 ++ test_regress/t/t_dfg_true_cycle_bad.out | 9 + test_regress/t/t_dfg_true_cycle_bad.py | 16 + test_regress/t/t_dfg_true_cycle_bad.v | 16 + test_regress/t/t_unoptflat_simple_2_bad.py | 2 +- 19 files changed, 961 insertions(+), 4 deletions(-) create mode 100644 src/V3DfgBreakCycles.cpp create mode 100644 test_regress/t/t_dfg_break_cycles.cpp create mode 100755 test_regress/t/t_dfg_break_cycles.py create mode 100644 test_regress/t/t_dfg_break_cycles.v create mode 100644 test_regress/t/t_dfg_true_cycle_bad.out create mode 100755 test_regress/t/t_dfg_true_cycle_bad.py create mode 100644 test_regress/t/t_dfg_true_cycle_bad.v diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index a98de32eb..3ed785b6d 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -606,6 +606,10 @@ Summary: optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`, :vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`. +.. option:: -fno-dfg-break-cycles + + Rarely needed. Disable breaking combinational cycles during DFG. + .. option:: -fno-dfg-peephole Rarely needed. Disable the DFG peephole optimizer. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e53bb0e9..c224960dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -227,6 +227,7 @@ set(COMMON_SOURCES V3Descope.cpp V3Dfg.cpp V3DfgAstToDfg.cpp + V3DfgBreakCycles.cpp V3DfgCache.cpp V3DfgDecomposition.cpp V3DfgDfgToAst.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index d160a3ab8..ee5cb7617 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -240,6 +240,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Descope.o \ V3Dfg.o \ V3DfgAstToDfg.o \ + V3DfgBreakCycles.o \ V3DfgCache.o \ V3DfgDecomposition.o \ V3DfgDfgToAst.o \ diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 3d037be60..16ccb332a 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -34,6 +34,131 @@ DfgGraph::~DfgGraph() { forEachVertex([](DfgVertex& vtxp) { delete &vtxp; }); } +std::unique_ptr DfgGraph::clone() const { + const bool scoped = !modulep(); + + DfgGraph* const clonep = new DfgGraph{modulep(), name()}; + + // Map from original vertex to clone + std::unordered_map vtxp2clonep(size() * 2); + + // Clone constVertices + for (const DfgConst& vtx : m_constVertices) { + DfgConst* const cp = new DfgConst{*clonep, vtx.fileline(), vtx.num()}; + vtxp2clonep.emplace(&vtx, cp); + } + // Clone variable vertices + for (const DfgVertexVar& vtx : m_varVertices) { + const DfgVertexVar* const vp = vtx.as(); + DfgVertexVar* cp = nullptr; + + switch (vtx.type()) { + case VDfgType::atVarArray: { + if (scoped) { + cp = new DfgVarArray{*clonep, vp->varScopep()}; + } else { + cp = new DfgVarArray{*clonep, vp->varp()}; + } + vtxp2clonep.emplace(&vtx, cp); + break; + } + case VDfgType::atVarPacked: { + if (scoped) { + cp = new DfgVarPacked{*clonep, vp->varScopep()}; + } else { + cp = new DfgVarPacked{*clonep, vp->varp()}; + } + vtxp2clonep.emplace(&vtx, cp); + break; + } + default: { + vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName()); + VL_UNREACHABLE; + break; + } + } + + if (vp->hasDfgRefs()) cp->setHasDfgRefs(); + if (vp->hasModRefs()) cp->setHasModRefs(); + if (vp->hasExtRefs()) cp->setHasExtRefs(); + } + // Clone operation vertices + for (const DfgVertex& vtx : m_opVertices) { + switch (vtx.type()) { +#include "V3Dfg__gen_clone_cases.h" // From ./astgen + case VDfgType::atSel: { + DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtypep()}; + cp->lsb(vtx.as()->lsb()); + vtxp2clonep.emplace(&vtx, cp); + break; + } + case VDfgType::atMux: { + DfgMux* const cp = new DfgMux{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } + default: { + vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); + VL_UNREACHABLE; + break; + } + } + } + UASSERT(size() == clonep->size(), "Size of clone should be the same"); + + // Constants have no inputs + // Hook up inputs of cloned variables + for (const DfgVertexVar& vtx : m_varVertices) { + switch (vtx.type()) { + case VDfgType::atVarArray: { + const DfgVarArray* const vp = vtx.as(); + DfgVarArray* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverIndex(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + case VDfgType::atVarPacked: { + const DfgVarPacked* const vp = vtx.as(); + DfgVarPacked* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverLsb(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + default: { + vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName()); + VL_UNREACHABLE; + break; + } + } + } + // Hook up inputs of cloned operation vertices + for (const DfgVertex& vtx : m_opVertices) { + DfgVertex* const cp = vtxp2clonep.at(&vtx); + // The code below doesn't work for DfgVertexVariadic, but none of the opVertices are such. + UASSERT_OBJ(!vtx.is(), &vtx, "DfgVertexVariadic not handled"); + const auto oSourceEdges = vtx.sourceEdges(); + auto cSourceEdges = cp->sourceEdges(); + UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, "Mismatched source count"); + for (size_t i = 0; i < oSourceEdges.second; ++i) { + if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { + cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); + } + } + } + + return std::unique_ptr{clonep}; +} + void DfgGraph::addGraph(DfgGraph& other) { m_size += other.m_size; other.m_size = 0; diff --git a/src/V3Dfg.h b/src/V3Dfg.h index e4df1e06a..4101b3573 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -706,6 +706,9 @@ public: // 'const' variant of 'forEachVertex'. No mutation allowed. inline void forEachVertex(std::function f) const; + // Return an identical, independent copy of this graph. Vertex and edge order might differ. + std::unique_ptr clone() const VL_MT_DISABLED; + // Add contents of other graph to this graph. Leaves other graph empty. void addGraph(DfgGraph& other) VL_MT_DISABLED; diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp new file mode 100644 index 000000000..774db0f76 --- /dev/null +++ b/src/V3DfgBreakCycles.cpp @@ -0,0 +1,489 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Converting cyclic DFGs into acyclic DFGs +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3Dfg.h" +#include "V3DfgPasses.h" +#include "V3Hash.h" + +#include +#include +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +// Similar algorithm used in ExtractCyclicComponents. +// This one sets DfgVertex::user(). See the static 'apply' method below. +class ColorStronglyConnectedComponents final { + static constexpr uint32_t UNASSIGNED = std::numeric_limits::max(); + + // TYPES + struct VertexState final { + uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC) + uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs + VertexState() = default; + VertexState(uint32_t i, uint32_t n) + : component{n} + , index{i} {} + }; + + // STATE + DfgGraph& m_dfg; // The input graph + uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph + uint32_t m_index = 0; // Visitation index counter + std::vector m_stack; // The stack used by the algorithm + + // METHODS + void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) { + UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex");); + + // Visiting vertex + const size_t rootIndex = vtxState.index = ++m_index; + + // Visit children + vtx.forEachSink([&](DfgVertex& child) { + VertexState& childSatate = child.user(); + // If the child has not yet been visited, then continue traversal + if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); + // If the child is not in an SCC + if (childSatate.component == UNASSIGNED) { + if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; + } + }); + + if (vtxState.index == rootIndex) { + // This is the 'root' of an SCC + + // A trivial SCC contains only a single vertex + const bool isTrivial = m_stack.empty() // + || m_stack.back()->getUser().index < rootIndex; + // We also need a separate component for vertices that drive themselves (which can + // happen for input like 'assign a = a'), as we want to extract them (they are cyclic). + const bool drivesSelf = vtx.findSink([&vtx](const DfgVertex& sink) { // + return &vtx == &sink; + }); + + if (!isTrivial || drivesSelf) { + // Allocate new component + ++m_nonTrivialSCCs; + vtxState.component = m_nonTrivialSCCs; + while (!m_stack.empty()) { + VertexState& topState = m_stack.back()->getUser(); + // Only higher nodes belong to the same SCC + if (topState.index < rootIndex) break; + m_stack.pop_back(); + topState.component = m_nonTrivialSCCs; + } + } else { + // Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph. + vtxState.component = 0; + } + } else { + // Not the root of an SCC + m_stack.push_back(&vtx); + } + } + + void colorSCCs() { + // Implements Pearce's algorithm to color the strongly connected components. For reference + // see "An Improved Algorithm for Finding the Strongly Connected Components of a Directed + // Graph", David J.Pearce, 2005. + + // We know constant nodes have no input edges, so they cannot be part + // of a non-trivial SCC. Mark them as such without any real traversals. + for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0}); + + // Start traversals through variables + for (DfgVertexVar& vtx : m_dfg.varVertices()) { + VertexState& vtxState = vtx.user(); + // If it has no input or no outputs, it cannot be part of a non-trivial SCC. + if (vtx.arity() == 0 || !vtx.hasSinks()) { + UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, + &vtx, "Non circular variable must be in a trivial SCC");); + vtxState.index = 0; + vtxState.component = 0; + continue; + } + // If not yet visited, start a traversal + if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + } + + // Start traversals through operations + for (DfgVertex& vtx : m_dfg.opVertices()) { + VertexState& vtxState = vtx.user(); + // If not yet visited, start a traversal + if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + } + } + + ColorStronglyConnectedComponents(DfgGraph& dfg) + : m_dfg{dfg} { + UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name()); + // Yet another implementation of Pearce's algorithm. + colorSCCs(); + // Re-assign user values + m_dfg.forEachVertex([](DfgVertex& vtx) { + const size_t component = vtx.getUser().component; + vtx.setUser(component); + }); + } + +public: + // Sets DfgVertex::user() for all vertext to: + // - 0, if the vertex is not part of a non-trivial strongly connected component + // and is not part of a self-loop. That is: the Vertex is not part of any cycle. + // - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. + // That is: each set of vertices that are reachable from each other will have the same + // non-zero value assigned. + // Returns the number of non-trivial SCCs (distinct cycles) + static uint32_t apply(DfgGraph& dfg) { + return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs; + } +}; + +class TraceDriver final : public DfgVisitor { + // TYPES + + // Structure denoting currently visited vertex with the MSB and LSB we are searching for + struct Visited final { + DfgVertex* m_vtxp; + uint32_t m_lsb; + uint32_t m_msb; + + Visited() = delete; + Visited(DfgVertex* vtxp, uint32_t lsb, uint32_t msb) + : m_vtxp{vtxp} + , m_lsb{lsb} + , m_msb{msb} {} + + struct Hash final { + size_t operator()(const Visited& item) const { + V3Hash hash{reinterpret_cast(item.m_vtxp)}; + hash += item.m_lsb; + hash += item.m_msb; + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const Visited& a, const Visited& b) const { + return a.m_vtxp == b.m_vtxp && a.m_lsb == b.m_lsb && a.m_msb == b.m_lsb; + } + }; + }; + + // STATE + DfgGraph& m_dfg; // The graph being processed + // The strongly connected component we are trying to escape + const uint32_t m_component; + uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex + uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex + // Result of tracing the currently visited Vertex. Use SET_RESULT below! + DfgVertex* m_resp = nullptr; + std::vector m_newVtxps; // New vertices created during the traversal + std::ofstream m_lineCoverageFile; // Line coverage file, just for testing + + std::vector m_stack; // Stack of currently visited vertices + // Denotes if a 'Visited' entry appear in m_stack + std::unordered_map m_visited; + + // METHODS + + // Create and return a new Vertex and add it to m_newVtxps. You should + // always use this to create new vertices, so unused ones (if a trace + // eventually fails) can be cleaned up at the end. + template + Vertex* make(FileLine* flp, uint32_t width) { + static_assert(std::is_base_of::value // + && !std::is_base_of::value // + && !std::is_same::value, + "Should only make operation vertices"); + AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); + Vertex* const vtxp = new Vertex{m_dfg, flp, dtypep}; + m_newVtxps.emplace_back(vtxp); + return vtxp; + } + + // Continue tracing drivers of the given vertex, at the given LSB. Every + // visitor should call this to continue the traversal, then immediately + // return after the call. 'visit' methods should not call 'iterate', call + // this method instead, which checks for cycles. + DfgVertex* trace(DfgVertex* const vtxp, const uint32_t msb, const uint32_t lsb) { + UASSERT_OBJ(!vtxp->is(), vtxp, "Cannot trace array variables"); + UASSERT_OBJ(vtxp->width() > msb, vtxp, "Traced Vertex too narrow"); + + // Push to stack + m_stack.emplace_back(vtxp, msb, lsb); + bool& onStackr = m_visited[m_stack.back()]; + + // Check for true combinational cycles + if (onStackr) { + // Pop from stack + m_stack.pop_back(); + + // Note: could issue a "proper combinational cycle" error here, + // but constructing a legible error message is hard as the Vertex + // Filelines can be very rough after optimizations (could consider + // reporting only the variables involved). Also this pass might + // run mulitple times and report the same error again. There will + // be an UNOPTFLAT issued during scheduling anyway, and the true + // cycle might still settle at run-time. + + // Stop trace + return nullptr; + } + + // Trace the vertex + onStackr = true; + + if (vtxp->user() != m_component) { + // If the currently traced vertex is in a different component, + // then we found what we were looking for. + if (msb != vtxp->width() - 1 || lsb != 0) { + // Apply a Sel to extract the relevant bits if only a part is needed + DfgSel* const selp = make(vtxp->fileline(), msb - lsb + 1); + selp->fromp(vtxp); + selp->lsb(lsb); + m_resp = selp; + } else { + // Otherwise just return the vertex + m_resp = vtxp; + } + } else { + // Otherwise visit the vertex + VL_RESTORER(m_msb); + VL_RESTORER(m_lsb); + m_msb = msb; + m_lsb = lsb; + m_resp = nullptr; + iterate(vtxp); + } + UASSERT_OBJ(!m_resp || m_resp->width() == (msb - lsb + 1), vtxp, "Wrong result width"); + + // Pop from stack + onStackr = false; + m_stack.pop_back(); + + // Done + return m_resp; + } + + // Use this macro to set the result in 'visit' methods. This also emits + // a line to m_lineCoverageFile for testing. + // TODO: Use C++20 std::source_location instead of a macro +#define SET_RESULT(vtxp) \ + do { \ + m_resp = vtxp; \ + if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \ + } while (false) + + // VISITORS + void visit(DfgVertex* vtxp) override { + // Base case: cannot continue ... + UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName()); + } + + void visit(DfgVarPacked* vtxp) override { + // Proceed with the driver that wholly covers the searched bits + const auto pair = vtxp->sourceEdges(); + for (size_t i = 0; i < pair.second; ++i) { + DfgVertex* const srcp = pair.first[i].sourcep(); + const uint32_t lsb = vtxp->driverLsb(i); + const uint32_t msb = lsb + srcp->width() - 1; + // If it does not cover the searched bit range, move on + if (m_lsb < lsb || msb < m_msb) continue; + // Trace this driver + SET_RESULT(trace(srcp, m_msb - lsb, m_lsb - lsb)); + return; + } + } + + void visit(DfgConcat* vtxp) override { + DfgVertex* const rhsp = vtxp->rhsp(); + DfgVertex* const lhsp = vtxp->lhsp(); + const uint32_t rWidth = rhsp->width(); + // If the traced bits are wholly in the RHS + if (rWidth > m_msb) { + SET_RESULT(trace(rhsp, m_msb, m_lsb)); + return; + } + // If the traced bits are wholly in the LHS + if (m_lsb >= rWidth) { + SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth)); + return; + } + // The traced bit span both sides, attempt to trace both + if (DfgVertex* const rp = trace(rhsp, rWidth - 1, m_lsb)) { + if (DfgVertex* const lp = trace(lhsp, m_msb - rWidth, 0)) { + DfgConcat* const resp = make(vtxp->fileline(), m_msb - m_lsb + 1); + resp->rhsp(rp); + resp->lhsp(lp); + SET_RESULT(resp); + return; + } + } + } + + void visit(DfgExtend* vtxp) override { + DfgVertex* const srcp = vtxp->srcp(); + if (srcp->width() > m_msb) { + SET_RESULT(trace(srcp, m_msb, m_lsb)); + return; + } + } + + void visit(DfgSel* vtxp) override { + const uint32_t lsb = vtxp->lsb(); + SET_RESULT(trace(vtxp->srcp(), m_msb + lsb, m_lsb + lsb)); + return; + } + +#undef SET_RESULT + + // CONSTRUCTOR + TraceDriver(DfgGraph& dfg, uint32_t component) + : m_dfg{dfg} + , m_component{component} { + if (v3Global.opt.debugCheck()) { + m_lineCoverageFile.open( // + v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + + "__V3DfgBreakCycles-TraceDriver-line-coverage.txt", // + std::ios_base::out | std::ios_base::app); + } + } + +public: + // Given a Vertex that is part of an SCC denoted by vtxp->user(), + // return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not + // part of the same SCC. Returns nullptr if such a vertex cannot be + // computed. This can add new vertices to the graph. + static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width) { + TraceDriver traceDriver{dfg, vtxp->user()}; + // Find the out-of-component driver of the given vertex + DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb); + // Delete unused newly created vertices (these can be created if a + // partial trace succeded, but an eventual one falied). Because new + // vertices should be created depth first, it is enough to do a single + // reverse pass over the collectoin + for (DfgVertex* const vtxp : vlstd::reverse_view(traceDriver.m_newVtxps)) { + // Keep the actual result! + if (vtxp == resultp) continue; + // Keep used ones! + if (vtxp->hasSinks()) continue; + // Delete it + VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); + } + // Return the result + return resultp; + } +}; + +std::pair, bool> +V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { + // Shorthand for dumping graph at given dump level + const auto dump = [&](int level, const DfgGraph& dfg, const std::string& name) { + if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-" + name); + }; + + // Can't do much with trivial things ('a = a' or 'a[1] = a[0]'), so bail + if (dfg.size() <= 2) { + UINFO(7, "Graph is trivial"); + dump(9, dfg, "trivial"); + ++ctx.m_breakCyclesContext.m_nTrivial; + return {nullptr, false}; + } + + // Show input for debugging + dump(7, dfg, "input"); + + // We might fail to make any improvements, so first create a clone of the + // graph. This is what we will be working on, and return if successful. + // Do not touch the input graph. + std::unique_ptr resultp = dfg.clone(); + // Just shorthand for code below + DfgGraph& res = *resultp; + dump(9, res, "clone"); + + // How many improvements have we made + size_t nImprovements = 0; + size_t prevNImprovements; + + // Iterate while an improvement can be made and the graph is still cyclic + do { + // Color SCCs (populates DfgVertex::user()) + const auto userDataInUse = res.userDataInUse(); + const uint32_t numNonTrivialSCCs = ColorStronglyConnectedComponents::apply(res); + + // Congrats if it has become acyclic + if (!numNonTrivialSCCs) { + UINFO(7, "Graph became acyclic after " << nImprovements << " improvements"); + dump(7, res, "result-acyclic"); + ++ctx.m_breakCyclesContext.m_nFixed; + return {std::move(resultp), true}; + } + + // Attempt new improvements + UINFO(9, "New iteration after " << nImprovements << " improvements"); + prevNImprovements = nImprovements; + + // Method 1. Attempt to push Sel form Var through to the driving + // expression of the selected bits. This can fix things like + // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. + for (DfgVertexVar& vtx : res.varVertices()) { + // Only handle DfgVarPacked at this point + DfgVarPacked* const varp = vtx.cast(); + if (!varp) continue; + // If Variable is not part of a cycle, move on + const uint32_t component = varp->getUser(); + if (!component) continue; + + UINFO(9, "Attempting to TraceDriver " << varp->nodep()->name()); + + varp->forEachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of cycle + if (sink.getUser() != component) return; + // Only Handle Sels now + DfgSel* const selp = sink.cast(); + if (!selp) return; + // Try to find of the driver of the selected bits outside the cycle + DfgVertex* const fixp = TraceDriver::apply(res, varp, selp->lsb(), selp->width()); + if (!fixp) return; + // Found an out-of-cycle driver. We can replace this sel with that. + selp->replaceWith(fixp); + selp->unlinkDelete(res); + ++nImprovements; + ++ctx.m_breakCyclesContext.m_nImprovements; + dump(9, res, "TraceDriver"); + }); + } + } while (nImprovements != prevNImprovements); + + // If an improvement was made, return the still cyclic improved graph + if (nImprovements) { + UINFO(7, "Graph was improved " << nImprovements << " times"); + dump(7, res, "result-improved"); + ++ctx.m_breakCyclesContext.m_nImproved; + return {std::move(resultp), false}; + } + + // No improvement was made + UINFO(7, "Graph NOT improved"); + dump(7, res, "result-original"); + ++ctx.m_breakCyclesContext.m_nUnchanged; + return {nullptr, false}; +} diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 64c03fb80..571b413ac 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -240,16 +240,34 @@ static void process(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of // small cycles. - const std::vector>& cyclicComponents + std::vector> cyclicComponents = dfg.extractCyclicComponents("cyclic"); // Split the remaining acyclic DFG into [weakly] connected components - const std::vector>& acyclicComponents - = dfg.splitIntoComponents("acyclic"); + std::vector> acyclicComponents = dfg.splitIntoComponents("acyclic"); // Quick sanity check UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty"); + // Attempt to convert cyclic components into acyclic ones + if (v3Global.opt.fDfgBreakCyckes()) { + for (auto it = cyclicComponents.begin(); it != cyclicComponents.end();) { + auto result = V3DfgPasses::breakCycles(**it, ctx); + if (!result.first) { + // No improvement, moving on. + ++it; + } else if (!result.second) { + // Improved, but still cyclic. Replace the original cyclic component. + *it = std::move(result.first); + ++it; + } else { + // Result became acyclic. Move to acyclicComponents, delete original. + acyclicComponents.emplace_back(std::move(result.first)); + it = cyclicComponents.erase(it); + } + } + } + // For each acyclic component for (auto& component : acyclicComponents) { if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 63130699e..5d1e86795 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -30,6 +30,16 @@ V3DfgBinToOneHotContext::~V3DfgBinToOneHotContext() { m_decodersCreated); } +V3DfgBreakCyclesContext::~V3DfgBreakCyclesContext() { + V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, made acyclic", m_nFixed); + V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, improved", m_nImproved); + V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, left unchanged", + m_nUnchanged); + V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, trivial", m_nTrivial); + V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, changes applied", + m_nImprovements); +} + V3DfgCseContext::~V3DfgCseContext() { V3Stats::addStat("Optimizations, DFG " + m_label + " CSE, expressions eliminated", m_eliminated); diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 3893c5d67..99cd404dd 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -40,6 +40,20 @@ public: ~V3DfgBinToOneHotContext() VL_MT_DISABLED; }; +class V3DfgBreakCyclesContext final { + const std::string m_label; // Label to apply to stats + +public: + VDouble0 m_nFixed; // Number of graphs that became acyclic + VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic + VDouble0 m_nUnchanged; // Number of graphs that were left unchanged + VDouble0 m_nTrivial; // Number of graphs that were not changed + VDouble0 m_nImprovements; // Number of changes made to graphs + explicit V3DfgBreakCyclesContext(const std::string& label) + : m_label{label} {} + ~V3DfgBreakCyclesContext() VL_MT_DISABLED; +}; + class V3DfgCseContext final { const std::string m_label; // Label to apply to stats @@ -93,6 +107,7 @@ public: VDouble0 m_resultEquations; // Number of result combinational equations V3DfgBinToOneHotContext m_binToOneHotContext{m_label}; + V3DfgBreakCyclesContext m_breakCyclesContext{m_label}; V3DfgCseContext m_cseContext0{m_label + " 1st"}; V3DfgCseContext m_cseContext1{m_label + " 2nd"}; V3DfgPeepholeContext m_peepholeContext{m_label}; @@ -120,6 +135,16 @@ DfgGraph* astToDfg(AstModule&, V3DfgOptimizationContext&) VL_MT_DISABLED; // Same as above, but for the entire netlist, after V3Scope DfgGraph* astToDfg(AstNetlist&, V3DfgOptimizationContext&) VL_MT_DISABLED; +// Attempt to make the given cyclic graph into an acyclic, or "less cyclic" +// equivalent. If the returned pointer is null, then no improvement was +// possible on the input graph. Otherwise the returned graph is an improvement +// on the input graph, with at least some cycles eliminated. The returned +// graph is always independent of the original. If an imporoved graph is +// returned, then the returned 'bool' flag indicated if the returned graph is +// acyclic (flag 'true'), or still cyclic (flag 'false'). +std::pair, bool> breakCycles(const DfgGraph&, + V3DfgOptimizationContext&) VL_MT_DISABLED; + // Optimize the given DfgGraph void optimize(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED; diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 4c99cdbba..c4643c115 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1336,6 +1336,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_fDfgPostInline = flag; m_fDfgScoped = flag; }); + DECL_OPTION("-fdfg-break-cycles", FOnOff, &m_fDfgBreakCycles); DECL_OPTION("-fdfg-peephole", FOnOff, &m_fDfgPeephole); DECL_OPTION("-fdfg-peephole-", CbPartialMatch, [this](const char* optp) { // m_fDfgPeepholeDisabled.erase(optp); diff --git a/src/V3Options.h b/src/V3Options.h index 2d062fcb9..e77cc1cad 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -420,6 +420,7 @@ private: bool m_fConstBitOpTree; // main switch: -fno-const-bit-op-tree constant bit op tree bool m_fConstEager = true; // main switch: -fno-const-eagerly run V3Const during passes bool m_fDedupe; // main switch: -fno-dedupe: logic deduplication + bool m_fDfgBreakCycles = true; // main switch: -fno-dfg-break-cycles bool m_fDfgPeephole = true; // main switch: -fno-dfg-peephole bool m_fDfgPreInline; // main switch: -fno-dfg-pre-inline and -fno-dfg bool m_fDfgPostInline; // main switch: -fno-dfg-post-inline and -fno-dfg @@ -735,6 +736,7 @@ public: bool fConstBitOpTree() const { return m_fConstBitOpTree; } bool fConstEager() const { return m_fConstEager; } bool fDedupe() const { return m_fDedupe; } + bool fDfgBreakCyckes() const { return m_fDfgBreakCycles; } bool fDfgPeephole() const { return m_fDfgPeephole; } bool fDfgPreInline() const { return m_fDfgPreInline; } bool fDfgPostInline() const { return m_fDfgPostInline; } diff --git a/src/astgen b/src/astgen index 41f311f2d..03493426a 100755 --- a/src/astgen +++ b/src/astgen @@ -1219,6 +1219,29 @@ def write_dfg_auto_classes(filename): fh.write("\n") +def write_dfg_clone_cases(filename): + with open_file(filename) as fh: + + def emitBlock(pattern, **fmt): + fh.write(textwrap.dedent(pattern).format(**fmt)) + + for node in DfgVertexList: + # Only generate code for automatically derived leaf nodes + if (node.file is not None) or not node.isLeaf: + continue + + emitBlock('''\ + case VDfgType::at{t}: {{ + Dfg{t}* const cp = new Dfg{t}{{*clonep, vtx.fileline(), vtx.dtypep()}}; + vtxp2clonep.emplace(&vtx, cp); + break; + }} + ''', + t=node.name, + s=node.superClass.name) + fh.write("\n") + + def write_dfg_ast_to_dfg(filename): with open_file(filename) as fh: for node in DfgVertexList: @@ -1408,6 +1431,7 @@ if Args.classes: write_type_tests("Dfg", DfgVertexList) write_dfg_macros("V3Dfg__gen_macros.h") write_dfg_auto_classes("V3Dfg__gen_auto_classes.h") + write_dfg_clone_cases("V3Dfg__gen_clone_cases.h") write_dfg_ast_to_dfg("V3Dfg__gen_ast_to_dfg.h") write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h") diff --git a/test_regress/t/t_dfg_break_cycles.cpp b/test_regress/t/t_dfg_break_cycles.cpp new file mode 100644 index 000000000..0664397e1 --- /dev/null +++ b/test_regress/t/t_dfg_break_cycles.cpp @@ -0,0 +1,60 @@ +// +// DESCRIPTION: Verilator: DFG optimizer equivalence testing +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 +// + +#include +#include + +#include +#include +#include + +void rngUpdate(uint64_t& x) { + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; +} + +int main(int, char**) { + // Create contexts + VerilatedContext ctx; + + // Create models + Vref ref{&ctx}; + Vopt opt{&ctx}; + + uint64_t rand_a = 0x5aef0c8dd70a4497; + uint64_t rand_b = 0xf0c0a8dd75ae4497; + uint64_t srand_a = 0x00fa8dcc7ae4957; + uint64_t srand_b = 0x0fa8dc7ae3c9574; + + for (size_t n = 0; n < 200000; ++n) { + // Update rngs + rngUpdate(rand_a); + rngUpdate(rand_b); + rngUpdate(srand_a); + rngUpdate(srand_b); + + // Assign inputs + ref.rand_a = opt.rand_a = rand_a; + ref.rand_b = opt.rand_b = rand_b; + ref.srand_a = opt.srand_a = srand_a; + ref.srand_b = opt.srand_b = srand_b; + + // Evaluate both models + ref.eval(); + opt.eval(); + + // Check equivalence +#include "checks.h" + + // increment time + ctx.timeInc(1); + } + + std::cout << "*-* All Finished *-*\n"; +} diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py new file mode 100755 index 000000000..79ff66fae --- /dev/null +++ b/test_regress/t/t_dfg_break_cycles.py @@ -0,0 +1,107 @@ +#!/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_all') +test.sim_time = 2000000 + +root = ".." + +if not os.path.exists(root + "/.git"): + test.skip("Not in a git repository") + +# Read expected source lines hit +expectedLines = set() + +with open(root + "/src/V3DfgBreakCycles.cpp", 'r', encoding="utf8") as fd: + for lineno, line in enumerate(fd, 1): + line = line.split("//")[0] + if re.match(r'^[^#]*SET_RESULT', line): + expectedLines.add(lineno) + +if not expectedLines: + test.error("Failed to read expected source line numbers") + +# Generate the equivalence checks and declaration boilerplate +rdFile = test.top_filename +plistFile = test.obj_dir + "/portlist.vh" +pdeclFile = test.obj_dir + "/portdecl.vh" +checkFile = test.obj_dir + "/checks.h" +nExpectedCycles = 0 +with open(rdFile, 'r', encoding="utf8") as rdFh, \ + open(plistFile, 'w', encoding="utf8") as plistFh, \ + open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \ + open(checkFile, 'w', encoding="utf8") as checkFh: + for line in rdFh: + line = line.split("//")[0] + m = re.search(r'`signal\((\w+),', line) + if not m: + continue + nExpectedCycles += 1 + sig = m.group(1) + plistFh.write(sig + ",\n") + pdeclFh.write("output " + sig + ";\n") + checkFh.write("if (ref." + sig + " != opt." + sig + ") {\n") + checkFh.write(" std::cout << \"Mismatched " + sig + "\" << std::endl;\n") + checkFh.write(" std::cout << \"Ref: 0x\" << std::hex << (ref." + sig + + " + 0) << std::endl;\n") + checkFh.write(" std::cout << \"Opt: 0x\" << std::hex << (opt." + sig + + " + 0) << std::endl;\n") + checkFh.write(" std::exit(1);\n") + checkFh.write("}\n") + +# Compile un-optimized +test.compile(verilator_flags2=[ + "--stats", + "--build", + "-fno-dfg-break-cycles", + "+incdir+" + test.obj_dir, + "-Mdir", test.obj_dir + "/obj_ref", + "--prefix", "Vref", + "-Wno-UNOPTFLAT" +]) # yapf:disable + +# Check we got the expected number of circular logic warnings +test.file_grep(test.obj_dir + "/obj_ref/Vref__stats.txt", + r'Warnings, Suppressed UNOPTFLAT\s+(\d+)', nExpectedCycles) + +# Compile optimized - also builds executable +test.compile(verilator_flags2=[ + "--stats", + "--build", + "--exe", + "+incdir+" + test.obj_dir, + "-Mdir", test.obj_dir + "/obj_opt", + "--prefix", "Vopt", + "-Werror-UNOPTFLAT", + "--dumpi-V3DfgBreakCycles", "9", # To fill code coverage + "-CFLAGS \"-I .. -I ../obj_ref\"", + "../obj_ref/Vref__ALL.a", + "../../t/" + test.name + ".cpp" +]) # yapf:disable + +# Check all source lines hit +coveredLines = set() +with open(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-TraceDriver-line-coverage.txt", + 'r', + encoding="utf8") as fd: + for line in fd: + coveredLines.add(int(line.strip())) + +if coveredLines != expectedLines: + for n in sorted(expectedLines - coveredLines): + test.error_keep_going(f"V3DfgBreakCycles.cpp line {n} not covered") + for n in sorted(coveredLines - expectedLines): + test.error_keep_going(f"V3DfgBreakCycles.cpp line {n} covered but not expected") + +# Execute test to check equivalence +test.execute(executable=test.obj_dir + "/obj_opt/Vopt") + +test.passes() diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v new file mode 100644 index 000000000..e083cf7f8 --- /dev/null +++ b/test_regress/t/t_dfg_break_cycles.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +`define signal(name, width) wire [width-1:0] name; + +module t ( +`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py + rand_a, rand_b, srand_a, srand_b + ); + +`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py + + input rand_a; + input rand_b; + input srand_a; + input srand_b; + wire logic [63:0] rand_a; + wire logic [63:0] rand_b; + wire logic signed [63:0] srand_a; + wire logic signed [63:0] srand_b; + + `signal(CONCAT_RHS, 2); + assign CONCAT_RHS[0] = rand_a[0]; + assign CONCAT_RHS[1] = CONCAT_RHS[0]; + + `signal(CONCAT_LHS, 2); + assign CONCAT_LHS[0] = CONCAT_LHS[1]; + assign CONCAT_LHS[1] = rand_a[1]; + + `signal(CONCAT_MID, 3); + assign CONCAT_MID[0] = |CONCAT_MID[2:1]; + assign CONCAT_MID[2:1] = {rand_a[2], ~rand_a[2]}; + + `signal(SEL, 3); + assign SEL[0] = rand_a[4]; + assign SEL[1] = SEL[0]; + assign SEL[2] = SEL[1]; + + `signal(EXTEND_SRC, 5); + assign EXTEND_SRC[0] = rand_a[3]; + assign EXTEND_SRC[3:1] = 3'(EXTEND_SRC[0]); + assign EXTEND_SRC[4] = EXTEND_SRC[1]; +endmodule diff --git a/test_regress/t/t_dfg_true_cycle_bad.out b/test_regress/t/t_dfg_true_cycle_bad.out new file mode 100644 index 000000000..9ab0466c3 --- /dev/null +++ b/test_regress/t/t_dfg_true_cycle_bad.out @@ -0,0 +1,9 @@ +%Warning-UNOPTFLAT: t/t_dfg_true_cycle_bad.v:10:23: Signal unoptimizable: Circular combinational logic: 'o' + 10 | output wire [9:0] o + | ^ + ... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest + ... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message. + t/t_dfg_true_cycle_bad.v:10:23: Example path: o + t/t_dfg_true_cycle_bad.v:12:22: Example path: ASSIGNW + t/t_dfg_true_cycle_bad.v:10:23: Example path: o +%Error: Exiting due to diff --git a/test_regress/t/t_dfg_true_cycle_bad.py b/test_regress/t/t_dfg_true_cycle_bad.py new file mode 100755 index 000000000..1bf1426f9 --- /dev/null +++ b/test_regress/t/t_dfg_true_cycle_bad.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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_dfg_true_cycle_bad.v b/test_regress/t/t_dfg_true_cycle_bad.v new file mode 100644 index 000000000..b9f7e8b56 --- /dev/null +++ b/test_regress/t/t_dfg_true_cycle_bad.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +`default_nettype none + +module t( + output wire [9:0] o +); + assign o[1:0] = o[9:8]; + assign o[3:2] = {o[0], o[1]}; + assign o[7:4] = 4'(o[3:2]); + assign o[9:8] = o[5:4]; +endmodule diff --git a/test_regress/t/t_unoptflat_simple_2_bad.py b/test_regress/t/t_unoptflat_simple_2_bad.py index 5c6e4b955..a87556b80 100755 --- a/test_regress/t/t_unoptflat_simple_2_bad.py +++ b/test_regress/t/t_unoptflat_simple_2_bad.py @@ -14,7 +14,7 @@ test.top_filename = "t/t_unoptflat_simple_2.v" # Compile only test.compile(verilator_flags3=[], - verilator_flags2=["--report-unoptflat"], + verilator_flags2=["--report-unoptflat", "-fno-dfg-break-cycles"], fails=True, expect_filename=test.golden_filename) From 4dc6a31276ec0ef57c42948e88e18fcae70deb6b Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Fri, 11 Jul 2025 02:37:55 +0200 Subject: [PATCH 012/252] Fix omitting error when assigning to an input (#6169) --- docs/CONTRIBUTORS | 1 + src/V3Width.cpp | 10 +++---- test_regress/t/t_force_input_assign_bad.out | 14 +++++++++ test_regress/t/t_force_input_assign_bad.py | 18 ++++++++++++ test_regress/t/t_force_input_assign_bad.v | 32 +++++++++++++++++++++ 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 test_regress/t/t_force_input_assign_bad.out create mode 100755 test_regress/t/t_force_input_assign_bad.py create mode 100644 test_regress/t/t_force_input_assign_bad.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 8b3ccd0ed..f82a13199 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -24,6 +24,7 @@ Anthony Donlon Anthony Moore Arkadiusz Kozdra Arthur Rosa +Artur Bieniek Aylon Chaim Porat Bartłomiej Chmiel Brian Li diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8ca414718..8dc92b97d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2461,11 +2461,11 @@ class WidthVisitor final : public VNVisitor { // if (debug() >= 9) nodep->dumpTree("- VRout: "); if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) { nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ()); - } else if (!nodep->varp()->isForced() && nodep->access().isWriteOrRW() - && nodep->varp()->isInput() && !nodep->varp()->isFuncLocal() - && nodep->varp()->isReadOnly() && (!m_ftaskp || !m_ftaskp->isConstructor()) - && !VN_IS(m_procedurep, InitialAutomatic) - && !VN_IS(m_procedurep, InitialStatic)) { + } else if (nodep->access().isWriteOrRW() && nodep->varp()->isInput() + && !nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly() + && (!m_ftaskp || !m_ftaskp->isConstructor()) + && !VN_IS(m_procedurep, InitialAutomatic) && !VN_IS(m_procedurep, InitialStatic) + && !VN_IS(nodep->abovep(), AssignForce) && !VN_IS(nodep->abovep(), Release)) { nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " << nodep->prettyNameQ()); } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly && (!m_ftaskp || !m_ftaskp->isConstructor()) diff --git a/test_regress/t/t_force_input_assign_bad.out b/test_regress/t/t_force_input_assign_bad.out new file mode 100644 index 000000000..133179ba5 --- /dev/null +++ b/test_regress/t/t_force_input_assign_bad.out @@ -0,0 +1,14 @@ +%Error-ASSIGNIN: t/t_force_input_assign_bad.v:18:10: Assigning to input/const variable: 'i' + : ... note: In instance 't' + 18 | s1.i = 2; + | ^ + ... For error description see https://verilator.org/warn/ASSIGNIN?v=latest +%Error-ASSIGNIN: t/t_force_input_assign_bad.v:21:10: Assigning to input/const variable: 'i' + : ... note: In instance 't' + 21 | s2.i = 2; + | ^ +%Error-ASSIGNIN: t/t_force_input_assign_bad.v:25:17: Assigning to input/const variable: 'i' + : ... note: In instance 't' + 25 | assign s3.i = 2; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_force_input_assign_bad.py b/test_regress/t/t_force_input_assign_bad.py new file mode 100755 index 000000000..d8bbae00a --- /dev/null +++ b/test_regress/t/t_force_input_assign_bad.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('vlt') + +test.lint(expect_filename=test.golden_filename, + verilator_flags2=['--error-limit 1000'], + fails=True) + +test.passes() diff --git a/test_regress/t/t_force_input_assign_bad.v b/test_regress/t/t_force_input_assign_bad.v new file mode 100644 index 000000000..380dc433d --- /dev/null +++ b/test_regress/t/t_force_input_assign_bad.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module sub(input [1:0] i); +endmodule + +module t; + sub s1(1); + sub s2(1); + sub s3(1); + sub s4(1); + sub s5(1); + initial begin + // these should fail + s1.i = 2; + force s1.i = '1; + + s2.i = 2; + release s2.i; + + force s3.i = '1; + assign s3.i = 2; + + // these should not + force s4.i = '1; + + release s5.i; + end +endmodule From 4e8a8a039881155724b220e000330923fe032e27 Mon Sep 17 00:00:00 2001 From: Igor Zaworski <84542280+igorosky@users.noreply.github.com> Date: Fri, 11 Jul 2025 03:11:09 +0200 Subject: [PATCH 013/252] Fix param-dependent class typedef linking (#6171) --- src/V3LinkDot.cpp | 6 ++++++ test_regress/t/t_typedef_param_class.py | 18 ++++++++++++++++++ test_regress/t/t_typedef_param_class.v | 23 +++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100755 test_regress/t/t_typedef_param_class.py create mode 100644 test_regress/t/t_typedef_param_class.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index d67934d68..1b39283f8 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -4471,6 +4471,12 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp()); if (!VN_IS(nodep->classOrPackagep(), Class) && !VN_IS(nodep->classOrPackagep(), Package)) { + if (m_statep->forPrimary()) { + // It may be a type that comes from parameter class that is not + // instantioned yet + iterate(cpackagep); + return; + } // Likely impossible, as error thrown earlier cpackagerefp->v3error( // LCOV_EXCL_LINE "'::' expected to reference a class/package but referenced '" diff --git a/test_regress/t/t_typedef_param_class.py b/test_regress/t/t_typedef_param_class.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_typedef_param_class.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_typedef_param_class.v b/test_regress/t/t_typedef_param_class.v new file mode 100644 index 000000000..7d6fc6ac6 --- /dev/null +++ b/test_regress/t/t_typedef_param_class.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1 #(type T); + typedef T::Some_type2 Some_type1; +endclass + +class Class2; + typedef int Some_type2; +endclass + +module t; + initial begin + int value0 = 7; + Class1#(Class2)::Some_type1 value1 = value0; + int value2 = value1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 58b867c39cb663ab6a64b21b7f95f64348834c57 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Fri, 11 Jul 2025 03:12:44 +0200 Subject: [PATCH 014/252] Support multiple variables on RHS of a `force` assignment (#6163) --- src/V3Force.cpp | 32 ++++++++++++++------- test_regress/t/t_force_rhs_ref_multiple.out | 5 ---- test_regress/t/t_force_rhs_ref_multiple.py | 2 +- test_regress/t/t_force_rhs_ref_multiple.v | 4 +-- 4 files changed, 25 insertions(+), 18 deletions(-) delete mode 100644 test_regress/t/t_force_rhs_ref_multiple.out diff --git a/src/V3Force.cpp b/src/V3Force.cpp index ae144dc4f..e22804fae 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -150,9 +150,11 @@ private: // AstVarRef::user2 -> Flag indicating not to replace reference // AstVarScope::user3 -> AstVarScope*, a `valVscp` force component for each VarScope of // forced RHS + // AstVarScope::user4p -> AstNodeExpr*, the RHS expression const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; + const VNUser4InUse m_user4InUse; AstUser1Allocator m_forceComponentsVar; AstUser1Allocator m_forceComponentsVarScope; @@ -185,6 +187,12 @@ public: ForceComponentsVarScope* tryGetForceComponents(AstVarRef* nodep) const { return m_forceComponentsVarScope.tryGet(nodep->varScopep()); } + void setValVscpRhsExpr(AstVarScope* valVscp, AstNodeExpr* rhsExpr) { + valVscp->user4p(rhsExpr); + } + AstNodeExpr* getValVscpRhsExpr(AstVarScope* valVscp) const { + return VN_CAST(valVscp->user4p(), NodeExpr); + } }; class ForceConvertVisitor final : public VNVisitor { @@ -233,9 +241,8 @@ class ForceConvertVisitor final : public VNVisitor { = new AstAssign{flp, lhsp->cloneTreePure(false), rhsp->cloneTreePure(false)}; transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) { AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp; - // TODO support multiple VarRefs on RHS - if (AstVarRef* const refp = VN_CAST(rhsp, VarRef)) - ForceState::setValVscp(refp, valVscp); + m_state.setValVscpRhsExpr(valVscp, rhsp->cloneTreePure(false)); + rhsp->foreach([valVscp](AstVarRef* refp) { ForceState::setValVscp(refp, valVscp); }); return valVscp; }); @@ -267,11 +274,14 @@ class ForceConvertVisitor final : public VNVisitor { transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) { return m_state.getForceComponents(vscp).m_enVscp; }); - // IEEE 1800-2023 10.6.2: If this is a net, and not a variable, then reset the read - // signal directly as well, in case something in the same process reads it later. Also, if - // it is a variable, and not a net, set the original signal to the forced value, as it - // needs to retain the forced value until the next procedural update, which might happen on - // a later eval. Luckily we can do all this in a single assignment. + + // IEEE 1800-2023 10.6.2: When released, then if the variable is not driven by a continuous + // assignment and does not currently have an active procedural continuous assignment, the + // variable shall not immediately change value and shall maintain its current value until + // the next procedural assignment to the variable is executed. Releasing a variable that is + // driven by a continuous assignment or currently has an active assign procedural + // continuous assignment shall reestablish that assignment and schedule a reevaluation in + // the continuous assignment's scheduling region. AstAssign* const resetRdp = new AstAssign{flp, lhsp->cloneTreePure(false), lhsp->unlinkFrBack()}; // Replace write refs on the LHS @@ -376,10 +386,12 @@ class ForceReplaceVisitor final : public VNVisitor { if (AstVarScope* const valVscp = ForceState::getValVscp(nodep)) { FileLine* const flp = nodep->fileline(); AstVarRef* const valp = new AstVarRef{flp, valVscp, VAccess::WRITE}; - AstVarRef* const rhsp = new AstVarRef{flp, nodep->varScopep(), VAccess::READ}; + AstNodeExpr* rhsp = m_state.getValVscpRhsExpr(valVscp); + UASSERT_OBJ(rhsp, flp, "RHS of force/release must be an AstNodeExpr"); + rhsp = rhsp->cloneTreePure(false); ForceState::markNonReplaceable(valp); - ForceState::markNonReplaceable(rhsp); + rhsp->foreach([](AstVarRef* refp) { ForceState::markNonReplaceable(refp); }); m_stmtp->addNextHere(new AstAssign{flp, valp, rhsp}); } diff --git a/test_regress/t/t_force_rhs_ref_multiple.out b/test_regress/t/t_force_rhs_ref_multiple.out deleted file mode 100644 index b6245d940..000000000 --- a/test_regress/t/t_force_rhs_ref_multiple.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error: t/t_force_rhs_ref_multiple.v:23: got='h0a exp='h00000009 -%Error: t/t_force_rhs_ref_multiple.v:27: got='h0a exp='h00000007 -%Error: t/t_force_rhs_ref_multiple.v:31: got='h0a exp='h00000004 -%Error: t/t_force_rhs_ref_multiple.v:35: got='h0a exp='h00000000 -%Error: t/t_force_rhs_ref_multiple.v:39: got='h0a exp='h00000000 diff --git a/test_regress/t/t_force_rhs_ref_multiple.py b/test_regress/t/t_force_rhs_ref_multiple.py index 61632dd07..4ff4dfaa5 100755 --- a/test_regress/t/t_force_rhs_ref_multiple.py +++ b/test_regress/t/t_force_rhs_ref_multiple.py @@ -13,6 +13,6 @@ test.scenarios('vlt') test.compile(verilator_flags2=["--exe", "--main", "--timing"]) -test.execute(expect_filename=test.golden_filename) +test.execute() test.passes() diff --git a/test_regress/t/t_force_rhs_ref_multiple.v b/test_regress/t/t_force_rhs_ref_multiple.v index ce44a36c8..84900b7dd 100644 --- a/test_regress/t/t_force_rhs_ref_multiple.v +++ b/test_regress/t/t_force_rhs_ref_multiple.v @@ -4,7 +4,8 @@ // any use, without warranty, 2025 by Antmicro. // SPDX-License-Identifier: CC0-1.0 -`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(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; logic [7:0] a = 1; @@ -19,7 +20,6 @@ module t; #1; a = 0; #1; - // TODO support multiple VarRefs on RHS `checkh(e, 9); b = 0; From 0982260d3b2708b0a624932ae9ffe33d367fd599 Mon Sep 17 00:00:00 2001 From: Petr Nohavica Date: Fri, 11 Jul 2025 19:10:36 +0200 Subject: [PATCH 015/252] Fix constructor parameters in inheritance hierarchies (#6036) (#6070) --- src/V3Class.cpp | 34 +++----- src/V3EmitCFunc.h | 65 ++++++++++----- src/V3EmitCHeaders.cpp | 14 ++-- test_regress/t/t_class_hier_construction.py | 18 +++++ test_regress/t/t_class_hier_construction.v | 88 +++++++++++++++++++++ 5 files changed, 174 insertions(+), 45 deletions(-) create mode 100755 test_regress/t/t_class_hier_construction.py create mode 100644 test_regress/t/t_class_hier_construction.v diff --git a/src/V3Class.cpp b/src/V3Class.cpp index a3a1a3aaf..1878b2582 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -57,28 +57,20 @@ class ClassVisitor final : public VNVisitor { // METHODS - bool recurseImplements(AstClass* nodep, bool setit) { - // Returns true to set useVirtualPublic(). - // If there's an implements of an interface class then we have - // multiple classes that point to same object, that need same - // VlClass (the diamond problem). C++ will require we use 'virtual - // public' for VlClass. So, we need the interface class, and all - // classes above, and any below using any implements to use - // 'virtual public' via useVirtualPublic(). - if (nodep->useVirtualPublic()) return true; // Short-circuit - if (nodep->isInterfaceClass()) setit = true; - for (const AstClassExtends* extp = nodep->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { - if (recurseImplements(extp->classp(), setit)) setit = true; - } - if (setit) { + void recurseImplements(AstClass* nodep) { + // In SystemVerilog, we have two inheritance chains: + // - extends of concrete clasess: mapped to non-virtual C++ inheritance + // as there is only single ancestor allowed + // - implements of concrete classes / extends of interface classes: mapped + // to virtual inheritance to allow diamond patterns with multiple ancestors + if (nodep->useVirtualPublic()) return; // Short-circuit to exit diamond cycles + if (nodep->isInterfaceClass()) { nodep->useVirtualPublic(true); - for (const AstClassExtends* extp = nodep->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { - (void)recurseImplements(extp->classp(), true); - } } - return setit; + for (const AstClassExtends* extp = nodep->extendsp(); extp; + extp = VN_AS(extp->nextp(), ClassExtends)) { + recurseImplements(extp->classp()); + } } // VISITORS @@ -89,7 +81,7 @@ class ClassVisitor final : public VNVisitor { nodep->name(m_prefix + nodep->name()); nodep->unlinkFrBack(); v3Global.rootp()->addModulesp(nodep); - (void)recurseImplements(nodep, false); + recurseImplements(nodep); // Make containing package // Note origName is the same as the class origName so errors look correct AstClassPackage* const packagep diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 491e6c5a2..388c4e77c 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -261,33 +261,61 @@ public: if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp; return nullptr; } - - void putConstructorSubinit(const AstClass* classp, AstCFunc* cfuncp, bool top, - std::set& doneClassesr) { - for (const AstClassExtends* extp = classp->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { - if (extp->classp()->useVirtualPublic()) { - // It's a c++ virtual class (diamond relation) - // Must get the subclasses initialized first - putConstructorSubinit(extp->classp(), cfuncp, false, doneClassesr); + void putConstructorSubinit(const AstClass* classp, AstCFunc* cfuncp) { + // Virtual bases in depth-first left-to-right order + std::vector virtualBases; + std::unordered_set doneClasses; + collectVirtualBasesRecursep(classp, virtualBases); + for (AstClass* vbase : virtualBases) { + if (doneClasses.count(vbase)) continue; + puts(doneClasses.empty() ? "" : "\n , "); + doneClasses.emplace(vbase); + puts(prefixNameProtect(vbase)); + if (constructorNeedsProcess(vbase)) { + puts("(vlProcess, vlSymsp)"); + } else { + puts("(vlSymsp)"); } - // Diamond pattern with same base class twice? - if (doneClassesr.find(extp->classp()) != doneClassesr.end()) continue; - puts(doneClassesr.empty() ? "" : "\n , "); - doneClassesr.emplace(extp->classp()); + } + const AstCNew* const superNewCallp = + getSuperNewCallRecursep(cfuncp->stmtsp()); + // Direct non-virtual bases in declaration order + for (const AstClassExtends* extp = classp->extendsp(); extp; + extp = VN_AS(extp->nextp(), ClassExtends)) { + if (extp->classp()->useVirtualPublic()) continue; + if (doneClasses.count(extp->classp())) continue; + puts(doneClasses.empty() ? "" : "\n , "); + doneClasses.emplace(extp->classp()); puts(prefixNameProtect(extp->classp())); if (constructorNeedsProcess(extp->classp())) { puts("(vlProcess, vlSymsp"); } else { puts("(vlSymsp"); } - if (top) { - const AstCNew* const superNewCallp = getSuperNewCallRecursep(cfuncp->stmtsp()); - UASSERT_OBJ(superNewCallp, cfuncp, "super.new call not found"); + // Handle super.new() args for the concrete parent + if (!extp->classp()->isInterfaceClass() && superNewCallp) { putCommaIterateNext(superNewCallp->argsp(), true); } puts(")"); - top = false; + } + } + void collectVirtualBasesRecursep(const AstClass* classp, + std::vector& virtualBases) { + std::set visited; + collectVirtualBasesRecursep(classp, virtualBases /*ref*/, visited /*ref*/); + } + void collectVirtualBasesRecursep(const AstClass* classp, + std::vector& virtualBases, + std::set& visited) { + if (visited.count(classp)) return; + visited.emplace(classp); + for (const AstClassExtends* extp = classp->extendsp(); extp; + extp = VN_AS(extp->nextp(), ClassExtends)) { + // Depth-first: recurse into this base first + collectVirtualBasesRecursep(extp->classp(), virtualBases, visited); + if (extp->classp()->useVirtualPublic()) { + virtualBases.push_back(extp->classp()); + } } } @@ -314,8 +342,7 @@ public: const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class); if (nodep->isConstructor() && classp && classp->extendsp()) { puts("\n : "); - std::set doneClasses; - putConstructorSubinit(classp, nodep, true, doneClasses /*ref*/); + putConstructorSubinit(classp, nodep); } } puts(" {\n"); diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index f8ae0d50a..ddd73cf97 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -564,16 +564,20 @@ class EmitCHeader final : public EmitCConstInit { if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) "); puts(prefixNameProtect(modp)); if (const AstClass* const classp = VN_CAST(modp, Class)) { - const string virtpub = classp->useVirtualPublic() ? "virtual public " : "public "; - puts(" : " + virtpub); + puts(" : "); if (classp->extendsp()) { + bool needComma = false; for (const AstClassExtends* extp = classp->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { + extp = VN_AS(extp->nextp(), ClassExtends)) { + if (needComma) puts(", "); + // Use virtual only for interfaces for class inheritance + // (extends) + puts(extp->classp()->useVirtualPublic() ? "virtual public " : "public "); putns(extp, prefixNameProtect(extp->classp())); - if (extp->nextp()) puts(", " + virtpub); + needComma = true; } } else { - puts("VlClass"); + puts("public virtual VlClass"); } } else { puts(" final : public VerilatedModule"); diff --git a/test_regress/t/t_class_hier_construction.py b/test_regress/t/t_class_hier_construction.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_hier_construction.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_hier_construction.v b/test_regress/t/t_class_hier_construction.v new file mode 100644 index 000000000..a3f60a566 --- /dev/null +++ b/test_regress/t/t_class_hier_construction.v @@ -0,0 +1,88 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Petr Nohavica +// 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); +`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); + +interface class IBottomMid; + pure virtual function void moo(int i); +endclass + +interface class IBottom; + pure virtual function bit foo(); +endclass + +interface class IMid extends IBottomMid; + pure virtual function string bar(); +endclass + +class bottom_class implements IBottom; + string name; + + function new(string name); + this.name = name; + endfunction + + virtual function bit foo(); + $display("%s", name); + endfunction +endclass + +class middle_class extends bottom_class implements IMid, IBottom; + function new(string name); + super.new($sformatf("middle %0s", name)); + endfunction + + virtual function bit foo(); + $display("%s", name); + return 0; + endfunction + + virtual function void moo(int i); + $display("moo: %d", i); + endfunction + + virtual function string bar(); + return name; + endfunction +endclass + +class top_class extends middle_class; + int i; + function new(string name, int i); + super.new($sformatf("%0s %0d", name, i)); + this.i = i; + endfunction +endclass + +class sky_class extends top_class; + function new(string name); + super.new(name, 42); + endfunction +endclass + + +module t; + + initial begin + sky_class s = new("ahoj"); + bottom_class b = s; + top_class t = s; + IMid im; + + `checks( b.name, "middle ahoj 42" ); + `checks( s.name, "middle ahoj 42" ); + `checks( t.name, "middle ahoj 42" ); + `checkh( t.i, 42); + `checks(s.bar(), "middle ahoj 42"); + im = s; + im.moo(42); + + $finish; + end + +endmodule From 2fc12d951e508b0305d5e1d9438a142db8df5854 Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 11 Jul 2025 17:11:31 +0000 Subject: [PATCH 016/252] Apply 'make format' --- src/V3Class.cpp | 8 +++----- src/V3EmitCFunc.h | 18 +++++++----------- src/V3EmitCHeaders.cpp | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 1878b2582..d0d6de7c2 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -63,12 +63,10 @@ class ClassVisitor final : public VNVisitor { // as there is only single ancestor allowed // - implements of concrete classes / extends of interface classes: mapped // to virtual inheritance to allow diamond patterns with multiple ancestors - if (nodep->useVirtualPublic()) return; // Short-circuit to exit diamond cycles - if (nodep->isInterfaceClass()) { - nodep->useVirtualPublic(true); - } + if (nodep->useVirtualPublic()) return; // Short-circuit to exit diamond cycles + if (nodep->isInterfaceClass()) { nodep->useVirtualPublic(true); } for (const AstClassExtends* extp = nodep->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { + extp = VN_AS(extp->nextp(), ClassExtends)) { recurseImplements(extp->classp()); } } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 388c4e77c..b3720f3e7 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -277,11 +277,10 @@ public: puts("(vlSymsp)"); } } - const AstCNew* const superNewCallp = - getSuperNewCallRecursep(cfuncp->stmtsp()); + const AstCNew* const superNewCallp = getSuperNewCallRecursep(cfuncp->stmtsp()); // Direct non-virtual bases in declaration order for (const AstClassExtends* extp = classp->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { + extp = VN_AS(extp->nextp(), ClassExtends)) { if (extp->classp()->useVirtualPublic()) continue; if (doneClasses.count(extp->classp())) continue; puts(doneClasses.empty() ? "" : "\n , "); @@ -300,22 +299,19 @@ public: } } void collectVirtualBasesRecursep(const AstClass* classp, - std::vector& virtualBases) { + std::vector& virtualBases) { std::set visited; collectVirtualBasesRecursep(classp, virtualBases /*ref*/, visited /*ref*/); } - void collectVirtualBasesRecursep(const AstClass* classp, - std::vector& virtualBases, - std::set& visited) { + void collectVirtualBasesRecursep(const AstClass* classp, std::vector& virtualBases, + std::set& visited) { if (visited.count(classp)) return; visited.emplace(classp); for (const AstClassExtends* extp = classp->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { + extp = VN_AS(extp->nextp(), ClassExtends)) { // Depth-first: recurse into this base first collectVirtualBasesRecursep(extp->classp(), virtualBases, visited); - if (extp->classp()->useVirtualPublic()) { - virtualBases.push_back(extp->classp()); - } + if (extp->classp()->useVirtualPublic()) { virtualBases.push_back(extp->classp()); } } } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index ddd73cf97..fd23589fd 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -568,7 +568,7 @@ class EmitCHeader final : public EmitCConstInit { if (classp->extendsp()) { bool needComma = false; for (const AstClassExtends* extp = classp->extendsp(); extp; - extp = VN_AS(extp->nextp(), ClassExtends)) { + extp = VN_AS(extp->nextp(), ClassExtends)) { if (needComma) puts(", "); // Use virtual only for interfaces for class inheritance // (extends) From 77180c4020e44b7f2b3389979dc6ca77a8ab7768 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 11 Jul 2025 19:19:09 +0100 Subject: [PATCH 017/252] Optimize more cycles in DFG (#6173) Added a second algorithm to break cycles in DFG by identifying which bits of a circular variable are actually independent of the variable, then reuse the existing (but extended) driver tracing algorithm to eliminate them. This can fix up things like: `assign gray = binary ^ (gray >> 1)` --- src/V3DfgBreakCycles.cpp | 641 +++++++++++++++++++++++++-- test_regress/t/t_dfg_break_cycles.py | 21 +- test_regress/t/t_dfg_break_cycles.v | 118 +++-- 3 files changed, 709 insertions(+), 71 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 774db0f76..b85609e04 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -20,6 +20,7 @@ #include "V3DfgPasses.h" #include "V3Hash.h" +#include #include #include #include @@ -191,32 +192,56 @@ class TraceDriver final : public DfgVisitor { DfgGraph& m_dfg; // The graph being processed // The strongly connected component we are trying to escape const uint32_t m_component; + const bool m_aggressive; // Trace aggressively, creating intermediate ops uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex // Result of tracing the currently visited Vertex. Use SET_RESULT below! DfgVertex* m_resp = nullptr; std::vector m_newVtxps; // New vertices created during the traversal - std::ofstream m_lineCoverageFile; // Line coverage file, just for testing std::vector m_stack; // Stack of currently visited vertices // Denotes if a 'Visited' entry appear in m_stack std::unordered_map m_visited; + std::ofstream m_lineCoverageFile; // Line coverage file, just for testing + // METHODS - // Create and return a new Vertex and add it to m_newVtxps. You should + // Create and return a new Vertex and add it to m_newVtxps. Fileline is + // taken from 'refp', but 'refp' is otherwise not used. You should // always use this to create new vertices, so unused ones (if a trace // eventually fails) can be cleaned up at the end. template - Vertex* make(FileLine* flp, uint32_t width) { + Vertex* make(const DfgVertex* refp, uint32_t width) { static_assert(std::is_base_of::value // - && !std::is_base_of::value // - && !std::is_same::value, - "Should only make operation vertices"); - AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); - Vertex* const vtxp = new Vertex{m_dfg, flp, dtypep}; - m_newVtxps.emplace_back(vtxp); - return vtxp; + && !std::is_base_of::value, + "Should only make operation vertices and constants"); + + constexpr bool okWithoutAggressive = // + std::is_same::value // + || std::is_same::value // + || std::is_same::value // + || std::is_same::value; + + UASSERT_OBJ( + okWithoutAggressive || m_aggressive, refp, + "Should only create Const, Sel, Concat, Exend Vertices without aggressive mode"); + + if VL_CONSTEXPR_CXX17 (std::is_same::value) { + DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width}; + m_newVtxps.emplace_back(vtxp); + return reinterpret_cast(vtxp); + } else { + // TODO: this is a gross hack around lack of C++17 'if constexpr' Vtx is always Vertex + // when this code is actually executed, but needs a fudged type to type check when + // Vertex is DfgConst, in which case this code is unreachable ... + using Vtx = typename std::conditional::value, DfgSel, + Vertex>::type; + AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); + Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; + m_newVtxps.emplace_back(vtxp); + return reinterpret_cast(vtxp); + } } // Continue tracing drivers of the given vertex, at the given LSB. Every @@ -256,7 +281,7 @@ class TraceDriver final : public DfgVisitor { // then we found what we were looking for. if (msb != vtxp->width() - 1 || lsb != 0) { // Apply a Sel to extract the relevant bits if only a part is needed - DfgSel* const selp = make(vtxp->fileline(), msb - lsb + 1); + DfgSel* const selp = make(vtxp, msb - lsb + 1); selp->fromp(vtxp); selp->lsb(lsb); m_resp = selp; @@ -280,9 +305,49 @@ class TraceDriver final : public DfgVisitor { m_stack.pop_back(); // Done + if (!m_resp) { + UINFO(9, "TraceDriver - Failed to trace vertex of type: " << vtxp->typeName()); + } return m_resp; } + template + Vertex* bitwiseBinary(Vertex* vtxp) { + static_assert(std::is_base_of::value, + "Should only call on DfgVertexBinary"); + if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, m_lsb)) { + if (DfgVertex* const lp = trace(vtxp->lhsp(), m_msb, m_lsb)) { + Vertex* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->rhsp(rp); + resp->lhsp(lp); + return resp; + } + } + return nullptr; + } + + // Predicate to do determine if vtxp[$bits(vtxp)-1:idx] is known to be zero + // Somewhat rudimentary but sufficient for current purposes. + static bool knownToBeZeroAtAndAbove(const DfgVertex* vtxp, uint32_t idx) { + if (const DfgConcat* const catp = vtxp->cast()) { + DfgConst* const lConstp = catp->lhsp()->cast(); + return lConstp && idx >= catp->rhsp()->width() && lConstp->isZero(); + } + if (const DfgExtend* const extp = vtxp->cast()) { + return idx >= extp->srcp()->width(); + } + return false; + } + + // Like knownToBeZeroAtAndAbove, but checks vtxp[idx:0] + static bool knownToBeZeroAtAndBelow(const DfgVertex* vtxp, uint32_t idx) { + if (const DfgConcat* const catp = vtxp->cast()) { + DfgConst* const rConstp = catp->rhsp()->cast(); + return rConstp && idx < rConstp->width() && rConstp->isZero(); + } + return false; + } + // Use this macro to set the result in 'visit' methods. This also emits // a line to m_lineCoverageFile for testing. // TODO: Use C++20 std::source_location instead of a macro @@ -327,10 +392,10 @@ class TraceDriver final : public DfgVisitor { SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth)); return; } - // The traced bit span both sides, attempt to trace both + // The traced bit spans both sides, attempt to trace both if (DfgVertex* const rp = trace(rhsp, rWidth - 1, m_lsb)) { if (DfgVertex* const lp = trace(lhsp, m_msb - rWidth, 0)) { - DfgConcat* const resp = make(vtxp->fileline(), m_msb - m_lsb + 1); + DfgConcat* const resp = make(vtxp, m_msb - m_lsb + 1); resp->rhsp(rp); resp->lhsp(lp); SET_RESULT(resp); @@ -341,10 +406,24 @@ class TraceDriver final : public DfgVisitor { void visit(DfgExtend* vtxp) override { DfgVertex* const srcp = vtxp->srcp(); - if (srcp->width() > m_msb) { + const uint32_t sWidth = srcp->width(); + // If the traced bits are wholly in the input + if (sWidth > m_msb) { SET_RESULT(trace(srcp, m_msb, m_lsb)); return; } + // If the traced bits are wholly in the extension + if (m_lsb >= sWidth) { + SET_RESULT(make(vtxp, m_msb - m_lsb + 1)); + return; + } + // The traced bits span both sides + if (DfgVertex* const sp = trace(srcp, sWidth - 1, m_lsb)) { + DfgExtend* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->srcp(sp); + SET_RESULT(resp); + return; + } } void visit(DfgSel* vtxp) override { @@ -353,12 +432,107 @@ class TraceDriver final : public DfgVisitor { return; } + void visit(DfgNot* vtxp) override { + if (!m_aggressive) return; + if (DfgVertex* const sp = trace(vtxp->srcp(), m_msb, m_lsb)) { + DfgNot* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->srcp(sp); + SET_RESULT(resp); + return; + } + } + + void visit(DfgAnd* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(bitwiseBinary(vtxp)); + } + void visit(DfgOr* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(bitwiseBinary(vtxp)); + } + void visit(DfgXor* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(bitwiseBinary(vtxp)); + } + + void visit(DfgShiftR* vtxp) override { + DfgVertex* const lhsp = vtxp->lhsp(); + if (DfgConst* const rConstp = vtxp->rhsp()->cast()) { + const uint32_t shiftAmnt = rConstp->toU32(); + // Width of lower half of result + const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt; + + // If the traced bits are wholly in the input + if (lowerWidth > m_msb) { + SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt)); + return; + } + // If the traced bits are wholly in the extension + if (m_lsb >= lowerWidth) { + SET_RESULT(make(vtxp, m_msb - m_lsb + 1)); + return; + } + // The traced bits span both sides + if (DfgVertex* const sp = trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt)) { + DfgExtend* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->srcp(sp); + SET_RESULT(resp); + return; + } + return; + } + + // The shift amount is non-negative, so we can conclude zero if all + // bits at and above the LSB we seek are zeroes + if (knownToBeZeroAtAndAbove(lhsp, m_lsb)) { + SET_RESULT(make(vtxp, m_msb - m_lsb + 1)); + return; + } + } + + void visit(DfgShiftL* vtxp) override { + DfgVertex* const lhsp = vtxp->lhsp(); + if (DfgConst* const rConstp = vtxp->rhsp()->cast()) { + const uint32_t shiftAmnt = rConstp->toU32(); + // Width of lower half of result + const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? vtxp->width() : shiftAmnt; + + // If the traced bits are wholly in the input + if (m_lsb >= lowerWidth) { + SET_RESULT(trace(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt)); + return; + } + // If the traced bits are wholly in the extension + if (lowerWidth > m_msb) { + SET_RESULT(make(vtxp, m_msb - m_lsb + 1)); + return; + } + // The traced bits span both sides + if (DfgVertex* const sp = trace(lhsp, m_msb - shiftAmnt, lowerWidth - shiftAmnt)) { + DfgConcat* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->rhsp(make(vtxp, resp->width() - sp->width())); + resp->lhsp(sp); + SET_RESULT(resp); + return; + } + return; + } + + // The shift amount is non-negative, so we can conclude zero if all + // bits at and below the MSB we seek are zeroes + if (knownToBeZeroAtAndBelow(lhsp, m_msb)) { + SET_RESULT(make(vtxp, m_msb - m_lsb + 1)); + return; + } + } + #undef SET_RESULT // CONSTRUCTOR - TraceDriver(DfgGraph& dfg, uint32_t component) + TraceDriver(DfgGraph& dfg, uint32_t component, bool aggressive) : m_dfg{dfg} - , m_component{component} { + , m_component{component} + , m_aggressive{aggressive} { if (v3Global.opt.debugCheck()) { m_lineCoverageFile.open( // v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() @@ -371,9 +545,13 @@ public: // Given a Vertex that is part of an SCC denoted by vtxp->user(), // return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not // part of the same SCC. Returns nullptr if such a vertex cannot be - // computed. This can add new vertices to the graph. - static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width) { - TraceDriver traceDriver{dfg, vtxp->user()}; + // computed. This can add new vertices to the graph. The 'aggressive' flag + // enables creating many intermediate operations. This should only be set + // if it is reasonably certain the tracing will succeed, otherwise we can + // waste a lot of compute. + static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width, + bool aggressive) { + TraceDriver traceDriver{dfg, vtxp->user(), aggressive}; // Find the out-of-component driver of the given vertex DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb); // Delete unused newly created vertices (these can be created if a @@ -393,6 +571,385 @@ public: } }; +class IndependentBits final : public DfgVisitor { + // STATE + DfgVarPacked* const m_varp; // The variable we are computing dependencies for + std::deque m_workList; // Work list for traversal + // Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'. + std::unordered_map m_vtxp2Mask; + + std::ofstream m_lineCoverageFile; // Line coverage file, just for testing + + // METHODS + + // Retrieve the mask for the given vertex (create it with value 0 if needed) + V3Number& mask(const DfgVertex* vtxp) { + return m_vtxp2Mask + .emplace(std::piecewise_construct, // + std::forward_as_tuple(vtxp), // + std::forward_as_tuple(vtxp->fileline(), static_cast(vtxp->width()), 0)) + .first->second; + } + + // Use this macro to call 'mask' in 'visit' methods. This also emits + // a line to m_lineCoverageFile for testing. + // TODO: Use C++20 std::source_location instead of a macro +#define MASK(vtxp) \ + ([this](const DfgVertex* p) -> V3Number& { \ + if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \ + return mask(p); \ + }(vtxp)) + + // VISITORS + void visit(DfgVertex* vtxp) override { + UINFO(9, "Unhandled vertex type " << vtxp->typeName()); + // Conservatively assume it depends on the variable... + mask(vtxp).setAllBits1(); // intentionally not using MASK here + } + + void visit(DfgVarPacked* vtxp) override { + // The mask of the traced variable is known to be all ones + if (vtxp == m_varp) return; + + // Combine the masks of all drivers + V3Number& m = MASK(vtxp); + vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { + const DfgVertex* const srcp = edge.sourcep(); + m.opSelInto(MASK(srcp), vtxp->driverLsb(i), srcp->width()); + }); + } + + void visit(DfgConcat* vtxp) override { + const DfgVertex* const rhsp = vtxp->rhsp(); + const DfgVertex* const lhsp = vtxp->lhsp(); + V3Number& m = MASK(vtxp); + m.opSelInto(MASK(rhsp), 0, rhsp->width()); + m.opSelInto(MASK(lhsp), rhsp->width(), lhsp->width()); + } + + void visit(DfgReplicate* vtxp) override { + const uint32_t count = vtxp->countp()->as()->toU32(); + const DfgVertex* const srcp = vtxp->srcp(); + const uint32_t sWidth = srcp->width(); + V3Number& vMask = MASK(vtxp); + V3Number& sMask = MASK(srcp); + for (uint32_t i = 0; i < count; ++i) vMask.opSelInto(sMask, i * sWidth, sWidth); + } + + void visit(DfgSel* vtxp) override { + const uint32_t lsb = vtxp->lsb(); + const uint32_t msb = lsb + vtxp->width() - 1; + MASK(vtxp).opSel(MASK(vtxp->fromp()), msb, lsb); + } + + void visit(DfgExtend* vtxp) override { + const DfgVertex* const srcp = vtxp->srcp(); + MASK(vtxp).opSelInto(MASK(srcp), 0, srcp->width()); + } + + void visit(DfgNot* vtxp) override { // + MASK(vtxp) = MASK(vtxp->srcp()); + } + + void visit(DfgAnd* vtxp) override { // + MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); + } + void visit(DfgOr* vtxp) override { // + MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); + } + void visit(DfgXor* vtxp) override { // + MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); + } + + void visit(DfgShiftR* vtxp) override { + DfgVertex* const rhsp = vtxp->rhsp(); + DfgVertex* const lhsp = vtxp->lhsp(); + const uint32_t width = vtxp->width(); + + // Constant shift can be computed precisely + if (DfgConst* const rConstp = rhsp->cast()) { + const uint32_t shiftAmount = rConstp->toU32(); + if (shiftAmount >= width) return; + V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; + shiftedMask.opShiftR(MASK(lhsp), rConstp->num()); + MASK(vtxp).opSelInto(shiftedMask, 0, width - shiftAmount); + return; + } + + // Otherwise, as the shift amount is non-negative, any bit at or below + // the most significant dependent bit might be dependent + V3Number& lMask = MASK(lhsp); + V3Number& vMask = MASK(vtxp); + int idx = width - 1; + while (idx >= 0 && lMask.bitIs0(idx)) --idx; + while (idx >= 0) vMask.setBit(idx--, '1'); + } + + void visit(DfgShiftL* vtxp) override { + DfgVertex* const rhsp = vtxp->rhsp(); + DfgVertex* const lhsp = vtxp->lhsp(); + const uint32_t width = vtxp->width(); + + // Constant shift can be computed precisely + if (DfgConst* const rConstp = rhsp->cast()) { + const uint32_t shiftAmount = rConstp->toU32(); + if (shiftAmount >= width) return; + V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; + shiftedMask.opShiftL(MASK(lhsp), rConstp->num()); + MASK(vtxp).opSelInto(shiftedMask, shiftAmount, width - shiftAmount); + return; + } + + // Otherwise, as the shift amount is non-negative, any bit at or above + // the least significant dependent bit might be dependent + V3Number& lMask = MASK(lhsp); + V3Number& vMask = MASK(vtxp); + uint32_t idx = 0; + while (idx < width && lMask.bitIs0(idx)) ++idx; + while (idx < width) vMask.setBit(idx++, '1'); + } + +#undef MASK + + // CONSTRUCTOR + IndependentBits(DfgVarPacked* varp) + : m_varp{varp} { + if (v3Global.opt.debugCheck()) { + m_lineCoverageFile.open( // + v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + + "__V3DfgBreakCycles-IndependentBits-line-coverage.txt", // + std::ios_base::out | std::ios_base::app); + } + + // The starting vertex depends on it's own value, duuhh... + mask(varp).setAllBits1(); + // Enqueue all sinks + varp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + + // While there is an item on the worklist .. + while (!m_workList.empty()) { + // Grab next item + DfgVertex* const currp = m_workList.front(); + m_workList.pop_front(); + + // Grab current mask of item + const V3Number& maskCurr = mask(currp); + // Remember current mask + const V3Number prevMask = maskCurr; + + // Dispatch + iterate(currp); + + // If mask changed, enqueue sinks + if (!prevMask.isCaseEq(maskCurr)) { + currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + + // Check the mask only ever expands (no bit goes 1 -> 0) + if (VL_UNLIKELY(v3Global.opt.debugCheck())) { + V3Number notCurr{maskCurr}; + notCurr.opNot(maskCurr); + V3Number prevAndNotCurr{maskCurr}; + prevAndNotCurr.opAnd(prevMask, notCurr); + UASSERT_OBJ(prevAndNotCurr.isEqZero(), currp, "Mask should only expand"); + } + } + } + } + +public: + // Given a variable, compute which bits in this variable are independent of + // the variable itself (simple forward dataflow analysis). Returns a bit + // mask where a set bit indicates that bit is independent of the variable + // itself (logic is not circular). The result is a conservative estimate, + // so bits reported dependent might not actually be, but all bits reported + // independent are known to be so. + static V3Number apply(DfgVarPacked* varp) { + IndependentBits independentBits{varp}; + // Combine the masks of all drivers of the variable + V3Number result{varp->fileline(), static_cast(varp->width()), 0}; + varp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { + const DfgVertex* const srcp = edge.sourcep(); + // The mask represents the dependent bits, so invert it + V3Number inverseMask{srcp->fileline(), static_cast(srcp->width()), 0}; + inverseMask.opNot(independentBits.mask(srcp)); + result.opSelInto(inverseMask, varp->driverLsb(i), srcp->width()); + }); + return result; + } +}; + +class FixUpSelDrivers final { +public: + // Attempt to push Sel form Var through to the driving + // expression of the selected bits. This can fix things like + // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. + // Returns the number of drivers fixed up. + static size_t apply(DfgGraph& dfg, DfgVarPacked* varp) { + UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name()); + const uint32_t component = varp->getUser(); + size_t nImprovements = 0; + varp->forEachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of cycle + if (sink.getUser() != component) return; + // Only Handle Sels now + DfgSel* const selp = sink.cast(); + if (!selp) return; + // Try to find of the driver of the selected bits outside the cycle + DfgVertex* const fixp + = TraceDriver::apply(dfg, varp, selp->lsb(), selp->width(), false); + if (!fixp) return; + // Found an out-of-cycle driver. We can replace this sel with that. + selp->replaceWith(fixp); + selp->unlinkDelete(dfg); + ++nImprovements; + }); + UINFO(9, "FixUpSelDrivers made " << nImprovements << " improvements"); + return nImprovements; + } +}; + +class FixUpIndependentRanges final { + // Returns a bitmask set if that bit of 'varp' is used (has a sink) + static V3Number computeUsedBits(DfgVarPacked* varp) { + V3Number result{varp->fileline(), static_cast(varp->width()), 0}; + varp->forEachSink([&](DfgVertex& sink) { + // If used via a Sel, mark the selected bits used + if (DfgSel* const selp = sink.cast()) { + uint32_t lsb = selp->lsb(); + uint32_t msb = lsb + selp->width() - 1; + for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1'); + return; + } + + // Used without a Sel, so all bits are used + result.setAllBits1(); + }); + return result; + } + + // Trace drivers of independent bits of 'varp' in the range '[hi:lo]' + // append replacement terms to 'termps'. Returns number of successful + // replacements. + static size_t gatherTerms(std::vector& termps, DfgGraph& dfg, + DfgVarPacked* const varp, const V3Number& indpBits, + const uint32_t hi, const uint32_t lo) { + size_t nImprovements = 0; + // Iterate through consecutive dependent/non-dependet ranges within [hi:lo] + bool isIndependent = indpBits.bitIs1(lo); + uint32_t lsb = lo; + for (uint32_t msb = lo; msb <= hi; ++msb) { + const bool endRange = msb == hi || isIndependent != indpBits.bitIs1(msb + 1); + if (!endRange) continue; + const uint32_t width = msb - lsb + 1; + DfgVertex* termp = nullptr; + // If the range is not self-dependent, attempt to trace its driver + if (isIndependent) { + termp = TraceDriver::apply(dfg, varp, lsb, width, true); + if (termp) { + ++nImprovements; + } else { + UINFO(5, "TraceDriver of independent range failed for " + << varp->nodep()->name() << "[" << msb << ":" << lsb << "]"); + } + } + // Fall back on using the part of the variable (if dependent, or trace failed) + if (!termp) { + AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); + DfgSel* const selp = new DfgSel{dfg, varp->fileline(), dtypep}; + // Do not connect selp->fromp yet, need to do afer replacing 'varp' + selp->lsb(lsb); + termp = selp; + } + // Record the term + termps.emplace_back(termp); + // Next iteration + isIndependent = !isIndependent; + lsb = msb + 1; + } + return nImprovements; + } + +public: + // Similar to FixUpSelDrivers, but first comptute which bits of the + // variable are self dependent, and fix up those that are independent + // but used. + static size_t apply(DfgGraph& dfg, DfgVarPacked* varp) { + UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name()); + size_t nImprovements = 0; + + // Figure out which bits of 'varp' are used + const V3Number usedBits = computeUsedBits(varp); + UINFO(9, "Used bits of '" << varp->nodep()->name() << "' are " + << usedBits.displayed(varp->nodep(), "%b")); + // Nothing to do if no bits are used + if (usedBits.isEqZero()) return 0; + + // Figure out which bits of 'varp' are dependent on themselves + const V3Number indpBits = IndependentBits::apply(varp); + UINFO(9, "Independent bits of '" << varp->nodep()->name() << "' are " + << indpBits.displayed(varp->nodep(), "%b")); + // Can't do anything if all bits are dependent + if (indpBits.isEqZero()) return 0; + + { + // Nothing to do if no used bits are independen (all used bits are dependent) + V3Number usedAndIndependent{varp->fileline(), static_cast(varp->width()), 0}; + usedAndIndependent.opAnd(usedBits, indpBits); + if (usedAndIndependent.isEqZero()) return 0; + } + + // We are computing the terms to concatenate and replace 'varp' with + std::vector termps; + + // Iterate through consecutive used/unused ranges + FileLine* const flp = varp->fileline(); + const uint32_t width = varp->width(); + bool isUsed = usedBits.bitIs1(0); // Is current range used + uint32_t lsb = 0; // LSB of current range + for (uint32_t msb = 0; msb < width; ++msb) { + const bool endRange = msb == width - 1 || isUsed != usedBits.bitIs1(msb + 1); + if (!endRange) continue; + if (isUsed) { + // The range is used, compute the replacement terms + nImprovements += gatherTerms(termps, dfg, varp, indpBits, msb, lsb); + } else { + // The range is not used, just use constant 0 as a placeholder + termps.emplace_back(new DfgConst{dfg, flp, msb - lsb + 1}); + } + // Next iteration + isUsed = !isUsed; + lsb = msb + 1; + } + + // If we managed to imporove something, apply the replacement + if (nImprovements) { + // Concatenate all the terms to create the replacement + DfgVertex* replacementp = termps.front(); + for (size_t i = 1; i < termps.size(); ++i) { + DfgVertex* const termp = termps[i]; + const uint32_t catWidth = replacementp->width() + termp->width(); + AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(catWidth); + DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; + catp->rhsp(replacementp); + catp->lhsp(termp); + replacementp = catp; + } + + // Replace the variable + varp->replaceWith(replacementp); + // Connect the Sel nodes in the replacement + for (DfgVertex* const termp : termps) { + if (DfgSel* const selp = termp->cast()) { + if (!selp->fromp()) selp->fromp(varp); + } + } + } + + UINFO(9, "FixUpIndependentRanges made " << nImprovements << " improvements"); + return nImprovements; + } +}; + std::pair, bool> V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { // Shorthand for dumping graph at given dump level @@ -441,9 +998,7 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { UINFO(9, "New iteration after " << nImprovements << " improvements"); prevNImprovements = nImprovements; - // Method 1. Attempt to push Sel form Var through to the driving - // expression of the selected bits. This can fix things like - // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. + // Method 1: FixUpSelDrivers for (DfgVertexVar& vtx : res.varVertices()) { // Only handle DfgVarPacked at this point DfgVarPacked* const varp = vtx.cast(); @@ -452,24 +1007,32 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { const uint32_t component = varp->getUser(); if (!component) continue; - UINFO(9, "Attempting to TraceDriver " << varp->nodep()->name()); + const size_t nFixed = FixUpSelDrivers::apply(res, varp); + if (nFixed) { + nImprovements += nFixed; + ctx.m_breakCyclesContext.m_nImprovements += nFixed; + dump(9, res, "FixUpSelDrivers"); + } + } - varp->forEachSink([&](DfgVertex& sink) { - // Ignore if sink is not part of cycle - if (sink.getUser() != component) return; - // Only Handle Sels now - DfgSel* const selp = sink.cast(); - if (!selp) return; - // Try to find of the driver of the selected bits outside the cycle - DfgVertex* const fixp = TraceDriver::apply(res, varp, selp->lsb(), selp->width()); - if (!fixp) return; - // Found an out-of-cycle driver. We can replace this sel with that. - selp->replaceWith(fixp); - selp->unlinkDelete(res); - ++nImprovements; - ++ctx.m_breakCyclesContext.m_nImprovements; - dump(9, res, "TraceDriver"); - }); + // If we managed to fix something, try again with the earlier methods + if (nImprovements != prevNImprovements) continue; + + // Method 2. FixUpIndependentRanges + for (DfgVertexVar& vtx : res.varVertices()) { + // Only handle DfgVarPacked at this point + DfgVarPacked* const varp = vtx.cast(); + if (!varp) continue; + // If Variable is not part of a cycle, move on + const uint32_t component = varp->getUser(); + if (!component) continue; + + const size_t nFixed = FixUpIndependentRanges::apply(res, varp); + if (nFixed) { + nImprovements += nFixed; + ctx.m_breakCyclesContext.m_nImprovements += nFixed; + dump(9, res, "FixUpIndependentRanges"); + } } } while (nImprovements != prevNImprovements); diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index 79ff66fae..19b96d307 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -25,6 +25,8 @@ with open(root + "/src/V3DfgBreakCycles.cpp", 'r', encoding="utf8") as fd: line = line.split("//")[0] if re.match(r'^[^#]*SET_RESULT', line): expectedLines.add(lineno) + if re.match(r'^[^#]*MASK', line): + expectedLines.add(lineno) if not expectedLines: test.error("Failed to read expected source line numbers") @@ -62,6 +64,8 @@ test.compile(verilator_flags2=[ "--stats", "--build", "-fno-dfg-break-cycles", + "-fno-dfg-post-inline", + "-fno-dfg-scoped", "+incdir+" + test.obj_dir, "-Mdir", test.obj_dir + "/obj_ref", "--prefix", "Vref", @@ -76,6 +80,8 @@ test.file_grep(test.obj_dir + "/obj_ref/Vref__stats.txt", test.compile(verilator_flags2=[ "--stats", "--build", + "-fno-dfg-post-inline", + "-fno-dfg-scoped", "--exe", "+incdir+" + test.obj_dir, "-Mdir", test.obj_dir + "/obj_opt", @@ -89,11 +95,16 @@ test.compile(verilator_flags2=[ # Check all source lines hit coveredLines = set() -with open(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-TraceDriver-line-coverage.txt", - 'r', - encoding="utf8") as fd: - for line in fd: - coveredLines.add(int(line.strip())) + + +def readCovered(fileName): + with open(fileName, 'r', encoding="utf8") as fd: + for line in fd: + coveredLines.add(int(line.strip())) + + +readCovered(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-TraceDriver-line-coverage.txt") +readCovered(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-IndependentBits-line-coverage.txt") if coveredLines != expectedLines: for n in sorted(expectedLines - coveredLines): diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index e083cf7f8..fe46e2055 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -8,39 +8,103 @@ module t ( `include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py - rand_a, rand_b, srand_a, srand_b - ); + rand_a, rand_b, srand_a, srand_b +); `include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py - input rand_a; - input rand_b; - input srand_a; - input srand_b; - wire logic [63:0] rand_a; - wire logic [63:0] rand_b; - wire logic signed [63:0] srand_a; - wire logic signed [63:0] srand_b; + input rand_a; + input rand_b; + input srand_a; + input srand_b; + wire logic [63:0] rand_a; + wire logic [63:0] rand_b; + wire logic signed [63:0] srand_a; + wire logic signed [63:0] srand_b; - `signal(CONCAT_RHS, 2); - assign CONCAT_RHS[0] = rand_a[0]; - assign CONCAT_RHS[1] = CONCAT_RHS[0]; + ////////////////////////////////////////////////////////////////////////// + // Interesting user code to cover + ////////////////////////////////////////////////////////////////////////// - `signal(CONCAT_LHS, 2); - assign CONCAT_LHS[0] = CONCAT_LHS[1]; - assign CONCAT_LHS[1] = rand_a[1]; + `signal(GRAY_SEL, 3); + assign GRAY_SEL = rand_a[2:0] ^ 3'(GRAY_SEL[2:1]); - `signal(CONCAT_MID, 3); - assign CONCAT_MID[0] = |CONCAT_MID[2:1]; - assign CONCAT_MID[2:1] = {rand_a[2], ~rand_a[2]}; + `signal(GRAY_SHIFT, 3); + assign GRAY_SHIFT = rand_a[2:0] ^ (GRAY_SHIFT >> 1); - `signal(SEL, 3); - assign SEL[0] = rand_a[4]; - assign SEL[1] = SEL[0]; - assign SEL[2] = SEL[1]; + `signal(GRAY_REV_SEL, 3); + assign GRAY_REV_SEL = rand_a[2:0] ^ {GRAY_REV_SEL[1:0], 1'b0}; + + `signal(GRAY_REV_SHIFT, 3); + assign GRAY_REV_SHIFT = rand_a[2:0] ^ (GRAY_REV_SHIFT << 1); + + ////////////////////////////////////////////////////////////////////////// + // Fill coverage + ////////////////////////////////////////////////////////////////////////// + + `signal(CONCAT_RHS, 2); + assign CONCAT_RHS[0] = rand_a[0]; + assign CONCAT_RHS[1] = CONCAT_RHS[0]; + + `signal(CONCAT_LHS, 2); + assign CONCAT_LHS[0] = CONCAT_LHS[1]; + assign CONCAT_LHS[1] = rand_a[1]; + + `signal(CONCAT_MID, 3); + assign CONCAT_MID[0] = |CONCAT_MID[2:1]; + assign CONCAT_MID[2:1] = {rand_a[2], ~rand_a[2]}; + + `signal(SEL, 3); + assign SEL[0] = rand_a[4]; + assign SEL[1] = SEL[0]; + assign SEL[2] = SEL[1]; + + `signal(EXTEND, 8); + assign EXTEND[0] = rand_a[3]; + assign EXTEND[3:1] = 3'(EXTEND[0]); + assign EXTEND[4] = EXTEND[1]; + assign EXTEND[6:5] = EXTEND[2:1]; + assign EXTEND[7] = EXTEND[3]; + + `signal(NOT, 3); + assign NOT = ~(rand_a[2:0] ^ 3'(NOT[2:1])); + + `signal(AND, 3); + assign AND = rand_a[2:0] & 3'(AND[2:1]); + + `signal(OR, 3); + assign OR = rand_a[2:0] | 3'(OR[2:1]); + + `signal(SHIFTR, 14); + assign SHIFTR = { + SHIFTR[6:5], // 13:12 + SHIFTR[7:6], // 11:10 + SHIFTR[5:4], // 9:8 + SHIFTR[3:0] >> 2, // 7:4 + rand_a[3:0] // 3:0 + }; + + `signal(SHIFTR_VARIABLE, 2); + assign SHIFTR_VARIABLE = rand_a[1:0] ^ ({1'b0, SHIFTR_VARIABLE[1]} >> rand_b[0]); + + `signal(SHIFTL, 14); + assign SHIFTL = { + SHIFTL[6:5], // 13:12 + SHIFTL[7:6], // 11:10 + SHIFTL[5:4], // 9:8 + SHIFTL[3:0] << 2, // 7:4 + rand_a[3:0] // 3:0 + }; + + `signal(SHIFTL_VARIABLE, 2); + assign SHIFTL_VARIABLE = rand_a[1:0] ^ ({SHIFTL_VARIABLE[0], 1'b0} << rand_b[0]); + + `signal(VAR_A, 2); + wire logic [1:0] VAR_B; + assign VAR_A = {rand_a[0], VAR_B[0]}; + assign VAR_B = (VAR_A >> 1) ^ 2'(VAR_B[1]); + + `signal(REPLICATE, 4); + assign REPLICATE = rand_a[3:0] ^ ({2{REPLICATE[3:2]}} >> 2); - `signal(EXTEND_SRC, 5); - assign EXTEND_SRC[0] = rand_a[3]; - assign EXTEND_SRC[3:1] = 3'(EXTEND_SRC[0]); - assign EXTEND_SRC[4] = EXTEND_SRC[1]; endmodule From 1044398f95bb7eb0279efbe917b3278ede1abba4 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Sat, 12 Jul 2025 03:04:51 +0200 Subject: [PATCH 018/252] Support member-level triggers for virtual interfaces (#5166) (#6148) --- src/V3Sched.cpp | 12 +++ src/V3Sched.h | 46 +++++++- src/V3SchedVirtIface.cpp | 84 +++++++++++++-- .../t/t_interface_virtual_controlflow.out | 25 ++--- .../t/t_interface_virtual_controlflow.py | 2 +- .../t/t_interface_virtual_controlflow.v | 100 ++++++++++-------- .../t/t_interface_virtual_sched_act.out | 15 +-- .../t/t_interface_virtual_sched_act.py | 2 +- .../t/t_interface_virtual_sched_act.v | 27 +++-- .../t/t_interface_virtual_sched_ico.cpp | 51 --------- .../t/t_interface_virtual_sched_ico.out | 61 ++++------- .../t/t_interface_virtual_sched_ico.py | 2 +- .../t/t_interface_virtual_sched_ico.v | 43 +++++--- .../t/t_interface_virtual_sched_nba.out | 23 ++-- .../t/t_interface_virtual_sched_nba.py | 2 +- .../t/t_interface_virtual_sched_nba.v | 29 +++-- test_regress/t/t_interface_virtual_timing.out | 38 ++----- test_regress/t/t_interface_virtual_timing.v | 9 +- .../t/t_virtual_interface_member_trigger.py | 18 ++++ .../t/t_virtual_interface_member_trigger.v | 45 ++++++++ 20 files changed, 374 insertions(+), 260 deletions(-) delete mode 100644 test_regress/t/t_interface_virtual_sched_ico.cpp create mode 100755 test_regress/t/t_virtual_interface_member_trigger.py create mode 100755 test_regress/t/t_virtual_interface_member_trigger.v diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index cfb810d4b..633d8fbf3 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -1189,6 +1189,18 @@ VirtIfaceTriggers::makeIfaceToSensMap(AstNetlist* const netlistp, size_t vifTrig return ifaceToSensMap; } +VirtIfaceTriggers::IfaceMemberSensMap +VirtIfaceTriggers::makeMemberToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex, + AstVarScope* trigVscp) const { + IfaceMemberSensMap memberToSensMap; + for (const auto& p : m_memberTriggers) { + memberToSensMap.emplace( + std::make_pair(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex))); + ++vifTriggerIndex; + } + return memberToSensMap; +} + //============================================================================ // Top level entry-point to scheduling diff --git a/src/V3Sched.h b/src/V3Sched.h index 4c430919b..5d197e5ca 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -153,17 +153,55 @@ public: }; class VirtIfaceTriggers final { + // Represents a specific member in a virtual interface + struct IfaceMember final { + const AstIface* m_ifacep; // Interface type + const AstVar* m_memberVarp; // pointer to member field + + IfaceMember(const AstIface* ifacep, const AstVar* memberVarp) + : m_ifacep(ifacep) + , m_memberVarp(memberVarp) {} + + bool operator<(const IfaceMember& other) const { + if (m_ifacep != other.m_ifacep) return m_ifacep < other.m_ifacep; + return m_memberVarp < other.m_memberVarp; + } + }; + + using IfaceMemberTrigger = std::pair; + using IfaceMemberTriggerVec = std::vector; + using IfaceMemberSensMap = std::map; + using IfaceTrigger = std::pair; using IfaceTriggerVec = std::vector; using IfaceSensMap = std::map; - IfaceTriggerVec m_triggers; + + IfaceMemberTriggerVec m_memberTriggers; + IfaceTriggerVec m_ifaceTriggers; public: - void emplace_back(IfaceTrigger&& p) { m_triggers.emplace_back(std::move(p)); } - IfaceTriggerVec::const_iterator begin() const { return m_triggers.begin(); } - IfaceTriggerVec::const_iterator end() const { return m_triggers.end(); } + void addMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp, + AstVarScope* triggerVscp) { + m_memberTriggers.emplace_back(IfaceMember(ifacep, memberVarp), triggerVscp); + } + + AstVarScope* findMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp) const { + IfaceMember target{ifacep, memberVarp}; + for (const auto& pair : m_memberTriggers) { + if (!(pair.first < target) && !(target < pair.first)) return pair.second; + } + return nullptr; + } + + IfaceMemberSensMap makeMemberToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex, + AstVarScope* trigVscp) const; + + void emplace_back(IfaceTrigger&& p) { m_ifaceTriggers.emplace_back(std::move(p)); } + IfaceTriggerVec::const_iterator begin() const { return m_ifaceTriggers.begin(); } + IfaceTriggerVec::const_iterator end() const { return m_ifaceTriggers.end(); } IfaceSensMap makeIfaceToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex, AstVarScope* trigVscp) const; + VL_UNCOPYABLE(VirtIfaceTriggers); VirtIfaceTriggers() = default; VirtIfaceTriggers(VirtIfaceTriggers&&) = default; diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index 3a7ba6b4c..06818e5cd 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -49,12 +49,15 @@ private: // TYPES using OnWriteToVirtIface = std::function; + using OnWriteToVirtIfaceMember + = std::function; // STATE AstNetlist* const m_netlistp; // Root node AstAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment AstIface* m_trigAssignIfacep = nullptr; // Interface type whose trigger is assigned // by m_trigAssignp + AstVar* m_trigAssignMemberVarp; // Member pointer whose trigger is assigned V3UniqueNames m_vifTriggerNames{"__VvifTrigger"}; // Unique names for virt iface // triggers VirtIfaceTriggers m_triggers; // Interfaces and corresponding trigger vars @@ -73,6 +76,25 @@ private: } }); } + // For each write across a virtual interface boundary (member-level tracking) + static void foreachWrittenVirtIfaceMember( + AstNode* const nodep, const std::function& onWrite) { + nodep->foreach([&](AstVarRef* const refp) { + if (refp->access().isReadOnly()) return; + if (AstIfaceRefDType* const dtypep = VN_CAST(refp->varp()->dtypep(), IfaceRefDType)) { + if (dtypep->isVirtual()) { + if (AstMemberSel* const memberSelp = VN_CAST(refp->firstAbovep(), MemberSel)) { + // Extract the member varp from the MemberSel node + AstVar* memberVarp = memberSelp->varp(); + onWrite(refp, dtypep->ifacep(), memberVarp); + } + } + } else if (AstIface* const ifacep = refp->varp()->sensIfacep()) { + AstVar* memberVarp = refp->varp(); + onWrite(refp, ifacep, memberVarp); + } + }); + } // Returns true if there is a write across a virtual interface boundary static bool writesToVirtIface(const AstNode* const nodep) { return nodep->exists([](const AstVarRef* const refp) { @@ -103,12 +125,31 @@ private: return new AstVarRef{flp, VN_AS(ifacep->user1p(), VarScope), VAccess::WRITE}; } + // Create trigger reference for a specific interface member + AstVarRef* createVirtIfaceMemberTriggerRefp(FileLine* const flp, AstIface* ifacep, + const AstVar* memberVarp) { + // Check if we already have a trigger for this specific member + AstVarScope* existingTrigger = m_triggers.findMemberTrigger(ifacep, memberVarp); + if (!existingTrigger) { + AstScope* const scopeTopp = m_netlistp->topScopep()->scopep(); + // Create a unique name for this member trigger + const std::string triggerName + = m_vifTriggerNames.get(ifacep) + "_Vtrigm_" + memberVarp->name(); + AstVarScope* const vscp = scopeTopp->createTemp(triggerName, 1); + m_triggers.addMemberTrigger(ifacep, memberVarp, vscp); + existingTrigger = vscp; + } + return new AstVarRef{flp, existingTrigger, VAccess::WRITE}; + } + // VISITORS void visit(AstNodeProcedure* nodep) override { VL_RESTORER(m_trigAssignp); m_trigAssignp = nullptr; VL_RESTORER(m_trigAssignIfacep); m_trigAssignIfacep = nullptr; + VL_RESTORER(m_trigAssignMemberVarp); + m_trigAssignMemberVarp = nullptr; iterateChildren(nodep); } void visit(AstCFunc* nodep) override { @@ -116,6 +157,8 @@ private: m_trigAssignp = nullptr; VL_RESTORER(m_trigAssignIfacep); m_trigAssignIfacep = nullptr; + VL_RESTORER(m_trigAssignMemberVarp); + m_trigAssignMemberVarp = nullptr; iterateChildren(nodep); } void visit(AstAssignW* nodep) override { @@ -140,11 +183,13 @@ private: { VL_RESTORER(m_trigAssignp); VL_RESTORER(m_trigAssignIfacep); + VL_RESTORER(m_trigAssignMemberVarp); iterateAndNextNull(nodep->thensp()); } { VL_RESTORER(m_trigAssignp); VL_RESTORER(m_trigAssignIfacep); + VL_RESTORER(m_trigAssignMemberVarp); iterateAndNextNull(nodep->elsesp()); } if (v3Global.usesTiming()) { @@ -152,6 +197,7 @@ private: // branch m_trigAssignp = nullptr; m_trigAssignIfacep = nullptr; + m_trigAssignMemberVarp = nullptr; } } void visit(AstWhile* nodep) override { @@ -161,18 +207,21 @@ private: { VL_RESTORER(m_trigAssignp); VL_RESTORER(m_trigAssignIfacep); + VL_RESTORER(m_trigAssignMemberVarp); iterateAndNextNull(nodep->stmtsp()); } if (v3Global.usesTiming()) { // Clear the trigger assignment, as there could have been timing controls in the loop m_trigAssignp = nullptr; m_trigAssignIfacep = nullptr; + m_trigAssignMemberVarp = nullptr; } } void visit(AstJumpBlock* nodep) override { { VL_RESTORER(m_trigAssignp); VL_RESTORER(m_trigAssignIfacep); + VL_RESTORER(m_trigAssignMemberVarp); iterateChildren(nodep); } if (v3Global.usesTiming()) { @@ -180,29 +229,48 @@ private: // block m_trigAssignp = nullptr; m_trigAssignIfacep = nullptr; + m_trigAssignMemberVarp = nullptr; } } void visit(AstNodeStmt* nodep) override { if (v3Global.usesTiming() && nodep->exists([](AstNode* nodep) { return nodep->isTimingControl(); })) { - m_trigAssignp = nullptr; // Could be after a delay - need new trigger assignment + m_trigAssignp = nullptr; m_trigAssignIfacep = nullptr; - // No restorer, as following statements should not reuse the old assignment + m_trigAssignMemberVarp = nullptr; } FileLine* const flp = nodep->fileline(); - foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) { - if (ifacep != m_trigAssignIfacep) { - // Write to different interface type than before - need new trigger assignment - // No restorer, as following statements should not reuse the old assignment + + foreachWrittenVirtIfaceMember(nodep, [&](AstVarRef*, AstIface* ifacep, + AstVar* memberVarp) { + if (ifacep != m_trigAssignIfacep || memberVarp != m_trigAssignMemberVarp) { + // Write to different interface member than before - need new trigger assignment m_trigAssignIfacep = ifacep; + m_trigAssignMemberVarp = memberVarp; m_trigAssignp = nullptr; } if (!m_trigAssignp) { - m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep), - new AstConst{flp, AstConst::BitTrue{}}}; + m_trigAssignp + = new AstAssign{flp, createVirtIfaceMemberTriggerRefp(flp, ifacep, memberVarp), + new AstConst{flp, AstConst::BitTrue{}}}; nodep->addNextHere(m_trigAssignp); } }); + // Fallback to whole-interface tracking if no member-specific assignments found + if (!m_trigAssignp) { + foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) { + if (ifacep != m_trigAssignIfacep) { + m_trigAssignIfacep = ifacep; + m_trigAssignMemberVarp = nullptr; + m_trigAssignp = nullptr; + } + if (!m_trigAssignp) { + m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep), + new AstConst{flp, AstConst::BitTrue{}}}; + nodep->addNextHere(m_trigAssignp); + } + }); + } } void visit(AstNodeExpr*) override {} // Accelerate void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/test_regress/t/t_interface_virtual_controlflow.out b/test_regress/t/t_interface_virtual_controlflow.out index 303bceb13..2a88fdbd2 100644 --- a/test_regress/t/t_interface_virtual_controlflow.out +++ b/test_regress/t/t_interface_virtual_controlflow.out @@ -1,19 +1,12 @@ [0] vif1.data==0000 [0] intf2.data==0000 -[0] vif4.data==0000 -[10] intf2.data==beef -[20] vif1.data==dead -[20] vif4.data==face -[30] intf2.data==beef -[40] vif1.data==dead -[40] vif4.data==face -[50] intf2.data==beef -[60] vif1.data==dead -[60] vif4.data==face -[70] intf2.data==beef -[80] intf2.data==beef -[80] vif4.data==cafe -[90] intf2.data==beef -[100] intf2.data==beef -[100] vif4.data==deaf +[0] vif3.data==0000 +[0] intf4.data==0000 +[5000] intf2.data==beef +[15000] vif1.data==dead +[15000] vif3.data==fafa +[15000] intf4.data==face +[75000] intf4.data==cafe +[95000] vif3.data==cafe +[95000] intf4.data==deaf *-* All Finished *-* diff --git a/test_regress/t/t_interface_virtual_controlflow.py b/test_regress/t/t_interface_virtual_controlflow.py index 7aedd7b28..f163beb3f 100755 --- a/test_regress/t/t_interface_virtual_controlflow.py +++ b/test_regress/t/t_interface_virtual_controlflow.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-fno-reorder"]) +test.compile(verilator_flags2=["--exe --main --timing -fno-reorder"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_controlflow.v b/test_regress/t/t_interface_virtual_controlflow.v index 4e5dd4cbd..f8d03dc8e 100644 --- a/test_regress/t/t_interface_virtual_controlflow.v +++ b/test_regress/t/t_interface_virtual_controlflow.v @@ -5,63 +5,69 @@ // SPDX-License-Identifier: CC0-1.0 interface Bus1; - logic [15:0] data; + logic [15:0] data; endinterface interface Bus2; - logic [15:0] data; + logic [15:0] data; endinterface interface Bus3; - logic [15:0] data; + logic [15:0] data; endinterface -module t ( - clk -); - input clk; - integer cyc = 0; - Bus1 intf1(); - Bus2 intf2(); - Bus3 intf3(), intf4(); - virtual Bus1 vif1 = intf1; - virtual Bus2 vif2 = intf2; - virtual Bus3 vif3 = intf3, vif4 = intf4; +module t_controlflow; + logic clk = 0; + integer cyc = 0; + Bus1 intf1(); + Bus2 intf2(); + Bus3 intf3(), intf4(); + virtual Bus1 vif1 = intf1; + virtual Bus2 vif2 = intf2; + virtual Bus3 vif3 = intf3, vif4 = intf4; - // Finish on negedge so that $finish is last - always @(negedge clk) - if (cyc >= 10) begin - $write("*-* All Finished *-*\n"); - $finish; + // Finish on negedge so that $finish is last + always @(negedge clk) begin + if (cyc >= 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end end - function void assign_to_intf3(); - if ($c("1")) return; - intf3.data = 'hcafe; - endfunction + function void assign_to_intf3(); + intf3.data = 'hcafe; + endfunction - always @(posedge clk) begin - logic foo = 1; - cyc <= cyc + 1; - if (cyc == 1 || cyc == 3 || cyc == 5) intf1.data = 'hdead; - else vif2.data = 'hbeef; - if (cyc == 1 || cyc == 3 || cyc == 5) begin - if (cyc >= 3) $c("// dummy statement"); - else intf3.data = 'hfafa; - intf4.data = 'hface; + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1 || cyc == 3 || cyc == 5) intf1.data = 'hdead; + else vif2.data = 'hbeef; + if (cyc == 1 || cyc == 3 || cyc == 5) begin + if (cyc < 3) intf3.data = 'hfafa; + intf4.data = 'hface; + end + if (cyc == 7) begin + intf4.data = 'hcafe; + end + if (cyc == 9) begin + assign_to_intf3; + intf4.data = 'hdeaf; + end + end + + always @(vif1.data) begin + $write("[%0t] vif1.data==%h\n", $time, vif1.data); + end + always @(intf2.data) begin + $write("[%0t] intf2.data==%h\n", $time, intf2.data); + end + always @(vif3.data) begin + $write("[%0t] vif3.data==%h\n", $time, vif3.data); + end + always @(intf4.data) begin + $write("[%0t] intf4.data==%h\n", $time, intf4.data); + end + + initial begin + repeat (20) #5ns clk = ~clk; end - if (cyc == 7) begin - while ($c("0")) begin - foo = 0; - intf3.data = 'hbebe; - end - intf4.data = 'hcafe; - end - if (cyc == 9) begin - assign_to_intf3; - intf4.data = 'hdeaf; - end - end - always_comb $write("[%0t] vif1.data==%h\n", $time, vif1.data); - always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data); - always_comb $write("[%0t] vif4.data==%h\n", $time, vif4.data); endmodule diff --git a/test_regress/t/t_interface_virtual_sched_act.out b/test_regress/t/t_interface_virtual_sched_act.out index 10090eb57..7c8ef1b3f 100644 --- a/test_regress/t/t_interface_virtual_sched_act.out +++ b/test_regress/t/t_interface_virtual_sched_act.out @@ -1,12 +1,5 @@ [0] data==0000 -[0] data==0000 -[10] data==0000 -[20] data==dead -[20] data==beef -[20] data==beef -[30] data==beef -[40] data==face -[40] data==cafe -[40] data==cafe -[50] data==cafe -*-* All Finished *-* +[20000] data==dead +[30000] data==beef +[40000] data==face +[50000] data==cafe diff --git a/test_regress/t/t_interface_virtual_sched_act.py b/test_regress/t/t_interface_virtual_sched_act.py index ab5dca066..1407fff26 100755 --- a/test_regress/t/t_interface_virtual_sched_act.py +++ b/test_regress/t/t_interface_virtual_sched_act.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_act.v b/test_regress/t/t_interface_virtual_sched_act.v index 258ac1620..7f37b8a37 100644 --- a/test_regress/t/t_interface_virtual_sched_act.v +++ b/test_regress/t/t_interface_virtual_sched_act.v @@ -8,10 +8,8 @@ interface Bus; logic [15:0] data; endinterface -module t ( - clk -); - input clk; +module t_sched_act; + logic clk = 0; integer cyc = 0; Bus intf(); virtual Bus vif = intf; @@ -23,10 +21,10 @@ module t ( // Finish on negedge so that $finish is last always @(negedge clk) - if (cyc >= 5) begin - $write("*-* All Finished *-*\n"); - $finish; - end + if (cyc >= 6) begin + $write("*-* All Finished *-*\n"); + $finish; + end always @(posedge clk or data) begin if (cyc == 1) intf.data <= 'hdead; @@ -35,6 +33,15 @@ module t ( else if (cyc == 4) intf.data <= 'hcafe; end - assign data = vif.data; - always_comb $write("[%0t] data==%h\n", $time, data); + always @(negedge clk) begin + data <= vif.data; + end + + always @(data) begin + $write("[%0t] data==%h\n", $time, data); + end + + initial begin + repeat (10) #5ns clk = ~clk; + end endmodule diff --git a/test_regress/t/t_interface_virtual_sched_ico.cpp b/test_regress/t/t_interface_virtual_sched_ico.cpp deleted file mode 100644 index 04d8c1cc3..000000000 --- a/test_regress/t/t_interface_virtual_sched_ico.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// -// Copyright 2023 by Geza Lore. 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 -// -//************************************************************************* - -#include "verilated.h" - -#include "Vt_interface_virtual_sched_ico.h" -#include "Vt_interface_virtual_sched_ico__Syms.h" - -#include - -int main(int argc, char** argv) { - const std::unique_ptr contextp{new VerilatedContext}; - contextp->debug(0); - contextp->commandArgs(argc, argv); - srand48(5); - - const std::unique_ptr topp{new VM_PREFIX}; - topp->clk = false; - topp->inc1 = 1; - topp->eval(); - topp->inc2 = 1; - topp->eval(); - - bool flop = true; - while (!contextp->gotFinish() && contextp->time() < 100000) { - contextp->timeInc(5); - if (topp->clk) { - if (flop) { - topp->inc1 += 1; - } else { - topp->inc2 += 1; - } - flop = !flop; - } - topp->clk = !topp->clk; - topp->eval(); - } - - if (!contextp->gotFinish()) { - vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); - } - return 0; -} diff --git a/test_regress/t/t_interface_virtual_sched_ico.out b/test_regress/t/t_interface_virtual_sched_ico.out index 5f94c05df..e40299462 100644 --- a/test_regress/t/t_interface_virtual_sched_ico.out +++ b/test_regress/t/t_interface_virtual_sched_ico.out @@ -1,43 +1,20 @@ -[0] intf1.inc==0 -[0] vif2.inc==0 -[0] intf1.inc==1 -[0] vif2.inc==0 -[0] intf1.inc==1 -[0] vif2.inc==0 -[0] intf1.inc==1 -[0] vif2.inc==1 -[5] intf1.inc==1 -[5] vif2.inc==1 -[10] intf1.inc==2 -[10] vif2.inc==1 -[15] intf1.inc==2 -[15] vif2.inc==1 -[20] intf1.inc==2 -[20] vif2.inc==2 -[25] intf1.inc==2 -[25] vif2.inc==2 -[30] intf1.inc==3 -[30] vif2.inc==2 -[35] intf1.inc==3 -[35] vif2.inc==2 -[40] intf1.inc==3 -[40] vif2.inc==3 -[45] intf1.inc==3 -[45] vif2.inc==3 -[50] intf1.inc==4 -[50] vif2.inc==3 -[55] intf1.inc==4 -[55] vif2.inc==3 -[60] intf1.inc==4 -[60] vif2.inc==4 -[65] intf1.inc==4 -[65] vif2.inc==4 -[70] intf1.inc==5 -[70] vif2.inc==4 -[75] intf1.inc==5 -[75] vif2.inc==4 -[80] intf1.inc==5 -[80] vif2.inc==5 -[85] intf1.inc==5 -[85] vif2.inc==5 +[0] intf1.inc==00000000 +[0] vif2.inc==00000001 +[5000] intf1.inc==00000001 +[10000] vif2.inc==00000002 +[15000] intf1.inc==00000002 +[20000] vif2.inc==00000003 +[25000] intf1.inc==00000003 +[30000] vif2.inc==00000004 +[35000] intf1.inc==00000004 +[40000] vif2.inc==00000005 +[45000] intf1.inc==00000005 +[50000] vif2.inc==00000006 +[55000] intf1.inc==00000006 +[60000] vif2.inc==00000007 +[65000] intf1.inc==00000007 +[70000] vif2.inc==00000008 +[75000] intf1.inc==00000008 +[80000] vif2.inc==00000009 +[85000] intf1.inc==00000009 *-* All Finished *-* diff --git a/test_regress/t/t_interface_virtual_sched_ico.py b/test_regress/t/t_interface_virtual_sched_ico.py index 5f13c2aee..980753f6d 100755 --- a/test_regress/t/t_interface_virtual_sched_ico.py +++ b/test_regress/t/t_interface_virtual_sched_ico.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile(make_main=False, v_flags2=["--exe", test.pli_filename]) +test.compile(verilator_flags2=["--exe --main --timing"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_ico.v b/test_regress/t/t_interface_virtual_sched_ico.v index 3d85a7ec9..3e1b8e280 100644 --- a/test_regress/t/t_interface_virtual_sched_ico.v +++ b/test_regress/t/t_interface_virtual_sched_ico.v @@ -8,15 +8,11 @@ interface If; logic [31:0] inc; endinterface -module top ( - clk, - inc1, - inc2 -); +module top; - input clk; - input [31:0] inc1; - input [31:0] inc2; + logic clk = 0; + logic [31:0] inc1 = 0; + logic [31:0] inc2 = 0; int cyc = 0; If intf1(); @@ -24,18 +20,41 @@ module top ( virtual If vif1 = intf1; virtual If vif2 = intf2; - assign vif1.inc = inc1; + // assign vif1.inc = inc1; + always @(posedge clk) begin + vif1.inc <= inc1; + end assign intf2.inc = inc2; always @(posedge clk) begin cyc <= cyc + 1; - if (cyc >= 8) begin + if (cyc >= 10) begin $write("*-* All Finished *-*\n"); $finish; end end - always_comb $write("[%0t] intf1.inc==%0h\n", $time, intf1.inc); - always_comb $write("[%0t] vif2.inc==%0h\n", $time, vif2.inc); + always @(intf1.inc) begin + $write("[%0t] intf1.inc==%h\n", $time, intf1.inc); + end + always @(vif2.inc) begin + $write("[%0t] vif2.inc==%h\n", $time, vif2.inc); + end + + initial begin + repeat (30) #5ns clk = ~clk; + end + + initial begin + inc1 = 1; + inc2 = 1; + + repeat (8) begin + #10ns; + inc1 = inc1 + 1; + inc2 = inc2 + 1; + end + + end endmodule diff --git a/test_regress/t/t_interface_virtual_sched_nba.out b/test_regress/t/t_interface_virtual_sched_nba.out index abde98039..714cdcee8 100644 --- a/test_regress/t/t_interface_virtual_sched_nba.out +++ b/test_regress/t/t_interface_virtual_sched_nba.out @@ -1,21 +1,10 @@ [0] intf1.data==0000 [0] intf2.data==0000 [0] vif3.data==0000 -[0] intf2.data==0000 -[10] intf2.data==0000 -[10] vif3.data==0000 -[20] intf1.data==dead -[20] intf2.data==0000 -[20] vif3.data==0000 -[30] intf2.data==dead -[30] vif3.data==0000 -[40] intf1.data==beef -[40] intf2.data==dead -[40] vif3.data==0000 -[50] intf2.data==beef -[50] vif3.data==0000 -[60] intf2.data==beef -[60] vif3.data==face -[70] intf2.data==beef -[70] vif3.data==cafe +[15000] intf1.data==dead +[30000] intf2.data==dead +[35000] intf1.data==beef +[50000] intf2.data==beef +[55000] vif3.data==face +[65000] vif3.data==cafe *-* All Finished *-* diff --git a/test_regress/t/t_interface_virtual_sched_nba.py b/test_regress/t/t_interface_virtual_sched_nba.py index ab5dca066..bded720e2 100755 --- a/test_regress/t/t_interface_virtual_sched_nba.py +++ b/test_regress/t/t_interface_virtual_sched_nba.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["--exe --main --timing"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_nba.v b/test_regress/t/t_interface_virtual_sched_nba.v index dc610aa50..3a22fd5c9 100644 --- a/test_regress/t/t_interface_virtual_sched_nba.v +++ b/test_regress/t/t_interface_virtual_sched_nba.v @@ -16,10 +16,9 @@ interface Bus3; logic [15:0] data; endinterface -module t ( - clk -); - input clk; +module t; + + logic clk = 0; integer cyc = 0; Bus1 intf1(); Bus2 intf2(); @@ -29,7 +28,10 @@ module t ( virtual Bus3 vif3 = intf3; logic [15:0] data; - assign vif2.data = data; + // assign vif2.data = data; + always @(negedge clk) begin + vif2.data <= data; + end always @(posedge clk) begin cyc <= cyc + 1; @@ -54,7 +56,18 @@ module t ( $finish; end - always_comb $write("[%0t] intf1.data==%h\n", $time, intf1.data); - always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data); - always_comb $write("[%0t] vif3.data==%h\n", $time, vif3.data); + always @(intf1.data) begin + $write("[%0t] intf1.data==%h\n", $time, intf1.data); + end + always @(intf2.data) begin + $write("[%0t] intf2.data==%h\n", $time, intf2.data); + end + always @(vif3.data) begin + $write("[%0t] vif3.data==%h\n", $time, vif3.data); + end + + initial begin + repeat (20) #5ns clk = ~clk; + end + endmodule diff --git a/test_regress/t/t_interface_virtual_timing.out b/test_regress/t/t_interface_virtual_timing.out index 2ca5ac2af..2c7435aaa 100644 --- a/test_regress/t/t_interface_virtual_timing.out +++ b/test_regress/t/t_interface_virtual_timing.out @@ -1,29 +1,11 @@ -vif1.data==dead -intf2.data==0000 -vif1.data==dead -intf2.data==0000 -intf2.data==beef -vif1.data==dead -intf2.data==beef -intf2.data==beef -vif1.data==cafe -intf2.data==beef -intf2.data==face -vif1.data==cafe -intf2.data==face -intf2.data==face -vif1.data==feed -intf2.data==face -intf2.data==deed -vif1.data==feed -intf2.data==deed -intf2.data==deed -vif1.data==deaf -intf2.data==deed -intf2.data==fafa -vif1.data==deaf -intf2.data==fafa -intf2.data==fafa -vif1.data==bebe -intf2.data==fafa +[0] vif1.data==dead +[0] intf2.data==0000 +[1] intf2.data==beef +[2] vif1.data==cafe +[3] intf2.data==face +[4] vif1.data==feed +[5] intf2.data==deed +[6] vif1.data==deaf +[7] intf2.data==fafa +[8] vif1.data==bebe *-* All Finished *-* diff --git a/test_regress/t/t_interface_virtual_timing.v b/test_regress/t/t_interface_virtual_timing.v index 258c7d8f6..6d6d285d7 100644 --- a/test_regress/t/t_interface_virtual_timing.v +++ b/test_regress/t/t_interface_virtual_timing.v @@ -37,6 +37,11 @@ module t; $finish; end - always_comb if ($time < 9) $write("vif1.data==%h\n", vif1.data); - always_comb if ($time < 9) $write("intf2.data==%h\n", intf2.data); + always @(vif1.data) begin + if ($time < 9) $write("[%0t] vif1.data==%h\n", $time, vif1.data); + end + always @(intf2.data) begin + if ($time < 9) $write("[%0t] intf2.data==%h\n", $time, intf2.data); + end + endmodule diff --git a/test_regress/t/t_virtual_interface_member_trigger.py b/test_regress/t/t_virtual_interface_member_trigger.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger.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_virtual_interface_member_trigger.v b/test_regress/t/t_virtual_interface_member_trigger.v new file mode 100755 index 000000000..a6bf92d7f --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps + +interface INTF; + logic x; + logic y; + logic z; +endinterface + +class Dummy; + virtual INTF vif; + function new(virtual INTF vif); + this.vif = vif; + endfunction +endclass + +module t_virtual_interface_member_trigger(); + logic s1, src_val; + logic s2; + + INTF vintf(); + + assign vintf.x = s1; + assign vintf.y = src_val; + assign vintf.z = !vintf.y; + assign s2 = vintf.z; + assign s1 = s2; + + Dummy d; + + initial begin + d = new(vintf); + #1ns; + src_val = 0; + #1ns; + if (!(d.vif.x == 1 && d.vif.y == 0 && d.vif.z == 1 && s1 == 1 && s2 == 1)) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From cefe1845df4cc1404da5af72405e269ca6224674 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 11 Jul 2025 21:53:29 -0400 Subject: [PATCH 019/252] Commentary: Changes update --- Changes | 13 +++++++++++-- docs/spelling.txt | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index d6e49d708..81e98b2a6 100644 --- a/Changes +++ b/Changes @@ -13,10 +13,19 @@ Verilator 5.039 devel **Other:** +* Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] +* Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] +* Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). -* Fix class extends dotted error (#6162). [Igor Zaworski] +* Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] +* Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] +* Fix class extends dotted error (#6162). [Igor Zaworski, Antmicro Ltd.] * Fix genvar error with `-O0` (#6165). [Max Wipfli] +* Fix uninitialized thread PGO counters (#6167). [Bartłomiej Chmiel, Antmicro Ltd.] +* Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173). [Geza Lore] +* Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.]] +* Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.] Verilator 5.038 2025-07-08 @@ -107,7 +116,7 @@ Verilator 5.038 2025-07-08 * Fix wide non-blocking assignment mis-optimization (#6150) (#6152) (#6155). [Todd Strader] * Fix interface array connections with non-zero low declaration index. * Fix developer build error on MacOS/Flex2.6.4 (#6153). [Paul Swirhun] -* Fix crash with --dumpi-V3LinkDot without --debug (#6159). [Igor Zaworski] +* Fix crash with --dumpi-V3LinkDot without --debug (#6159). [Igor Zaworski, Antmicro Ltd.] Verilator 5.036 2025-04-27 diff --git a/docs/spelling.txt b/docs/spelling.txt index ee363f8ae..b8cdf4132 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -481,6 +481,7 @@ Wfuture Whatson Wildman Wim +Wipfli Wmisleading Wno Wojciech From 2f199f20cfb0f35f014bf643e7c71646a3083b05 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 12 Jul 2025 14:14:17 -0400 Subject: [PATCH 020/252] Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. --- Changes | 1 + docs/guide/warnings.rst | 22 ++++++++++++++++++++++ src/V3Error.h | 14 ++++++++------ src/V3FileLine.h | 4 ++++ src/V3LinkParse.cpp | 7 ++++--- src/V3Number.cpp | 2 +- src/V3Width.cpp | 11 +++++++++-- test_regress/t/t_enum_bad_value.out | 24 +++++++++++++++++++----- test_regress/t/t_enum_bad_value.v | 12 +++++++++--- 9 files changed, 77 insertions(+), 20 deletions(-) diff --git a/Changes b/Changes index 81e98b2a6..7e20b5412 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 5.039 devel **Other:** +* Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index d377d6236..b14612600 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -709,6 +709,28 @@ List Of Warnings missing." +.. option:: ENUMITEMWIDTH + + An error that an enum item value is being assigned from a value which + would be truncated (similar to :option:`WIDTHTRUNC`), or from a sized + literal constant with a different bit width (similar to + :option:`WIDTHTRUNC` or :option:`WIDTHEXPAND`). IEEE requires this + error, but it may be disabled. + + Faulty example: + + .. code-block:: sv + :linenos: + :emphasize-lines: 2 + + typedef enum [3:0] { + WRONG_WIDTH = 33'h3 //<--- Warning + } enum_t; + + To repair, correct the size of the item's value directly, or use a cast, + so the resulting width matches the enum's width. + + .. option:: ENUMVALUE An error that an enum data type value is being assigned from another data diff --git a/src/V3Error.h b/src/V3Error.h index d4d7a6656..307de72e8 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -99,6 +99,7 @@ public: DEPRECATED, // Feature will be deprecated ENCAPSULATED, // Error: local/protected violation ENDLABEL, // End lable name mismatch + ENUMITEMWIDTH, // Error: enum item width mismatch ENUMVALUE, // Error: enum type needs explicit cast EOFNEWLINE, // End-of-file missing newline GENCLK, // Generated Clock. Historical, never issued. @@ -204,8 +205,8 @@ public: "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", - "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", - "GENUNNAMED", "HIERBLOCK", + "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", + "GENCLK", "GENUNNAMED", "HIERBLOCK", "IFDEPTH", "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", @@ -219,7 +220,8 @@ public: "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP" ,"UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", "USERWARN", - "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", + "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", + "ZERODLY", "ZEROREPL", " MAX" }; // clang-format on @@ -249,9 +251,9 @@ public: bool pretendError() const VL_MT_SAFE { 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 == MODMISSING - || m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE - || m_e == ZEROREPL // Says IEEE + || m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == IMPURE + || m_e == MODMISSING || m_e == PINNOTFOUND || m_e == PKGNODECL + || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE ); } // Warnings to mention manual diff --git a/src/V3FileLine.h b/src/V3FileLine.h index ead89fd63..e7e0cbb8e 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -369,6 +369,10 @@ public: && m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx); } + bool equalFirstLineCol(const FileLine& rhs) const { + return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno + && m_firstColumn == rhs.m_firstColumn); + } // Returns -1 if (*this) should come before rhs after sorted. 1 for the opposite case. 0 for // equivalent. int operatorCompare(const FileLine& rhs) const { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index c1128b796..77769d5a7 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -267,16 +267,17 @@ class LinkParseVisitor final : public VNVisitor { const int left = nodep->rangep()->leftConst(); const int right = nodep->rangep()->rightConst(); const int increment = (left > right) ? -1 : 1; - int offset_from_init = 0; + uint32_t offset_from_init = 0; AstEnumItem* addp = nullptr; FileLine* const flp = nodep->fileline(); - for (int i = left; i != (right + increment); i += increment, offset_from_init++) { + for (int i = left; i != (right + increment); i += increment, ++offset_from_init) { const string name = nodep->name() + cvtToStr(i); AstNodeExpr* valuep = nullptr; if (nodep->valuep()) { + // V3Width looks for Adds with same fileline as the EnumItem valuep = new AstAdd{flp, nodep->valuep()->cloneTree(true), - new AstConst(flp, AstConst::Unsized32{}, offset_from_init)}; + new AstConst{flp, AstConst::Unsized32{}, offset_from_init}}; } addp = AstNode::addNext(addp, new AstEnumItem{flp, name, nullptr, valuep}); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 7856c04c4..09933f352 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1188,7 +1188,7 @@ uint32_t V3Number::countOnes() const { uint32_t V3Number::mostSetBitP1() const { for (int bit = width() - 1; bit >= 0; bit--) { - if (bitIs1(bit)) return bit + 1; + if (!bitIs0(bit)) return bit + 1; } return 0; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8dc92b97d..f35bcebb0 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2553,9 +2553,16 @@ class WidthVisitor final : public VNVisitor { // Default type is int, but common to assign narrower values, so minwidth from value userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p()); bool warnOn = true; - if (const AstConst* const constp = VN_CAST(nodep->valuep(), Const)) { + AstNodeExpr* valuep = nodep->valuep(); + if (const AstAdd* const anodep = VN_CAST(valuep, Add)) { + // If constructed by V3LinkParse due to "enumitem[N_REPEATS] value" + if (anodep->fileline()->equalFirstLineCol(*(nodep->fileline()))) + valuep = anodep->lhsp(); + } + if (const AstConst* const constp = VN_CAST(valuep, Const)) { if (static_cast(constp->num().mostSetBitP1()) > nodep->width()) { - constp->v3error("Enum value exceeds width of enum type (IEEE 1800-2023 6.19)"); + constp->v3warn(ENUMITEMWIDTH, + "Enum value exceeds width of enum type (IEEE 1800-2023 6.19)"); warnOn = false; // Prevent normal WIDTHTRUNC } } diff --git a/test_regress/t/t_enum_bad_value.out b/test_regress/t/t_enum_bad_value.out index d9920ebaa..a185699b5 100644 --- a/test_regress/t/t_enum_bad_value.out +++ b/test_regress/t/t_enum_bad_value.out @@ -1,6 +1,20 @@ -%Error: t/t_enum_bad_value.v:10:19: Enum value exceeds width of enum type (IEEE 1800-2023 6.19) - : ... note: In instance 't' - 10 | VALUE_BAD = 8 - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:12:37: Enum value exceeds width of enum type (IEEE 1800-2023 6.19) + : ... note: In instance 't' + 12 | typedef enum [2:0] { VALUE_BAD1 = 8 } enum_t; + | ^ + ... For error description see https://verilator.org/warn/ENUMITEMWIDTH?v=latest +%Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:14:29: Enum value exceeds width of enum type (IEEE 1800-2023 6.19) + : ... note: In instance 't' + 14 | enum bit [4:0] {BAD2[4] = 100} bad2; + | ^~~ +%Warning-WIDTHTRUNC: t/t_enum_bad_value.v:14:19: Operator ADD expects 5 bits on the LHS, but LHS's CONST '?32?sh64' generates 32 or 7 bits. + : ... note: In instance 't' + 14 | enum bit [4:0] {BAD2[4] = 100} bad2; + | ^~~~ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:16:28: Enum value exceeds width of enum type (IEEE 1800-2023 6.19) + : ... note: In instance 't' + 16 | enum logic [3:0] {BAD3 = 5'bxxxxx} bad3; + | ^~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_enum_bad_value.v b/test_regress/t/t_enum_bad_value.v index ed97f82a0..3834b382d 100644 --- a/test_regress/t/t_enum_bad_value.v +++ b/test_regress/t/t_enum_bad_value.v @@ -6,8 +6,14 @@ module t(); - typedef enum [2:0] { - VALUE_BAD = 8 - } enum_t; + enum bit signed [3:0] {OK1 = -1} ok1_t; // As is signed, loss of 1 bits is ok per IEEE + enum bit signed [3:0] {OK2 = 3} ok2_t; + typedef enum [2:0] { VALUE_BAD1 = 8 } enum_t; + + enum bit [4:0] {BAD2[4] = 100} bad2; + + enum logic [3:0] {BAD3 = 5'bxxxxx} bad3; + + initial $stop; endmodule From e2e5d9eaf137d239d82df1b3c28586f68dbc5ca0 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 14 Jul 2025 12:51:58 +0200 Subject: [PATCH 021/252] Support disabling a fork from outside that fork (#6174) --- include/verilated_std.sv | 8 + src/V3AstNodeOther.h | 2 +- src/V3LinkJump.cpp | 91 +- src/V3ParseImp.h | 2 +- src/verilog.y | 4 +- test_regress/t/t_disable.out | 2 +- test_regress/t/t_disable_outside.py | 18 + test_regress/t/t_disable_outside.v | 26 + test_regress/t/t_disable_outside2.py | 18 + test_regress/t/t_disable_outside2.v | 29 + .../t/t_disable_within_task_unsup.out | 5 + test_regress/t/t_disable_within_task_unsup.py | 16 + test_regress/t/t_disable_within_task_unsup.v | 31 + test_regress/t/t_dump_json.out | 979 ++++++++++++++---- 14 files changed, 994 insertions(+), 237 deletions(-) create mode 100755 test_regress/t/t_disable_outside.py create mode 100644 test_regress/t/t_disable_outside.v create mode 100755 test_regress/t/t_disable_outside2.py create mode 100644 test_regress/t/t_disable_outside2.v create mode 100644 test_regress/t/t_disable_within_task_unsup.out create mode 100755 test_regress/t/t_disable_within_task_unsup.py create mode 100644 test_regress/t/t_disable_within_task_unsup.v diff --git a/include/verilated_std.sv b/include/verilated_std.sv index 1c13cd42e..d20d4e836 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -175,6 +175,14 @@ package std; `endif endtask + static task killQueue(ref process processQueue[$]); +`ifdef VERILATOR_TIMING + while (processQueue.size() > 0) begin + processQueue.pop_back().kill(); + end +`endif + endtask + // Two process references are equal if the different classes' containing // m_process are equal. Can't yet use <=> as the base class template // comparisons doesn't define <=> as they don't yet require --timing and C++20. diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 5d2ac8b3c..6f7e3195b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1974,7 +1974,7 @@ class AstVar final : public AstNode { bool m_isConst : 1; // Table contains constant data bool m_isContinuously : 1; // Ever assigned continuously (for force/release) bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier - bool m_isStatic : 1; // Static C variable (for Verilog see instead isAutomatic) + bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime()) bool m_isPulldown : 1; // Tri0 bool m_isPullup : 1; // Tri1 bool m_isIfaceParent : 1; // dtype is reference to interface present in this module diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 3cfb7afab..fa65ec3d7 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -35,6 +35,7 @@ #include "V3AstUserAllocator.h" #include "V3Error.h" +#include "V3UniqueNames.h" #include @@ -60,6 +61,8 @@ class LinkJumpVisitor final : public VNVisitor { int m_modRepeatNum = 0; // Repeat counter VOptionBool m_unrollFull; // Pragma full, disable, or default unrolling std::vector m_blockStack; // All begin blocks above current node + V3UniqueNames m_queueNames{ + "__VprocessQueue"}; // Names for queues needed for 'disable' handling // METHODS AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { @@ -159,6 +162,29 @@ class LinkJumpVisitor final : public VNVisitor { if (AstNode* const refp = nodep->op4p()) addPrefixToBlocksRecurse(prefix, refp); if (AstNode* const refp = nodep->nextp()) addPrefixToBlocksRecurse(prefix, refp); } + static AstNode* getMemberp(const AstNodeModule* const nodep, const std::string& name) { + for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { + if (itemp->name() == name) return itemp; + } + return nullptr; + } + bool existsBlockAbove(const std::string& name) const { + for (const AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) { + if (stackp->name() == name) return true; + } + return false; + } + static AstStmtExpr* getQueuePushProcessSelfp(AstVarRef* const queueRefp) { + // Constructs queue.push_back(std::process::self()) statement + FileLine* const fl = queueRefp->fileline(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + AstFunc* const selfMethodp = VN_AS(getMemberp(processClassp, "self"), Func); + AstFuncRef* const processSelfp = new AstFuncRef{fl, selfMethodp, nullptr}; + processSelfp->classOrPackagep(processClassp); + return new AstStmtExpr{ + fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; + } // VISITORS void visit(AstNodeModule* nodep) override { @@ -338,22 +364,65 @@ class LinkJumpVisitor final : public VNVisitor { void visit(AstDisable* nodep) override { UINFO(8, " DISABLE " << nodep); AstNode* const targetp = nodep->targetp(); + FileLine* const fl = nodep->fileline(); UASSERT_OBJ(targetp, nodep, "Unlinked disable statement"); if (VN_IS(targetp, Task)) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name"); - } else if (VN_IS(targetp, Fork)) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name"); - } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { - const std::string targetName = beginp->name(); - bool aboveBlock = false; - for (AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) { - UINFO(9, " UNDERBLK " << stackp); - if (stackp->name() == targetName) { - aboveBlock = true; - break; + } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { + // The support is limited only to disabling a fork from outside that fork. + // It utilizes the process::kill()` method. For each `disable` a queue of processes is + // declared. At the beginning of each fork that can be disabled, its process handle is + // pushed to the queue. `disable` statement is replaced with calling `kill()` method on + // each element of the queue. + if (existsBlockAbove(forkp->name())) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork"); + } + if (m_ftaskp) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); + } + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + // Declare queue of processes (as a global variable for simplicity) + AstVar* const processQueuep = new AstVar{ + fl, VVarType::VAR, m_queueNames.get(forkp->name()), VFlagChildDType{}, + new AstQueueDType{fl, VFlagChildDType{}, + new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; + processQueuep->lifetime(VLifetime::STATIC); + topPkgp->addStmtsp(processQueuep); + + AstVarRef* const queueWriteRefp + = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; + AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); + + for (AstNode* forkItemp = forkp->stmtsp(); forkItemp; forkItemp = forkItemp->nextp()) { + // Add push_back statement at the beginning of each fork. + // Wrap into begin block if needed + AstBegin* beginp = VN_CAST(forkItemp, Begin); + if (!beginp) { + beginp = new AstBegin{fl, "", nullptr}; + forkItemp->replaceWith(beginp); + beginp->addStmtsp(forkItemp); + // In order to continue the iteration + forkItemp = beginp; + } + if (pushCurrentProcessp->backp()) { + beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp->cloneTree(false)); + } else { + beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); } } - if (aboveBlock) { + + AstVarRef* const queueRefp + = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; + AstTaskRef* const killQueueCall + = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), + new AstArg{fl, "", queueRefp}}; + killQueueCall->classOrPackagep(processClassp); + nodep->addNextHere(new AstStmtExpr{fl, killQueueCall}); + } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { + const std::string targetName = beginp->name(); + if (existsBlockAbove(targetName)) { if (beginp->user3()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling block that contains a fork"); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 5e210691c..fe6c100c1 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -293,12 +293,12 @@ public: void dumpInputsFile() VL_MT_DISABLED; void dumpTokensAhead(int line) VL_MT_DISABLED; static void candidatePli(VSpellCheck* spellerp) VL_MT_DISABLED; + void importIfInStd(FileLine* fileline, const string& id); private: void preprocDumps(std::ostream& os); void lexFile(const string& modname) VL_MT_DISABLED; void yylexReadTok() VL_MT_DISABLED; - void importIfInStd(FileLine* fileline, const string& id); void tokenPull() VL_MT_DISABLED; void tokenPipeline() VL_MT_DISABLED; // Internal; called from tokenToBison int tokenPipelineId(int token) VL_MT_DISABLED; diff --git a/src/verilog.y b/src/verilog.y index 085182f07..bc09282af 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3754,7 +3754,9 @@ statement_item: // IEEE: statement_item // // IEEE: disable_statement | yDISABLE yFORK ';' { $$ = new AstDisableFork{$1}; } | yDISABLE idDottedSel ';' - { $$ = new AstDisable{$1, $2}; } + { $$ = new AstDisable{$1, $2}; + PARSEP->importIfInStd($1, "process"); + } // // IEEE: event_trigger | yP_MINUSGT expr ';' { $$ = new AstFireEvent{$1, $2, false}; } diff --git a/test_regress/t/t_disable.out b/test_regress/t/t_disable.out index 6a0001088..a42b835d4 100644 --- a/test_regress/t/t_disable.out +++ b/test_regress/t/t_disable.out @@ -1,4 +1,4 @@ -%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork by name +%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork from within same fork 11 | disable foo; | ^~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest diff --git a/test_regress/t/t_disable_outside.py b/test_regress/t/t_disable_outside.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_outside.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_outside.v b/test_regress/t/t_disable_outside.v new file mode 100644 index 000000000..526c5eaf3 --- /dev/null +++ b/test_regress/t/t_disable_outside.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + begin : blk + int x = 0; + fork : fork_blk + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk; + #2; + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_disable_outside2.py b/test_regress/t/t_disable_outside2.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_outside2.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_outside2.v b/test_regress/t/t_disable_outside2.v new file mode 100644 index 000000000..f4e397359 --- /dev/null +++ b/test_regress/t/t_disable_outside2.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + for (int i = 0; i < 3; i++) begin + begin : blk + int x = 0; + fork : fork_blk + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + if (i < 2) disable fork_blk; + #2; + if (i < 2 && x != 1) $stop; + if (i == 2 && x != 2) $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_disable_within_task_unsup.out b/test_regress/t/t_disable_within_task_unsup.out new file mode 100644 index 000000000..2435b8d8e --- /dev/null +++ b/test_regress/t/t_disable_within_task_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_disable_within_task_unsup.v:8:4: Unsupported: disabling fork from task / function + 8 | disable t.init.fork_blk; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_disable_within_task_unsup.py b/test_regress/t/t_disable_within_task_unsup.py new file mode 100755 index 000000000..44c75cc0f --- /dev/null +++ b/test_regress/t/t_disable_within_task_unsup.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('simulator') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_disable_within_task_unsup.v b/test_regress/t/t_disable_within_task_unsup.v new file mode 100644 index 000000000..cc7ae4923 --- /dev/null +++ b/test_regress/t/t_disable_within_task_unsup.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +task disable_fork_blk; + disable t.init.fork_blk; +endtask + +module t(/*AUTOARG*/); + + initial begin : init + int x = 0; + fork : fork_blk + begin + x = 1; + disable_fork_blk(); + x = 2; + end + begin + #1; + x = 3; + end + join + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 36d58e850..c2dc38ecd 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -1,427 +1,427 @@ -{"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", +{"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"(E)","stdPackagep":"(F)","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"t","addr":"(E)","loc":"e,7:8,7:9","isChecker":false,"isProgram":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"t","addr":"(G)","loc":"e,7:8,7:9","isChecker":false,"isProgram":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ - {"type":"PORT","name":"clk","addr":"(F)","loc":"e,9:4,9:7","exprp": []}, - {"type":"VAR","name":"clk","addr":"(G)","loc":"e,11:10,11:13","dtypep":"UNLINKED","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"PORT","name":"clk","addr":"(H)","loc":"e,9:4,9:7","exprp": []}, + {"type":"VAR","name":"clk","addr":"(I)","loc":"e,11:10,11:13","dtypep":"UNLINKED","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"LOGIC_IMPLICIT","addr":"(H)","loc":"e,11:10,11:13","dtypep":"(H)","keyword":"LOGIC_IMPLICIT","generic":false,"rangep": []} + {"type":"BASICDTYPE","name":"LOGIC_IMPLICIT","addr":"(J)","loc":"e,11:10,11:13","dtypep":"(J)","keyword":"LOGIC_IMPLICIT","generic":false,"rangep": []} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"cyc","addr":"(I)","loc":"e,13:12,13:15","dtypep":"UNLINKED","origName":"cyc","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"cyc","addr":"(K)","loc":"e,13:12,13:15","dtypep":"UNLINKED","origName":"cyc","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"integer","addr":"(J)","loc":"e,13:4,13:11","dtypep":"(J)","keyword":"integer","range":"31:0","generic":false,"rangep": []} + {"type":"BASICDTYPE","name":"integer","addr":"(L)","loc":"e,13:4,13:11","dtypep":"(L)","keyword":"integer","range":"31:0","generic":false,"rangep": []} ],"delayp": [], "valuep": [ - {"type":"CONST","name":"?32?sh0","addr":"(K)","loc":"e,13:18,13:19","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(M)","loc":"e,13:18,13:19","dtypep":"(N)"} ],"attrsp": []}, - {"type":"VAR","name":"crc","addr":"(M)","loc":"e,14:15,14:18","dtypep":"UNLINKED","origName":"crc","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"crc","addr":"(O)","loc":"e,14:15,14:18","dtypep":"UNLINKED","origName":"crc","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(N)","loc":"e,14:4,14:7","dtypep":"(N)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(P)","loc":"e,14:4,14:7","dtypep":"(P)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(O)","loc":"e,14:8,14:9","ascending":false, + {"type":"RANGE","name":"","addr":"(Q)","loc":"e,14:8,14:9","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh3f","addr":"(P)","loc":"e,14:9,14:11","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3f","addr":"(R)","loc":"e,14:9,14:11","dtypep":"(S)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(R)","loc":"e,14:12,14:13","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(T)","loc":"e,14:12,14:13","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"sum","addr":"(S)","loc":"e,15:15,15:18","dtypep":"UNLINKED","origName":"sum","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"sum","addr":"(U)","loc":"e,15:15,15:18","dtypep":"UNLINKED","origName":"sum","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(T)","loc":"e,15:4,15:7","dtypep":"(T)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(V)","loc":"e,15:4,15:7","dtypep":"(V)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(U)","loc":"e,15:8,15:9","ascending":false, + {"type":"RANGE","name":"","addr":"(W)","loc":"e,15:8,15:9","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh3f","addr":"(V)","loc":"e,15:9,15:11","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3f","addr":"(X)","loc":"e,15:9,15:11","dtypep":"(S)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(W)","loc":"e,15:12,15:13","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(Y)","loc":"e,15:12,15:13","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"in","addr":"(X)","loc":"e,18:16,18:18","dtypep":"UNLINKED","origName":"in","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"in","addr":"(Z)","loc":"e,18:16,18:18","dtypep":"UNLINKED","origName":"in","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(Y)","loc":"e,18:9,18:10","dtypep":"(Y)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(AB)","loc":"e,18:9,18:10","dtypep":"(AB)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(Z)","loc":"e,18:9,18:10","ascending":false, + {"type":"RANGE","name":"","addr":"(BB)","loc":"e,18:9,18:10","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(AB)","loc":"e,18:10,18:12","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(CB)","loc":"e,18:10,18:12","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(CB)","loc":"e,18:13,18:14","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(EB)","loc":"e,18:13,18:14","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"ASSIGNW","name":"","addr":"(DB)","loc":"e,18:19,18:20","dtypep":"UNLINKED", + {"type":"ASSIGNW","name":"","addr":"(FB)","loc":"e,18:19,18:20","dtypep":"UNLINKED", "rhsp": [ - {"type":"SELEXTRACT","name":"","addr":"(EB)","loc":"e,18:24,18:25","dtypep":"UNLINKED", + {"type":"SELEXTRACT","name":"","addr":"(GB)","loc":"e,18:24,18:25","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"crc","addr":"(FB)","loc":"e,18:21,18:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(HB)","loc":"e,18:21,18:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(GB)","loc":"e,18:25,18:27","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(IB)","loc":"e,18:25,18:27","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(HB)","loc":"e,18:28,18:29","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(JB)","loc":"e,18:28,18:29","dtypep":"(N)"} ],"attrp": []} ], "lhsp": [ - {"type":"PARSEREF","name":"in","addr":"(IB)","loc":"e,18:16,18:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"in","addr":"(KB)","loc":"e,18:16,18:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": [],"strengthSpecp": []}, - {"type":"VAR","name":"out","addr":"(JB)","loc":"e,22:25,22:28","dtypep":"UNLINKED","origName":"out","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"out","addr":"(LB)","loc":"e,22:25,22:28","dtypep":"UNLINKED","origName":"out","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(KB)","loc":"e,22:9,22:10","dtypep":"(KB)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(MB)","loc":"e,22:9,22:10","dtypep":"(MB)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(LB)","loc":"e,22:9,22:10","ascending":false, + {"type":"RANGE","name":"","addr":"(NB)","loc":"e,22:9,22:10","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(MB)","loc":"e,22:10,22:12","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(OB)","loc":"e,22:10,22:12","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(NB)","loc":"e,22:13,22:14","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(PB)","loc":"e,22:13,22:14","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"CELL","name":"test","addr":"(OB)","loc":"e,25:9,25:13","origName":"test","recursive":false,"modp":"(PB)", + {"type":"CELL","name":"test","addr":"(QB)","loc":"e,25:9,25:13","origName":"test","recursive":false,"modp":"(RB)", "pinsp": [ - {"type":"PIN","name":"out","addr":"(QB)","loc":"e,27:15,27:18","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", + {"type":"PIN","name":"out","addr":"(SB)","loc":"e,27:15,27:18","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", "exprp": [ - {"type":"SELEXTRACT","name":"","addr":"(RB)","loc":"e,27:45,27:46","dtypep":"UNLINKED", + {"type":"SELEXTRACT","name":"","addr":"(TB)","loc":"e,27:45,27:46","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"out","addr":"(SB)","loc":"e,27:42,27:45","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"out","addr":"(UB)","loc":"e,27:42,27:45","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(TB)","loc":"e,27:46,27:48","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(VB)","loc":"e,27:46,27:48","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(UB)","loc":"e,27:49,27:50","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(WB)","loc":"e,27:49,27:50","dtypep":"(N)"} ],"attrp": []} ]}, - {"type":"PIN","name":"clk","addr":"(VB)","loc":"e,29:15,29:18","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", + {"type":"PIN","name":"clk","addr":"(XB)","loc":"e,29:15,29:18","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", "exprp": [ - {"type":"PARSEREF","name":"clk","addr":"(WB)","loc":"e,29:42,29:45","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"clk","addr":"(YB)","loc":"e,29:42,29:45","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ]}, - {"type":"PIN","name":"in","addr":"(XB)","loc":"e,30:15,30:17","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", + {"type":"PIN","name":"in","addr":"(ZB)","loc":"e,30:15,30:17","svDotName":true,"svImplicit":false,"modVarp":"UNLINKED","modPTypep":"UNLINKED", "exprp": [ - {"type":"SELEXTRACT","name":"","addr":"(YB)","loc":"e,30:44,30:45","dtypep":"UNLINKED", + {"type":"SELEXTRACT","name":"","addr":"(AC)","loc":"e,30:44,30:45","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"in","addr":"(ZB)","loc":"e,30:42,30:44","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"in","addr":"(BC)","loc":"e,30:42,30:44","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(AC)","loc":"e,30:45,30:47","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(CC)","loc":"e,30:45,30:47","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(BC)","loc":"e,30:48,30:49","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(DC)","loc":"e,30:48,30:49","dtypep":"(N)"} ],"attrp": []} ]} ],"paramsp": [],"rangep": [],"intfRefsp": []}, - {"type":"VAR","name":"result","addr":"(CC)","loc":"e,33:16,33:22","dtypep":"UNLINKED","origName":"result","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"result","addr":"(EC)","loc":"e,33:16,33:22","dtypep":"UNLINKED","origName":"result","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"WIRE","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(DC)","loc":"e,33:9,33:10","dtypep":"(DC)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(FC)","loc":"e,33:9,33:10","dtypep":"(FC)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(EC)","loc":"e,33:9,33:10","ascending":false, + {"type":"RANGE","name":"","addr":"(GC)","loc":"e,33:9,33:10","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh3f","addr":"(FC)","loc":"e,33:10,33:12","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3f","addr":"(HC)","loc":"e,33:10,33:12","dtypep":"(S)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(GC)","loc":"e,33:13,33:14","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(IC)","loc":"e,33:13,33:14","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"ASSIGNW","name":"","addr":"(HC)","loc":"e,33:23,33:24","dtypep":"UNLINKED", + {"type":"ASSIGNW","name":"","addr":"(JC)","loc":"e,33:23,33:24","dtypep":"UNLINKED", "rhsp": [ - {"type":"REPLICATE","name":"","addr":"(IC)","loc":"e,33:25,33:26","dtypep":"(JC)", + {"type":"REPLICATE","name":"","addr":"(KC)","loc":"e,33:25,33:26","dtypep":"(LC)", "srcp": [ - {"type":"CONCAT","name":"","addr":"(KC)","loc":"e,33:31,33:32","dtypep":"UNLINKED", + {"type":"CONCAT","name":"","addr":"(MC)","loc":"e,33:31,33:32","dtypep":"UNLINKED", "lhsp": [ - {"type":"CONST","name":"32'h0","addr":"(LC)","loc":"e,33:26,33:31","dtypep":"(MC)"} + {"type":"CONST","name":"32'h0","addr":"(NC)","loc":"e,33:26,33:31","dtypep":"(OC)"} ], "rhsp": [ - {"type":"PARSEREF","name":"out","addr":"(NC)","loc":"e,33:33,33:36","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"out","addr":"(PC)","loc":"e,33:33,33:36","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ]} ], "countp": [ - {"type":"CONST","name":"32'h1","addr":"(OC)","loc":"e,33:25,33:26","dtypep":"(MC)"} + {"type":"CONST","name":"32'h1","addr":"(QC)","loc":"e,33:25,33:26","dtypep":"(OC)"} ]} ], "lhsp": [ - {"type":"PARSEREF","name":"result","addr":"(PC)","loc":"e,33:16,33:22","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"result","addr":"(RC)","loc":"e,33:16,33:22","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": [],"strengthSpecp": []}, - {"type":"ALWAYS","name":"","addr":"(QC)","loc":"e,36:4,36:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(SC)","loc":"e,36:4,36:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], "stmtsp": [ - {"type":"EVENTCONTROL","name":"","addr":"(RC)","loc":"e,36:11,36:12", + {"type":"EVENTCONTROL","name":"","addr":"(TC)","loc":"e,36:11,36:12", "sensesp": [ - {"type":"SENTREE","name":"","addr":"(SC)","loc":"e,36:11,36:12","isMulti":false, + {"type":"SENTREE","name":"","addr":"(UC)","loc":"e,36:11,36:12","isMulti":false, "sensesp": [ - {"type":"SENITEM","name":"","addr":"(TC)","loc":"e,36:14,36:21","edgeType":"POS", + {"type":"SENITEM","name":"","addr":"(VC)","loc":"e,36:14,36:21","edgeType":"POS", "sensp": [ - {"type":"PARSEREF","name":"clk","addr":"(UC)","loc":"e,36:22,36:25","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"clk","addr":"(WC)","loc":"e,36:22,36:25","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"condp": []} ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(VC)","loc":"e,36:27,36:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(XC)","loc":"e,36:27,36:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], "stmtsp": [ - {"type":"ASSIGNDLY","name":"","addr":"(WC)","loc":"e,40:11,40:13","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(YC)","loc":"e,40:11,40:13","dtypep":"UNLINKED", "rhsp": [ - {"type":"ADD","name":"","addr":"(XC)","loc":"e,40:18,40:19","dtypep":"UNLINKED", + {"type":"ADD","name":"","addr":"(ZC)","loc":"e,40:18,40:19","dtypep":"UNLINKED", "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(YC)","loc":"e,40:14,40:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(AD)","loc":"e,40:14,40:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"?32?sh1","addr":"(ZC)","loc":"e,40:20,40:21","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh1","addr":"(BD)","loc":"e,40:20,40:21","dtypep":"(N)"} ]} ], "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(AD)","loc":"e,40:7,40:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(CD)","loc":"e,40:7,40:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(BD)","loc":"e,41:11,41:13","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(DD)","loc":"e,41:11,41:13","dtypep":"UNLINKED", "rhsp": [ - {"type":"REPLICATE","name":"","addr":"(CD)","loc":"e,41:14,41:15","dtypep":"(JC)", + {"type":"REPLICATE","name":"","addr":"(ED)","loc":"e,41:14,41:15","dtypep":"(LC)", "srcp": [ - {"type":"CONCAT","name":"","addr":"(DD)","loc":"e,41:24,41:25","dtypep":"UNLINKED", + {"type":"CONCAT","name":"","addr":"(FD)","loc":"e,41:24,41:25","dtypep":"UNLINKED", "lhsp": [ - {"type":"SELEXTRACT","name":"","addr":"(ED)","loc":"e,41:18,41:19","dtypep":"UNLINKED", + {"type":"SELEXTRACT","name":"","addr":"(GD)","loc":"e,41:18,41:19","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"crc","addr":"(FD)","loc":"e,41:15,41:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(HD)","loc":"e,41:15,41:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "leftp": [ - {"type":"CONST","name":"?32?sh3e","addr":"(GD)","loc":"e,41:19,41:21","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3e","addr":"(ID)","loc":"e,41:19,41:21","dtypep":"(S)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(HD)","loc":"e,41:22,41:23","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(JD)","loc":"e,41:22,41:23","dtypep":"(N)"} ],"attrp": []} ], "rhsp": [ - {"type":"XOR","name":"","addr":"(ID)","loc":"e,41:43,41:44","dtypep":"UNLINKED", + {"type":"XOR","name":"","addr":"(KD)","loc":"e,41:43,41:44","dtypep":"UNLINKED", "lhsp": [ - {"type":"XOR","name":"","addr":"(JD)","loc":"e,41:34,41:35","dtypep":"UNLINKED", + {"type":"XOR","name":"","addr":"(LD)","loc":"e,41:34,41:35","dtypep":"UNLINKED", "lhsp": [ - {"type":"SELBIT","name":"","addr":"(KD)","loc":"e,41:29,41:30","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(MD)","loc":"e,41:29,41:30","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"crc","addr":"(LD)","loc":"e,41:26,41:29","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(ND)","loc":"e,41:26,41:29","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh3f","addr":"(MD)","loc":"e,41:30,41:32","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3f","addr":"(OD)","loc":"e,41:30,41:32","dtypep":"(S)"} ],"thsp": [],"attrp": []} ], "rhsp": [ - {"type":"SELBIT","name":"","addr":"(ND)","loc":"e,41:39,41:40","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(PD)","loc":"e,41:39,41:40","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"crc","addr":"(OD)","loc":"e,41:36,41:39","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(QD)","loc":"e,41:36,41:39","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh2","addr":"(PD)","loc":"e,41:40,41:41","dtypep":"(QD)"} + {"type":"CONST","name":"?32?sh2","addr":"(RD)","loc":"e,41:40,41:41","dtypep":"(SD)"} ],"thsp": [],"attrp": []} ]} ], "rhsp": [ - {"type":"SELBIT","name":"","addr":"(RD)","loc":"e,41:48,41:49","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(TD)","loc":"e,41:48,41:49","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"crc","addr":"(SD)","loc":"e,41:45,41:48","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(UD)","loc":"e,41:45,41:48","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh0","addr":"(TD)","loc":"e,41:49,41:50","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(VD)","loc":"e,41:49,41:50","dtypep":"(N)"} ],"thsp": [],"attrp": []} ]} ]} ], "countp": [ - {"type":"CONST","name":"32'h1","addr":"(UD)","loc":"e,41:14,41:15","dtypep":"(MC)"} + {"type":"CONST","name":"32'h1","addr":"(WD)","loc":"e,41:14,41:15","dtypep":"(OC)"} ]} ], "lhsp": [ - {"type":"PARSEREF","name":"crc","addr":"(VD)","loc":"e,41:7,41:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(XD)","loc":"e,41:7,41:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(WD)","loc":"e,42:11,42:13","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(YD)","loc":"e,42:11,42:13","dtypep":"UNLINKED", "rhsp": [ - {"type":"XOR","name":"","addr":"(XD)","loc":"e,42:21,42:22","dtypep":"UNLINKED", + {"type":"XOR","name":"","addr":"(ZD)","loc":"e,42:21,42:22","dtypep":"UNLINKED", "lhsp": [ - {"type":"PARSEREF","name":"result","addr":"(YD)","loc":"e,42:14,42:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"result","addr":"(AE)","loc":"e,42:14,42:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"REPLICATE","name":"","addr":"(ZD)","loc":"e,42:23,42:24","dtypep":"(JC)", + {"type":"REPLICATE","name":"","addr":"(BE)","loc":"e,42:23,42:24","dtypep":"(LC)", "srcp": [ - {"type":"CONCAT","name":"","addr":"(AE)","loc":"e,42:33,42:34","dtypep":"UNLINKED", + {"type":"CONCAT","name":"","addr":"(CE)","loc":"e,42:33,42:34","dtypep":"UNLINKED", "lhsp": [ - {"type":"SELEXTRACT","name":"","addr":"(BE)","loc":"e,42:27,42:28","dtypep":"UNLINKED", + {"type":"SELEXTRACT","name":"","addr":"(DE)","loc":"e,42:27,42:28","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"sum","addr":"(CE)","loc":"e,42:24,42:27","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(EE)","loc":"e,42:24,42:27","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "leftp": [ - {"type":"CONST","name":"?32?sh3e","addr":"(DE)","loc":"e,42:28,42:30","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3e","addr":"(FE)","loc":"e,42:28,42:30","dtypep":"(S)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(EE)","loc":"e,42:31,42:32","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(GE)","loc":"e,42:31,42:32","dtypep":"(N)"} ],"attrp": []} ], "rhsp": [ - {"type":"XOR","name":"","addr":"(FE)","loc":"e,42:52,42:53","dtypep":"UNLINKED", + {"type":"XOR","name":"","addr":"(HE)","loc":"e,42:52,42:53","dtypep":"UNLINKED", "lhsp": [ - {"type":"XOR","name":"","addr":"(GE)","loc":"e,42:43,42:44","dtypep":"UNLINKED", + {"type":"XOR","name":"","addr":"(IE)","loc":"e,42:43,42:44","dtypep":"UNLINKED", "lhsp": [ - {"type":"SELBIT","name":"","addr":"(HE)","loc":"e,42:38,42:39","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(JE)","loc":"e,42:38,42:39","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"sum","addr":"(IE)","loc":"e,42:35,42:38","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(KE)","loc":"e,42:35,42:38","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh3f","addr":"(JE)","loc":"e,42:39,42:41","dtypep":"(Q)"} + {"type":"CONST","name":"?32?sh3f","addr":"(LE)","loc":"e,42:39,42:41","dtypep":"(S)"} ],"thsp": [],"attrp": []} ], "rhsp": [ - {"type":"SELBIT","name":"","addr":"(KE)","loc":"e,42:48,42:49","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(ME)","loc":"e,42:48,42:49","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"sum","addr":"(LE)","loc":"e,42:45,42:48","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(NE)","loc":"e,42:45,42:48","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh2","addr":"(ME)","loc":"e,42:49,42:50","dtypep":"(QD)"} + {"type":"CONST","name":"?32?sh2","addr":"(OE)","loc":"e,42:49,42:50","dtypep":"(SD)"} ],"thsp": [],"attrp": []} ]} ], "rhsp": [ - {"type":"SELBIT","name":"","addr":"(NE)","loc":"e,42:57,42:58","dtypep":"UNLINKED", + {"type":"SELBIT","name":"","addr":"(PE)","loc":"e,42:57,42:58","dtypep":"UNLINKED", "fromp": [ - {"type":"PARSEREF","name":"sum","addr":"(OE)","loc":"e,42:54,42:57","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(QE)","loc":"e,42:54,42:57","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "bitp": [ - {"type":"CONST","name":"?32?sh0","addr":"(PE)","loc":"e,42:58,42:59","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(RE)","loc":"e,42:58,42:59","dtypep":"(N)"} ],"thsp": [],"attrp": []} ]} ]} ], "countp": [ - {"type":"CONST","name":"32'h1","addr":"(QE)","loc":"e,42:23,42:24","dtypep":"(MC)"} + {"type":"CONST","name":"32'h1","addr":"(SE)","loc":"e,42:23,42:24","dtypep":"(OC)"} ]} ]} ], "lhsp": [ - {"type":"PARSEREF","name":"sum","addr":"(RE)","loc":"e,42:7,42:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(TE)","loc":"e,42:7,42:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []}, - {"type":"IF","name":"","addr":"(SE)","loc":"e,43:7,43:9", + {"type":"IF","name":"","addr":"(UE)","loc":"e,43:7,43:9", "condp": [ - {"type":"EQ","name":"","addr":"(TE)","loc":"e,43:15,43:17","dtypep":"(UE)", + {"type":"EQ","name":"","addr":"(VE)","loc":"e,43:15,43:17","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(VE)","loc":"e,43:11,43:14","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(XE)","loc":"e,43:11,43:14","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"?32?sh0","addr":"(WE)","loc":"e,43:18,43:19","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(YE)","loc":"e,43:18,43:19","dtypep":"(N)"} ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(XE)","loc":"e,43:21,43:26","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(ZE)","loc":"e,43:21,43:26","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], "stmtsp": [ - {"type":"ASSIGNDLY","name":"","addr":"(YE)","loc":"e,45:14,45:16","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(AF)","loc":"e,45:14,45:16","dtypep":"UNLINKED", "rhsp": [ - {"type":"CONST","name":"64'h5aef0c8dd70a4497","addr":"(ZE)","loc":"e,45:17,45:38","dtypep":"(AF)"} + {"type":"CONST","name":"64'h5aef0c8dd70a4497","addr":"(BF)","loc":"e,45:17,45:38","dtypep":"(CF)"} ], "lhsp": [ - {"type":"PARSEREF","name":"crc","addr":"(BF)","loc":"e,45:10,45:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(DF)","loc":"e,45:10,45:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(CF)","loc":"e,46:14,46:16","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(EF)","loc":"e,46:14,46:16","dtypep":"UNLINKED", "rhsp": [ - {"type":"CONST","name":"'0","addr":"(DF)","loc":"e,46:17,46:19","dtypep":"(UE)"} + {"type":"CONST","name":"'0","addr":"(FF)","loc":"e,46:17,46:19","dtypep":"(WE)"} ], "lhsp": [ - {"type":"PARSEREF","name":"sum","addr":"(EF)","loc":"e,46:10,46:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(GF)","loc":"e,46:10,46:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []} ]} ], "elsesp": [ - {"type":"IF","name":"","addr":"(FF)","loc":"e,48:12,48:14", + {"type":"IF","name":"","addr":"(HF)","loc":"e,48:12,48:14", "condp": [ - {"type":"LT","name":"","addr":"(GF)","loc":"e,48:20,48:21","dtypep":"(UE)", + {"type":"LT","name":"","addr":"(IF)","loc":"e,48:20,48:21","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(HF)","loc":"e,48:16,48:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(JF)","loc":"e,48:16,48:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"?32?sha","addr":"(IF)","loc":"e,48:22,48:24","dtypep":"(JF)"} + {"type":"CONST","name":"?32?sha","addr":"(KF)","loc":"e,48:22,48:24","dtypep":"(LF)"} ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(KF)","loc":"e,48:26,48:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(MF)","loc":"e,48:26,48:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], "stmtsp": [ - {"type":"ASSIGNDLY","name":"","addr":"(LF)","loc":"e,49:14,49:16","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(NF)","loc":"e,49:14,49:16","dtypep":"UNLINKED", "rhsp": [ - {"type":"CONST","name":"'0","addr":"(MF)","loc":"e,49:17,49:19","dtypep":"(UE)"} + {"type":"CONST","name":"'0","addr":"(OF)","loc":"e,49:17,49:19","dtypep":"(WE)"} ], "lhsp": [ - {"type":"PARSEREF","name":"sum","addr":"(NF)","loc":"e,49:10,49:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(PF)","loc":"e,49:10,49:13","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []} ]} ], "elsesp": [ - {"type":"IF","name":"","addr":"(OF)","loc":"e,51:12,51:14", + {"type":"IF","name":"","addr":"(QF)","loc":"e,51:12,51:14", "condp": [ - {"type":"LT","name":"","addr":"(PF)","loc":"e,51:20,51:21","dtypep":"(UE)", + {"type":"LT","name":"","addr":"(RF)","loc":"e,51:20,51:21","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(QF)","loc":"e,51:16,51:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(SF)","loc":"e,51:16,51:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"?32?sh5a","addr":"(RF)","loc":"e,51:22,51:24","dtypep":"(SF)"} + {"type":"CONST","name":"?32?sh5a","addr":"(TF)","loc":"e,51:22,51:24","dtypep":"(UF)"} ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(TF)","loc":"e,51:26,51:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],"stmtsp": []} + {"type":"BEGIN","name":"","addr":"(VF)","loc":"e,51:26,51:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],"stmtsp": []} ], "elsesp": [ - {"type":"IF","name":"","addr":"(UF)","loc":"e,53:12,53:14", + {"type":"IF","name":"","addr":"(WF)","loc":"e,53:12,53:14", "condp": [ - {"type":"EQ","name":"","addr":"(VF)","loc":"e,53:20,53:22","dtypep":"(UE)", + {"type":"EQ","name":"","addr":"(XF)","loc":"e,53:20,53:22","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"cyc","addr":"(WF)","loc":"e,53:16,53:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"cyc","addr":"(YF)","loc":"e,53:16,53:19","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"?32?sh63","addr":"(XF)","loc":"e,53:23,53:25","dtypep":"(SF)"} + {"type":"CONST","name":"?32?sh63","addr":"(ZF)","loc":"e,53:23,53:25","dtypep":"(UF)"} ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(YF)","loc":"e,53:27,53:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(AG)","loc":"e,53:27,53:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], "stmtsp": [ - {"type":"DISPLAY","name":"","addr":"(ZF)","loc":"e,54:10,54:16", + {"type":"DISPLAY","name":"","addr":"(BG)","loc":"e,54:10,54:16", "fmtp": [ - {"type":"SFORMATF","name":"","addr":"(AG)","loc":"e,54:10,54:16","dtypep":"(BG)", + {"type":"SFORMATF","name":"","addr":"(CG)","loc":"e,54:10,54:16","dtypep":"(DG)", "exprsp": [ - {"type":"CONST","name":"232'h5b2530745d206379633d3d253064206372633d25782073756d3d25780a","addr":"(CG)","loc":"e,54:17,54:49","dtypep":"(DG)"}, - {"type":"TIME","name":"","addr":"(EG)","loc":"e,54:51,54:56","dtypep":"(FG)","timeunit":"NONE"}, - {"type":"PARSEREF","name":"cyc","addr":"(GG)","loc":"e,54:58,54:61","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []}, - {"type":"PARSEREF","name":"crc","addr":"(HG)","loc":"e,54:63,54:66","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []}, - {"type":"PARSEREF","name":"sum","addr":"(IG)","loc":"e,54:68,54:71","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"CONST","name":"232'h5b2530745d206379633d3d253064206372633d25782073756d3d25780a","addr":"(EG)","loc":"e,54:17,54:49","dtypep":"(FG)"}, + {"type":"TIME","name":"","addr":"(GG)","loc":"e,54:51,54:56","dtypep":"(HG)","timeunit":"NONE"}, + {"type":"PARSEREF","name":"cyc","addr":"(IG)","loc":"e,54:58,54:61","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []}, + {"type":"PARSEREF","name":"crc","addr":"(JG)","loc":"e,54:63,54:66","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []}, + {"type":"PARSEREF","name":"sum","addr":"(KG)","loc":"e,54:68,54:71","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"scopeNamep": []} ],"filep": []}, - {"type":"IF","name":"","addr":"(JG)","loc":"e,55:10,55:12", + {"type":"IF","name":"","addr":"(LG)","loc":"e,55:10,55:12", "condp": [ - {"type":"NEQCASE","name":"","addr":"(KG)","loc":"e,55:18,55:21","dtypep":"(UE)", + {"type":"NEQCASE","name":"","addr":"(MG)","loc":"e,55:18,55:21","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"crc","addr":"(LG)","loc":"e,55:14,55:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"crc","addr":"(NG)","loc":"e,55:14,55:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"64'hc77bb9b3784ea091","addr":"(MG)","loc":"e,55:22,55:42","dtypep":"(AF)"} + {"type":"CONST","name":"64'hc77bb9b3784ea091","addr":"(OG)","loc":"e,55:22,55:42","dtypep":"(CF)"} ]} ], "thensp": [ - {"type":"STOP","name":"","addr":"(NG)","loc":"e,55:44,55:49","isFatal":false} + {"type":"STOP","name":"","addr":"(PG)","loc":"e,55:44,55:49","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(OG)","loc":"e,58:10,58:12", + {"type":"IF","name":"","addr":"(QG)","loc":"e,58:10,58:12", "condp": [ - {"type":"NEQCASE","name":"","addr":"(PG)","loc":"e,58:18,58:21","dtypep":"(UE)", + {"type":"NEQCASE","name":"","addr":"(RG)","loc":"e,58:18,58:21","dtypep":"(WE)", "lhsp": [ - {"type":"PARSEREF","name":"sum","addr":"(QG)","loc":"e,58:14,58:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"sum","addr":"(SG)","loc":"e,58:14,58:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"CONST","name":"64'h4afe43fb79d7b71e","addr":"(RG)","loc":"e,58:22,58:42","dtypep":"(AF)"} + {"type":"CONST","name":"64'h4afe43fb79d7b71e","addr":"(TG)","loc":"e,58:22,58:42","dtypep":"(CF)"} ]} ], "thensp": [ - {"type":"STOP","name":"","addr":"(SG)","loc":"e,58:44,58:49","isFatal":false} + {"type":"STOP","name":"","addr":"(UG)","loc":"e,58:44,58:49","isFatal":false} ],"elsesp": []}, - {"type":"DISPLAY","name":"","addr":"(TG)","loc":"e,59:10,59:16", + {"type":"DISPLAY","name":"","addr":"(VG)","loc":"e,59:10,59:16", "fmtp": [ - {"type":"SFORMATF","name":"","addr":"(UG)","loc":"e,59:10,59:16","dtypep":"(BG)", + {"type":"SFORMATF","name":"","addr":"(WG)","loc":"e,59:10,59:16","dtypep":"(DG)", "exprsp": [ - {"type":"CONST","name":"168'h2a2d2a20416c6c2046696e6973686564202a2d2a0a","addr":"(VG)","loc":"e,59:17,59:41","dtypep":"(WG)"} + {"type":"CONST","name":"168'h2a2d2a20416c6c2046696e6973686564202a2d2a0a","addr":"(XG)","loc":"e,59:17,59:41","dtypep":"(YG)"} ],"scopeNamep": []} ],"filep": []}, - {"type":"FINISH","name":"","addr":"(XG)","loc":"e,60:10,60:17"} + {"type":"FINISH","name":"","addr":"(ZG)","loc":"e,60:10,60:17"} ]} ],"elsesp": []} ]} @@ -431,125 +431,660 @@ ]} ]} ]}, - {"type":"MODULE","name":"Test","addr":"(PB)","loc":"e,66:8,66:12","isChecker":false,"isProgram":false,"origName":"Test","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"Test","addr":"(RB)","loc":"e,66:8,66:12","isChecker":false,"isProgram":false,"origName":"Test","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ - {"type":"PORT","name":"out","addr":"(YG)","loc":"e,68:4,68:7","exprp": []}, - {"type":"PORT","name":"clk","addr":"(ZG)","loc":"e,70:4,70:7","exprp": []}, - {"type":"PORT","name":"in","addr":"(AH)","loc":"e,70:9,70:11","exprp": []}, - {"type":"VAR","name":"clk","addr":"(BH)","loc":"e,78:10,78:13","dtypep":"UNLINKED","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"PORT","name":"out","addr":"(AH)","loc":"e,68:4,68:7","exprp": []}, + {"type":"PORT","name":"clk","addr":"(BH)","loc":"e,70:4,70:7","exprp": []}, + {"type":"PORT","name":"in","addr":"(CH)","loc":"e,70:9,70:11","exprp": []}, + {"type":"VAR","name":"clk","addr":"(DH)","loc":"e,78:10,78:13","dtypep":"UNLINKED","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"LOGIC_IMPLICIT","addr":"(CH)","loc":"e,78:10,78:13","dtypep":"(CH)","keyword":"LOGIC_IMPLICIT","generic":false,"rangep": []} + {"type":"BASICDTYPE","name":"LOGIC_IMPLICIT","addr":"(EH)","loc":"e,78:10,78:13","dtypep":"(EH)","keyword":"LOGIC_IMPLICIT","generic":false,"rangep": []} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"in","addr":"(DH)","loc":"e,79:17,79:19","dtypep":"UNLINKED","origName":"in","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"in","addr":"(FH)","loc":"e,79:17,79:19","dtypep":"UNLINKED","origName":"in","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(EH)","loc":"e,79:10,79:11","dtypep":"(EH)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(GH)","loc":"e,79:10,79:11","dtypep":"(GH)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(FH)","loc":"e,79:10,79:11","ascending":false, + {"type":"RANGE","name":"","addr":"(HH)","loc":"e,79:10,79:11","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(GH)","loc":"e,79:11,79:13","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(IH)","loc":"e,79:11,79:13","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(HH)","loc":"e,79:14,79:15","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(JH)","loc":"e,79:14,79:15","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"out","addr":"(IH)","loc":"e,80:22,80:25","dtypep":"UNLINKED","origName":"out","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + {"type":"VAR","name":"out","addr":"(KH)","loc":"e,80:22,80:25","dtypep":"UNLINKED","origName":"out","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"logic","addr":"(JH)","loc":"e,80:11,80:14","dtypep":"(JH)","keyword":"logic","generic":false, + {"type":"BASICDTYPE","name":"logic","addr":"(LH)","loc":"e,80:11,80:14","dtypep":"(LH)","keyword":"logic","generic":false, "rangep": [ - {"type":"RANGE","name":"","addr":"(KH)","loc":"e,80:15,80:16","ascending":false, + {"type":"RANGE","name":"","addr":"(MH)","loc":"e,80:15,80:16","ascending":false, "leftp": [ - {"type":"CONST","name":"?32?sh1f","addr":"(LH)","loc":"e,80:16,80:18","dtypep":"(BB)"} + {"type":"CONST","name":"?32?sh1f","addr":"(NH)","loc":"e,80:16,80:18","dtypep":"(DB)"} ], "rightp": [ - {"type":"CONST","name":"?32?sh0","addr":"(MH)","loc":"e,80:19,80:20","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(OH)","loc":"e,80:19,80:20","dtypep":"(N)"} ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"ALWAYS","name":"","addr":"(NH)","loc":"e,82:4,82:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(PH)","loc":"e,82:4,82:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], "stmtsp": [ - {"type":"EVENTCONTROL","name":"","addr":"(OH)","loc":"e,82:11,82:12", + {"type":"EVENTCONTROL","name":"","addr":"(QH)","loc":"e,82:11,82:12", "sensesp": [ - {"type":"SENTREE","name":"","addr":"(PH)","loc":"e,82:11,82:12","isMulti":false, + {"type":"SENTREE","name":"","addr":"(RH)","loc":"e,82:11,82:12","isMulti":false, "sensesp": [ - {"type":"SENITEM","name":"","addr":"(QH)","loc":"e,82:13,82:20","edgeType":"POS", + {"type":"SENITEM","name":"","addr":"(SH)","loc":"e,82:13,82:20","edgeType":"POS", "sensp": [ - {"type":"PARSEREF","name":"clk","addr":"(RH)","loc":"e,82:21,82:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"clk","addr":"(TH)","loc":"e,82:21,82:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"condp": []} ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(SH)","loc":"e,82:26,82:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(UH)","loc":"e,82:26,82:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], "stmtsp": [ - {"type":"ASSIGNDLY","name":"","addr":"(TH)","loc":"e,83:11,83:13","dtypep":"UNLINKED", + {"type":"ASSIGNDLY","name":"","addr":"(VH)","loc":"e,83:11,83:13","dtypep":"UNLINKED", "rhsp": [ - {"type":"PARSEREF","name":"in","addr":"(UH)","loc":"e,83:14,83:16","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"in","addr":"(WH)","loc":"e,83:14,83:16","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ], "lhsp": [ - {"type":"PARSEREF","name":"out","addr":"(VH)","loc":"e,83:7,83:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"out","addr":"(XH)","loc":"e,83:7,83:10","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": []}, - {"type":"ASSERTCTL","name":"","addr":"(WH)","loc":"e,86:7,86:17","ctlType":"$assertoff", + {"type":"ASSERTCTL","name":"","addr":"(YH)","loc":"e,86:7,86:17","ctlType":"$assertoff", "controlTypep": [ - {"type":"CONST","name":"32'h4","addr":"(XH)","loc":"e,86:7,86:17","dtypep":"(MC)"} + {"type":"CONST","name":"32'h4","addr":"(ZH)","loc":"e,86:7,86:17","dtypep":"(OC)"} ],"assertTypesp": [],"directiveTypesp": []}, - {"type":"ASSERTCTL","name":"","addr":"(YH)","loc":"e,87:7,87:18","ctlType":"$assertkill", + {"type":"ASSERTCTL","name":"","addr":"(AI)","loc":"e,87:7,87:18","ctlType":"$assertkill", "controlTypep": [ - {"type":"CONST","name":"32'h5","addr":"(ZH)","loc":"e,87:7,87:18","dtypep":"(MC)"} + {"type":"CONST","name":"32'h5","addr":"(BI)","loc":"e,87:7,87:18","dtypep":"(OC)"} ],"assertTypesp": [],"directiveTypesp": []}, - {"type":"ASSERT","name":"","addr":"(AI)","loc":"e,88:7,88:13","type":"[SIMPLE_IMMEDIATE]", + {"type":"ASSERT","name":"","addr":"(CI)","loc":"e,88:7,88:13","type":"[SIMPLE_IMMEDIATE]", "propp": [ - {"type":"CONST","name":"?32?sh0","addr":"(BI)","loc":"e,88:14,88:15","dtypep":"(L)"} + {"type":"CONST","name":"?32?sh0","addr":"(DI)","loc":"e,88:14,88:15","dtypep":"(N)"} ],"sentreep": [],"failsp": [],"passsp": []}, - {"type":"ASSERTCTL","name":"","addr":"(CI)","loc":"e,89:7,89:16","ctlType":"$asserton", + {"type":"ASSERTCTL","name":"","addr":"(EI)","loc":"e,89:7,89:16","ctlType":"$asserton", "controlTypep": [ - {"type":"CONST","name":"32'h3","addr":"(DI)","loc":"e,89:7,89:16","dtypep":"(MC)"} + {"type":"CONST","name":"32'h3","addr":"(FI)","loc":"e,89:7,89:16","dtypep":"(OC)"} ],"assertTypesp": [],"directiveTypesp": []}, - {"type":"ASSERTCTL","name":"","addr":"(EI)","loc":"e,90:7,90:21","ctlType":"", + {"type":"ASSERTCTL","name":"","addr":"(GI)","loc":"e,90:7,90:21","ctlType":"", "controlTypep": [ - {"type":"CONST","name":"?32?sh3","addr":"(FI)","loc":"e,90:22,90:23","dtypep":"(QD)"} + {"type":"CONST","name":"?32?sh3","addr":"(HI)","loc":"e,90:22,90:23","dtypep":"(SD)"} ], "assertTypesp": [ - {"type":"CONST","name":"?32?sh8","addr":"(GI)","loc":"e,90:25,90:26","dtypep":"(JF)"} + {"type":"CONST","name":"?32?sh8","addr":"(II)","loc":"e,90:25,90:26","dtypep":"(LF)"} ],"directiveTypesp": []}, - {"type":"BEGIN","name":"blk","addr":"(HI)","loc":"e,91:15,91:18","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], + {"type":"BEGIN","name":"blk","addr":"(JI)","loc":"e,91:15,91:18","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], "stmtsp": [ - {"type":"DISABLE","name":"","addr":"(II)","loc":"e,92:10,92:17", + {"type":"DISABLE","name":"","addr":"(KI)","loc":"e,92:10,92:17", "targetRefp": [ - {"type":"PARSEREF","name":"blk","addr":"(JI)","loc":"e,92:18,92:21","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"blk","addr":"(LI)","loc":"e,92:18,92:21","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ]} ]} ]} ]} ]} + ]}, + {"type":"PACKAGE","name":"$unit","addr":"(E)","loc":"a,0:0,0:0","origName":"__024unit","level":3,"modPublic":false,"inLibrary":true,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + "stmtsp": [ + {"type":"PACKAGEIMPORT","name":"*","addr":"(MI)","loc":"d,31:9,31:12","packagep":"(F)"} + ]}, + {"type":"PACKAGE","name":"std","addr":"(F)","loc":"d,31:9,31:12","origName":"std","level":4,"modPublic":false,"inLibrary":true,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + "stmtsp": [ + {"type":"CLASS","name":"mailbox","addr":"(NI)","loc":"d,32:4,32:9","isExtended":false,"isInterfaceClass":false,"isVirtual":false,"origName":"mailbox","level":5,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","classOrPackagep":"UNLINKED","inlinesp": [], + "stmtsp": [ + {"type":"VAR","name":"T","addr":"(OI)","loc":"d,32:25,32:26","dtypep":"UNLINKED","origName":"T","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"GPARAM","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":true,"isParam":true,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"PARSETYPEDTYPE","name":"","addr":"(PI)","loc":"d,32:20,32:24","dtypep":"UNLINKED","generic":false} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"TYPEDEFFWD","name":"T","addr":"(QI)","loc":"d,32:25,32:26"}, + {"type":"VAR","name":"m_bound","addr":"(RI)","loc":"d,33:21,33:28","dtypep":"UNLINKED","origName":"m_bound","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(SI)","loc":"d,33:17,33:20","dtypep":"(SI)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"m_queue","addr":"(TI)","loc":"d,34:19,34:26","dtypep":"UNLINKED","origName":"m_queue","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BRACKETARRAYDTYPE","name":"","addr":"(UI)","loc":"d,34:26,34:27","dtypep":"UNLINKED","generic":false, + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(VI)","loc":"d,34:17,34:18","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ], + "elementsp": [ + {"type":"UNBOUNDED","name":"","addr":"(WI)","loc":"d,34:27,34:28","dtypep":"(XI)"} + ]} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"FUNC","name":"new","addr":"(YI)","loc":"d,36:16,36:19","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"new","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"bound","addr":"(ZI)","loc":"d,36:24,36:29","dtypep":"UNLINKED","origName":"bound","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(AJ)","loc":"d,36:20,36:23","dtypep":"(AJ)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [], + "valuep": [ + {"type":"CONST","name":"?32?sh0","addr":"(BJ)","loc":"d,36:32,36:33","dtypep":"(N)"} + ],"attrsp": []}, + {"type":"ASSIGN","name":"","addr":"(CJ)","loc":"d,37:18,37:19","dtypep":"UNLINKED", + "rhsp": [ + {"type":"PARSEREF","name":"bound","addr":"(DJ)","loc":"d,37:20,37:25","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "lhsp": [ + {"type":"PARSEREF","name":"m_bound","addr":"(EJ)","loc":"d,37:10,37:17","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"num","addr":"(FJ)","loc":"d,40:20,40:23","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"num", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(GJ)","loc":"d,40:16,40:19","dtypep":"(GJ)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"RETURN","name":"","addr":"(HJ)","loc":"d,41:10,41:16", + "lhsp": [ + {"type":"DOT","name":"","addr":"(IJ)","loc":"d,41:24,41:25","dtypep":"UNLINKED","colon":false, + "lhsp": [ + {"type":"PARSEREF","name":"m_queue","addr":"(JJ)","loc":"d,41:17,41:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"FUNCREF","name":"size","addr":"(KJ)","loc":"d,41:25,41:29","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ]} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"put","addr":"(LJ)","loc":"d,44:12,44:15","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"put","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(MJ)","loc":"d,44:18,44:25","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(NJ)","loc":"d,44:16,44:17","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"try_put","addr":"(OJ)","loc":"d,52:20,52:27","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"try_put", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(PJ)","loc":"d,52:16,52:19","dtypep":"(PJ)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(QJ)","loc":"d,52:30,52:37","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(RJ)","loc":"d,52:28,52:29","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"IF","name":"","addr":"(SJ)","loc":"d,53:10,53:12", + "condp": [ + {"type":"LOGOR","name":"","addr":"(TJ)","loc":"d,53:27,53:29","dtypep":"(WE)", + "lhsp": [ + {"type":"EQ","name":"","addr":"(UJ)","loc":"d,53:22,53:24","dtypep":"(WE)", + "lhsp": [ + {"type":"PARSEREF","name":"m_bound","addr":"(VJ)","loc":"d,53:14,53:21","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(WJ)","loc":"d,53:25,53:26","dtypep":"(N)"} + ]} + ], + "rhsp": [ + {"type":"LT","name":"","addr":"(XJ)","loc":"d,53:36,53:37","dtypep":"(WE)", + "lhsp": [ + {"type":"FUNCREF","name":"num","addr":"(YJ)","loc":"d,53:30,53:33","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ], + "rhsp": [ + {"type":"PARSEREF","name":"m_bound","addr":"(ZJ)","loc":"d,53:38,53:45","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ]} + ], + "thensp": [ + {"type":"BEGIN","name":"","addr":"(AK)","loc":"d,53:47,53:52","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + "stmtsp": [ + {"type":"STMTEXPR","name":"","addr":"(BK)","loc":"d,54:20,54:21", + "exprp": [ + {"type":"DOT","name":"","addr":"(CK)","loc":"d,54:20,54:21","dtypep":"UNLINKED","colon":false, + "lhsp": [ + {"type":"PARSEREF","name":"m_queue","addr":"(DK)","loc":"d,54:13,54:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"TASKREF","name":"push_back","addr":"(EK)","loc":"d,54:21,54:30","dtypep":"(FK)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [], + "pinsp": [ + {"type":"ARG","name":"","addr":"(GK)","loc":"d,54:31,54:38", + "exprp": [ + {"type":"PARSEREF","name":"message","addr":"(HK)","loc":"d,54:31,54:38","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []} + ]} + ]}, + {"type":"RETURN","name":"","addr":"(IK)","loc":"d,55:13,55:19", + "lhsp": [ + {"type":"CONST","name":"?32?sh1","addr":"(JK)","loc":"d,55:20,55:21","dtypep":"(N)"} + ]} + ]} + ],"elsesp": []}, + {"type":"RETURN","name":"","addr":"(KK)","loc":"d,57:10,57:16", + "lhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(LK)","loc":"d,57:17,57:18","dtypep":"(N)"} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"get","addr":"(MK)","loc":"d,60:12,60:15","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"get","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(NK)","loc":"d,60:22,60:29","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(OK)","loc":"d,60:20,60:21","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"try_get","addr":"(PK)","loc":"d,69:20,69:27","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"try_get", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(QK)","loc":"d,69:16,69:19","dtypep":"(QK)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(RK)","loc":"d,69:34,69:41","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(SK)","loc":"d,69:32,69:33","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"IF","name":"","addr":"(TK)","loc":"d,70:10,70:12", + "condp": [ + {"type":"GT","name":"","addr":"(UK)","loc":"d,70:20,70:21","dtypep":"(WE)", + "lhsp": [ + {"type":"FUNCREF","name":"num","addr":"(VK)","loc":"d,70:14,70:17","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ], + "rhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(WK)","loc":"d,70:22,70:23","dtypep":"(N)"} + ]} + ], + "thensp": [ + {"type":"BEGIN","name":"","addr":"(XK)","loc":"d,70:25,70:30","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + "stmtsp": [ + {"type":"ASSIGN","name":"","addr":"(YK)","loc":"d,71:21,71:22","dtypep":"UNLINKED", + "rhsp": [ + {"type":"DOT","name":"","addr":"(ZK)","loc":"d,71:30,71:31","dtypep":"UNLINKED","colon":false, + "lhsp": [ + {"type":"PARSEREF","name":"m_queue","addr":"(AL)","loc":"d,71:23,71:30","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"FUNCREF","name":"pop_front","addr":"(BL)","loc":"d,71:31,71:40","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ]} + ], + "lhsp": [ + {"type":"PARSEREF","name":"message","addr":"(CL)","loc":"d,71:13,71:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []}, + {"type":"RETURN","name":"","addr":"(DL)","loc":"d,72:13,72:19", + "lhsp": [ + {"type":"CONST","name":"?32?sh1","addr":"(EL)","loc":"d,72:20,72:21","dtypep":"(N)"} + ]} + ]} + ],"elsesp": []}, + {"type":"RETURN","name":"","addr":"(FL)","loc":"d,74:10,74:16", + "lhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(GL)","loc":"d,74:17,74:18","dtypep":"(N)"} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"peek","addr":"(HL)","loc":"d,77:12,77:16","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"peek","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(IL)","loc":"d,77:23,77:30","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(JL)","loc":"d,77:21,77:22","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"try_peek","addr":"(KL)","loc":"d,86:20,86:28","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"try_peek", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(LL)","loc":"d,86:16,86:19","dtypep":"(LL)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"message","addr":"(ML)","loc":"d,86:35,86:42","dtypep":"UNLINKED","origName":"message","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"T","addr":"(NL)","loc":"d,86:33,86:34","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"IF","name":"","addr":"(OL)","loc":"d,87:10,87:12", + "condp": [ + {"type":"GT","name":"","addr":"(PL)","loc":"d,87:20,87:21","dtypep":"(WE)", + "lhsp": [ + {"type":"FUNCREF","name":"num","addr":"(QL)","loc":"d,87:14,87:17","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ], + "rhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(RL)","loc":"d,87:22,87:23","dtypep":"(N)"} + ]} + ], + "thensp": [ + {"type":"BEGIN","name":"","addr":"(SL)","loc":"d,87:25,87:30","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + "stmtsp": [ + {"type":"ASSIGN","name":"","addr":"(TL)","loc":"d,88:21,88:22","dtypep":"UNLINKED", + "rhsp": [ + {"type":"SELBIT","name":"","addr":"(UL)","loc":"d,88:30,88:31","dtypep":"UNLINKED", + "fromp": [ + {"type":"PARSEREF","name":"m_queue","addr":"(VL)","loc":"d,88:23,88:30","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "bitp": [ + {"type":"CONST","name":"?32?sh0","addr":"(WL)","loc":"d,88:31,88:32","dtypep":"(N)"} + ],"thsp": [],"attrp": []} + ], + "lhsp": [ + {"type":"PARSEREF","name":"message","addr":"(XL)","loc":"d,88:13,88:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []}, + {"type":"RETURN","name":"","addr":"(YL)","loc":"d,89:13,89:19", + "lhsp": [ + {"type":"CONST","name":"?32?sh1","addr":"(ZL)","loc":"d,89:20,89:21","dtypep":"(N)"} + ]} + ]} + ],"elsesp": []}, + {"type":"RETURN","name":"","addr":"(AM)","loc":"d,91:10,91:16", + "lhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(BM)","loc":"d,91:17,91:18","dtypep":"(N)"} + ]} + ],"scopeNamep": []} + ],"extendsp": []}, + {"type":"CLASS","name":"semaphore","addr":"(CM)","loc":"d,95:4,95:9","isExtended":false,"isInterfaceClass":false,"isVirtual":false,"origName":"semaphore","level":5,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","classOrPackagep":"UNLINKED","inlinesp": [], + "stmtsp": [ + {"type":"VAR","name":"m_keyCount","addr":"(DM)","loc":"d,96:21,96:31","dtypep":"UNLINKED","origName":"m_keyCount","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(EM)","loc":"d,96:17,96:20","dtypep":"(EM)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"FUNC","name":"new","addr":"(FM)","loc":"d,98:16,98:19","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"new","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"keyCount","addr":"(GM)","loc":"d,98:24,98:32","dtypep":"UNLINKED","origName":"keyCount","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(HM)","loc":"d,98:20,98:23","dtypep":"(HM)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [], + "valuep": [ + {"type":"CONST","name":"?32?sh0","addr":"(IM)","loc":"d,98:35,98:36","dtypep":"(N)"} + ],"attrsp": []}, + {"type":"ASSIGN","name":"","addr":"(JM)","loc":"d,99:21,99:22","dtypep":"UNLINKED", + "rhsp": [ + {"type":"PARSEREF","name":"keyCount","addr":"(KM)","loc":"d,99:23,99:31","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(LM)","loc":"d,99:10,99:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []} + ],"scopeNamep": []}, + {"type":"TASK","name":"put","addr":"(MM)","loc":"d,102:21,102:24","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"put","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"keyCount","addr":"(NM)","loc":"d,102:29,102:37","dtypep":"UNLINKED","origName":"keyCount","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(OM)","loc":"d,102:25,102:28","dtypep":"(OM)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [], + "valuep": [ + {"type":"CONST","name":"?32?sh1","addr":"(PM)","loc":"d,102:40,102:41","dtypep":"(N)"} + ],"attrsp": []}, + {"type":"ASSIGN","name":"","addr":"(QM)","loc":"d,103:21,103:23","dtypep":"UNLINKED", + "rhsp": [ + {"type":"ADD","name":"","addr":"(RM)","loc":"d,103:21,103:23","dtypep":"UNLINKED", + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(SM)","loc":"d,103:10,103:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"PARSEREF","name":"keyCount","addr":"(TM)","loc":"d,103:24,103:32","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ], + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(UM)","loc":"d,103:10,103:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []} + ],"scopeNamep": []}, + {"type":"TASK","name":"get","addr":"(VM)","loc":"d,106:12,106:15","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"get","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"keyCount","addr":"(WM)","loc":"d,106:20,106:28","dtypep":"UNLINKED","origName":"keyCount","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(XM)","loc":"d,106:16,106:19","dtypep":"(XM)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [], + "valuep": [ + {"type":"CONST","name":"?32?sh1","addr":"(YM)","loc":"d,106:31,106:32","dtypep":"(N)"} + ],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"try_get","addr":"(ZM)","loc":"d,115:20,115:27","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"try_get", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(AN)","loc":"d,115:16,115:19","dtypep":"(AN)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"keyCount","addr":"(BN)","loc":"d,115:32,115:40","dtypep":"UNLINKED","origName":"keyCount","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(CN)","loc":"d,115:28,115:31","dtypep":"(CN)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"delayp": [], + "valuep": [ + {"type":"CONST","name":"?32?sh1","addr":"(DN)","loc":"d,115:43,115:44","dtypep":"(N)"} + ],"attrsp": []}, + {"type":"IF","name":"","addr":"(EN)","loc":"d,116:10,116:12", + "condp": [ + {"type":"GTE","name":"","addr":"(FN)","loc":"d,116:25,116:27","dtypep":"(WE)", + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(GN)","loc":"d,116:14,116:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"PARSEREF","name":"keyCount","addr":"(HN)","loc":"d,116:28,116:36","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ], + "thensp": [ + {"type":"BEGIN","name":"","addr":"(IN)","loc":"d,116:38,116:43","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + "stmtsp": [ + {"type":"ASSIGN","name":"","addr":"(JN)","loc":"d,117:24,117:26","dtypep":"UNLINKED", + "rhsp": [ + {"type":"SUB","name":"","addr":"(KN)","loc":"d,117:24,117:26","dtypep":"UNLINKED", + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(LN)","loc":"d,117:13,117:23","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"PARSEREF","name":"keyCount","addr":"(MN)","loc":"d,117:27,117:35","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ], + "lhsp": [ + {"type":"PARSEREF","name":"m_keyCount","addr":"(NN)","loc":"d,117:13,117:23","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []}, + {"type":"RETURN","name":"","addr":"(ON)","loc":"d,118:13,118:19", + "lhsp": [ + {"type":"CONST","name":"?32?sh1","addr":"(PN)","loc":"d,118:20,118:21","dtypep":"(N)"} + ]} + ]} + ],"elsesp": []}, + {"type":"RETURN","name":"","addr":"(QN)","loc":"d,120:10,120:16", + "lhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(RN)","loc":"d,120:17,120:18","dtypep":"(N)"} + ]} + ],"scopeNamep": []} + ],"extendsp": []}, + {"type":"CLASS","name":"process","addr":"(SN)","loc":"d,124:4,124:9","isExtended":false,"isInterfaceClass":false,"isVirtual":false,"origName":"process","level":5,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","classOrPackagep":"UNLINKED","inlinesp": [], + "stmtsp": [ + {"type":"TYPEDEF","name":"state","addr":"(TN)","loc":"d,131:9,131:14","dtypep":"UNLINKED","attrPublic":false, + "childDTypep": [ + {"type":"DEFIMPLICITDTYPE","name":"__typeimpenum0","addr":"(UN)","loc":"d,125:15,125:19","dtypep":"UNLINKED","generic":false, + "childDTypep": [ + {"type":"ENUMDTYPE","name":"","addr":"(VN)","loc":"d,125:15,125:19","dtypep":"UNLINKED","enum":true,"generic":false,"refDTypep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"int","addr":"(WN)","loc":"d,125:20,125:21","dtypep":"(WN)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ], + "itemsp": [ + {"type":"ENUMITEM","name":"FINISHED","addr":"(XN)","loc":"d,126:10,126:18","dtypep":"UNLINKED","rangep": [], + "valuep": [ + {"type":"CONST","name":"?32?sh0","addr":"(YN)","loc":"d,126:22,126:23","dtypep":"(N)"} + ]}, + {"type":"ENUMITEM","name":"RUNNING","addr":"(ZN)","loc":"d,127:10,127:17","dtypep":"UNLINKED","rangep": [], + "valuep": [ + {"type":"CONST","name":"?32?sh1","addr":"(AO)","loc":"d,127:22,127:23","dtypep":"(N)"} + ]}, + {"type":"ENUMITEM","name":"WAITING","addr":"(BO)","loc":"d,128:10,128:17","dtypep":"UNLINKED","rangep": [], + "valuep": [ + {"type":"CONST","name":"?32?sh2","addr":"(CO)","loc":"d,128:22,128:23","dtypep":"(SD)"} + ]}, + {"type":"ENUMITEM","name":"SUSPENDED","addr":"(DO)","loc":"d,129:10,129:19","dtypep":"UNLINKED","rangep": [], + "valuep": [ + {"type":"CONST","name":"?32?sh3","addr":"(EO)","loc":"d,129:22,129:23","dtypep":"(SD)"} + ]}, + {"type":"ENUMITEM","name":"KILLED","addr":"(FO)","loc":"d,130:10,130:16","dtypep":"UNLINKED","rangep": [], + "valuep": [ + {"type":"CONST","name":"?32?sh4","addr":"(GO)","loc":"d,130:22,130:23","dtypep":"(HO)"} + ]} + ]} + ]} + ],"attrsp": []}, + {"type":"FUNC","name":"self","addr":"(IO)","loc":"d,138:31,138:35","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"self", + "fvarp": [ + {"type":"REFDTYPE","name":"process","addr":"(JO)","loc":"d,138:23,138:30","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"p","addr":"(KO)","loc":"d,139:18,139:19","dtypep":"UNLINKED","origName":"p","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"process","addr":"(LO)","loc":"d,139:10,139:17","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [], + "valuep": [ + {"type":"NEW","name":"new","addr":"(MO)","loc":"d,139:22,139:25","dtypep":"UNLINKED","isImplicit":false,"isScoped":false,"dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ],"attrsp": []}, + {"type":"RETURN","name":"","addr":"(NO)","loc":"d,143:10,143:16", + "lhsp": [ + {"type":"PARSEREF","name":"p","addr":"(OO)","loc":"d,143:17,143:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"set_status","addr":"(PO)","loc":"d,146:31,146:41","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"set_status","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"s","addr":"(QO)","loc":"d,146:48,146:49","dtypep":"UNLINKED","origName":"s","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"REFDTYPE","name":"state","addr":"(RO)","loc":"d,146:42,146:47","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"delayp": [],"valuep": [],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"status","addr":"(SO)","loc":"d,152:22,152:28","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"status", + "fvarp": [ + {"type":"REFDTYPE","name":"state","addr":"(TO)","loc":"d,152:16,152:21","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"RETURN","name":"","addr":"(UO)","loc":"d,156:10,156:16", + "lhsp": [ + {"type":"PARSEREF","name":"RUNNING","addr":"(VO)","loc":"d,156:17,156:24","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"kill","addr":"(WO)","loc":"d,160:21,160:25","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"kill","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"STMTEXPR","name":"","addr":"(XO)","loc":"d,161:10,161:20", + "exprp": [ + {"type":"TASKREF","name":"set_status","addr":"(YO)","loc":"d,161:10,161:20","dtypep":"(FK)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [], + "pinsp": [ + {"type":"ARG","name":"","addr":"(ZO)","loc":"d,161:21,161:27", + "exprp": [ + {"type":"PARSEREF","name":"KILLED","addr":"(AP)","loc":"d,161:21,161:27","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"suspend","addr":"(BP)","loc":"d,164:21,164:28","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"suspend","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"DISPLAY","name":"","addr":"(CP)","loc":"d,165:10,165:16", + "fmtp": [ + {"type":"SFORMATF","name":"","addr":"(DP)","loc":"d,165:10,165:16","dtypep":"(DG)", + "exprsp": [ + {"type":"CONST","name":"296'h7374643a3a70726f636573733a3a73757370656e642829206e6f7420737570706f72746564","addr":"(EP)","loc":"d,165:17,165:56","dtypep":"(FP)"} + ],"scopeNamep": []} + ],"filep": []}, + {"type":"STOP","name":"","addr":"(GP)","loc":"d,165:10,165:16","isFatal":false} + ],"scopeNamep": []}, + {"type":"TASK","name":"resume","addr":"(HP)","loc":"d,168:21,168:27","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"resume","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"STMTEXPR","name":"","addr":"(IP)","loc":"d,169:10,169:20", + "exprp": [ + {"type":"TASKREF","name":"set_status","addr":"(JP)","loc":"d,169:10,169:20","dtypep":"(FK)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [], + "pinsp": [ + {"type":"ARG","name":"","addr":"(KP)","loc":"d,169:21,169:28", + "exprp": [ + {"type":"PARSEREF","name":"RUNNING","addr":"(LP)","loc":"d,169:21,169:28","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"await","addr":"(MP)","loc":"d,172:12,172:17","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"await","fvarp": [],"classOrPackagep": [],"stmtsp": [],"scopeNamep": []}, + {"type":"TASK","name":"killQueue","addr":"(NP)","loc":"d,178:19,178:28","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"killQueue","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"processQueue","addr":"(OP)","loc":"d,178:41,178:53","dtypep":"UNLINKED","origName":"processQueue","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BRACKETARRAYDTYPE","name":"","addr":"(PP)","loc":"d,178:53,178:54","dtypep":"UNLINKED","generic":false, + "childDTypep": [ + {"type":"REFDTYPE","name":"process","addr":"(QP)","loc":"d,178:33,178:40","dtypep":"UNLINKED","generic":false,"typedefp":"UNLINKED","refDTypep":"UNLINKED","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []} + ], + "elementsp": [ + {"type":"UNBOUNDED","name":"","addr":"(RP)","loc":"d,178:54,178:55","dtypep":"(XI)"} + ]} + ],"delayp": [],"valuep": [],"attrsp": []} + ],"scopeNamep": []}, + {"type":"FUNC","name":"get_randstate","addr":"(SP)","loc":"d,223:23,223:36","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"get_randstate", + "fvarp": [ + {"type":"BASICDTYPE","name":"string","addr":"(TP)","loc":"d,223:16,223:22","dtypep":"(TP)","keyword":"string","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"s","addr":"(UP)","loc":"d,224:17,224:18","dtypep":"UNLINKED","origName":"s","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"string","addr":"(VP)","loc":"d,224:10,224:16","dtypep":"(VP)","keyword":"string","generic":false,"rangep": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"STMTEXPR","name":"","addr":"(WP)","loc":"d,226:11,226:12", + "exprp": [ + {"type":"DOT","name":"","addr":"(XP)","loc":"d,226:11,226:12","dtypep":"UNLINKED","colon":false, + "lhsp": [ + {"type":"PARSEREF","name":"s","addr":"(YP)","loc":"d,226:10,226:11","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"TASKREF","name":"itoa","addr":"(ZP)","loc":"d,226:12,226:16","dtypep":"(FK)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [], + "pinsp": [ + {"type":"ARG","name":"","addr":"(AQ)","loc":"d,226:17,226:24", + "exprp": [ + {"type":"RAND","name":"","addr":"(BQ)","loc":"d,226:17,226:24","dtypep":"UNLINKED","seedp": []} + ]} + ],"scopeNamep": []} + ]} + ]}, + {"type":"STMTEXPR","name":"","addr":"(CQ)","loc":"d,227:10,227:23", + "exprp": [ + {"type":"TASKREF","name":"set_randstate","addr":"(DQ)","loc":"d,227:10,227:23","dtypep":"(FK)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [], + "pinsp": [ + {"type":"ARG","name":"","addr":"(EQ)","loc":"d,227:24,227:25", + "exprp": [ + {"type":"PARSEREF","name":"s","addr":"(FQ)","loc":"d,227:24,227:25","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []} + ]}, + {"type":"RETURN","name":"","addr":"(GQ)","loc":"d,228:10,228:16", + "lhsp": [ + {"type":"PARSEREF","name":"s","addr":"(HQ)","loc":"d,228:17,228:18","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ]} + ],"scopeNamep": []}, + {"type":"TASK","name":"set_randstate","addr":"(IQ)","loc":"d,231:21,231:34","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"set_randstate","fvarp": [],"classOrPackagep": [], + "stmtsp": [ + {"type":"VAR","name":"s","addr":"(JQ)","loc":"d,231:42,231:43","dtypep":"UNLINKED","origName":"s","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", + "childDTypep": [ + {"type":"BASICDTYPE","name":"string","addr":"(KQ)","loc":"d,231:35,231:41","dtypep":"(KQ)","keyword":"string","generic":false,"rangep": []} + ],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"SYSFUNCASTASK","name":"","addr":"(LQ)","loc":"d,232:10,232:18", + "lhsp": [ + {"type":"RAND","name":"","addr":"(MQ)","loc":"d,232:10,232:18","dtypep":"UNLINKED", + "seedp": [ + {"type":"DOT","name":"","addr":"(NQ)","loc":"d,232:20,232:21","dtypep":"UNLINKED","colon":false, + "lhsp": [ + {"type":"PARSEREF","name":"s","addr":"(OQ)","loc":"d,232:19,232:20","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"FUNCREF","name":"atoi","addr":"(PQ)","loc":"d,232:21,232:25","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","namep": [],"pinsp": [],"scopeNamep": []} + ]} + ]} + ]} + ],"scopeNamep": []} + ],"extendsp": []}, + {"type":"FUNC","name":"randomize","addr":"(QQ)","loc":"d,235:17,235:26","dtypep":"UNLINKED","method":false,"dpiExport":false,"dpiImport":false,"dpiOpenChild":false,"dpiOpenParent":false,"isExternDef":false,"isExternProto":false,"prototype":false,"recursive":false,"taskPublic":false,"cname":"randomize", + "fvarp": [ + {"type":"BASICDTYPE","name":"int","addr":"(RQ)","loc":"d,235:13,235:16","dtypep":"(RQ)","keyword":"int","range":"31:0","generic":false,"rangep": []} + ],"classOrPackagep": [], + "stmtsp": [ + {"type":"ASSIGN","name":"","addr":"(SQ)","loc":"d,236:17,236:18","dtypep":"UNLINKED", + "rhsp": [ + {"type":"CONST","name":"?32?sh0","addr":"(TQ)","loc":"d,236:19,236:20","dtypep":"(N)"} + ], + "lhsp": [ + {"type":"PARSEREF","name":"randomize","addr":"(UQ)","loc":"d,236:7,236:16","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} + ],"timingControlp": []} + ],"scopeNamep": []} ]} ],"filesp": [], "miscsp": [ - {"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(KI)", + {"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(FK)", "typesp": [ - {"type":"BASICDTYPE","name":"integer","addr":"(LI)","loc":"d,34:27,34:28","dtypep":"(LI)","keyword":"integer","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(L)","loc":"d,36:32,36:33","dtypep":"(L)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(UE)","loc":"d,53:22,53:24","dtypep":"(UE)","keyword":"logic","generic":true,"rangep": []}, - {"type":"VOIDDTYPE","name":"","addr":"(KI)","loc":"d,54:21,54:30","dtypep":"(KI)","generic":false}, - {"type":"BASICDTYPE","name":"logic","addr":"(QD)","loc":"d,128:22,128:23","dtypep":"(QD)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(MI)","loc":"d,130:22,130:23","dtypep":"(MI)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(NI)","loc":"d,165:17,165:56","dtypep":"(NI)","keyword":"logic","range":"295:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"string","addr":"(BG)","loc":"d,165:10,165:16","dtypep":"(BG)","keyword":"string","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(Q)","loc":"e,14:9,14:11","dtypep":"(Q)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(BB)","loc":"e,18:10,18:12","dtypep":"(BB)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(MC)","loc":"e,33:26,33:31","dtypep":"(MC)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(JC)","loc":"e,33:25,33:26","dtypep":"(JC)","keyword":"logic","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(AF)","loc":"e,45:17,45:38","dtypep":"(AF)","keyword":"logic","range":"63:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(JF)","loc":"e,48:22,48:24","dtypep":"(JF)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(SF)","loc":"e,51:22,51:24","dtypep":"(SF)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(DG)","loc":"e,54:17,54:49","dtypep":"(DG)","keyword":"logic","range":"231:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"QData","addr":"(FG)","loc":"e,54:51,54:56","dtypep":"(FG)","keyword":"QData","range":"63:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(WG)","loc":"e,59:17,59:41","dtypep":"(WG)","keyword":"logic","range":"167:0","generic":true,"rangep": []} + {"type":"BASICDTYPE","name":"integer","addr":"(XI)","loc":"d,34:27,34:28","dtypep":"(XI)","keyword":"integer","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(N)","loc":"d,36:32,36:33","dtypep":"(N)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(WE)","loc":"d,53:22,53:24","dtypep":"(WE)","keyword":"logic","generic":true,"rangep": []}, + {"type":"VOIDDTYPE","name":"","addr":"(FK)","loc":"d,54:21,54:30","dtypep":"(FK)","generic":false}, + {"type":"BASICDTYPE","name":"logic","addr":"(SD)","loc":"d,128:22,128:23","dtypep":"(SD)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(HO)","loc":"d,130:22,130:23","dtypep":"(HO)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(FP)","loc":"d,165:17,165:56","dtypep":"(FP)","keyword":"logic","range":"295:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"string","addr":"(DG)","loc":"d,165:10,165:16","dtypep":"(DG)","keyword":"string","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(S)","loc":"e,14:9,14:11","dtypep":"(S)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(DB)","loc":"e,18:10,18:12","dtypep":"(DB)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(OC)","loc":"e,33:26,33:31","dtypep":"(OC)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(LC)","loc":"e,33:25,33:26","dtypep":"(LC)","keyword":"logic","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(CF)","loc":"e,45:17,45:38","dtypep":"(CF)","keyword":"logic","range":"63:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(LF)","loc":"e,48:22,48:24","dtypep":"(LF)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(UF)","loc":"e,51:22,51:24","dtypep":"(UF)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(FG)","loc":"e,54:17,54:49","dtypep":"(FG)","keyword":"logic","range":"231:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"QData","addr":"(HG)","loc":"e,54:51,54:56","dtypep":"(HG)","keyword":"QData","range":"63:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(YG)","loc":"e,59:17,59:41","dtypep":"(YG)","keyword":"logic","range":"167:0","generic":true,"rangep": []} ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(OI)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(VQ)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ - {"type":"SCOPE","name":"@CONST-POOL@","addr":"(PI)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(OI)","varsp": [],"blocksp": [],"inlinesp": []} + {"type":"SCOPE","name":"@CONST-POOL@","addr":"(WQ)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(VQ)","varsp": [],"blocksp": [],"inlinesp": []} ]} ]} ]} From 03e0d49d9926c5cb54d9c7f2a21dac9413cfb19f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 14 Jul 2025 22:09:34 +0100 Subject: [PATCH 022/252] Optimize DFG partial assignments (#6176) This is mostly a refactoring, but also enables handling some more UNOPTFLAT, when the variable is only partially assigned in the cycle. Previously the way partial assignments to variables were handled were through the DfgVerexVar types themselves, which kept track of all drivers. This has been replaced by DfgVertexSplice (which always drives a DfgVeretexVar), and all DfgVertexVar now only have a single source, either a DfgVertexSplice, if partially assigned, or an arbitrary DfgVertex when updated as a whole. --- src/V3Dfg.cpp | 195 ++++++++++++++++++++-------- src/V3Dfg.h | 16 +-- src/V3DfgAstToDfg.cpp | 118 +++++++++++------ src/V3DfgBreakCycles.cpp | 40 ++++-- src/V3DfgDecomposition.cpp | 89 ++----------- src/V3DfgDfgToAst.cpp | 59 +++++---- src/V3DfgPasses.cpp | 14 +- src/V3DfgPeephole.cpp | 14 +- src/V3DfgRegularize.cpp | 26 ++-- src/V3DfgVertices.h | 131 ++++++++++--------- test_regress/t/t_dfg_break_cycles.v | 4 + 11 files changed, 399 insertions(+), 307 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 16ccb332a..613b29e7d 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -97,6 +97,16 @@ std::unique_ptr DfgGraph::clone() const { vtxp2clonep.emplace(&vtx, cp); break; } + case VDfgType::atSpliceArray: { + DfgSpliceArray* const cp = new DfgSpliceArray{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } + case VDfgType::atSplicePacked: { + DfgSplicePacked* const cp = new DfgSplicePacked{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } default: { vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); VL_UNREACHABLE; @@ -109,49 +119,55 @@ std::unique_ptr DfgGraph::clone() const { // Constants have no inputs // Hook up inputs of cloned variables for (const DfgVertexVar& vtx : m_varVertices) { - switch (vtx.type()) { - case VDfgType::atVarArray: { - const DfgVarArray* const vp = vtx.as(); - DfgVarArray* const cp = vtxp2clonep.at(vp)->as(); - vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { - cp->addDriver(vp->driverFileLine(i), // - vp->driverIndex(i), // - vtxp2clonep.at(srcp)); - } - }); - break; - } - case VDfgType::atVarPacked: { - const DfgVarPacked* const vp = vtx.as(); - DfgVarPacked* const cp = vtxp2clonep.at(vp)->as(); - vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { - cp->addDriver(vp->driverFileLine(i), // - vp->driverLsb(i), // - vtxp2clonep.at(srcp)); - } - }); - break; - } - default: { - vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName()); - VL_UNREACHABLE; - break; - } + // All variable vertices are unary + if (DfgVertex* const srcp = vtx.srcp()) { + vtxp2clonep.at(&vtx)->as()->srcp(vtxp2clonep.at(srcp)); } } // Hook up inputs of cloned operation vertices for (const DfgVertex& vtx : m_opVertices) { - DfgVertex* const cp = vtxp2clonep.at(&vtx); - // The code below doesn't work for DfgVertexVariadic, but none of the opVertices are such. - UASSERT_OBJ(!vtx.is(), &vtx, "DfgVertexVariadic not handled"); - const auto oSourceEdges = vtx.sourceEdges(); - auto cSourceEdges = cp->sourceEdges(); - UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, "Mismatched source count"); - for (size_t i = 0; i < oSourceEdges.second; ++i) { - if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { - cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); + if (vtx.is()) { + switch (vtx.type()) { + case VDfgType::atSpliceArray: { + const DfgSpliceArray* const vp = vtx.as(); + DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverIndex(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + case VDfgType::atSplicePacked: { + const DfgSplicePacked* const vp = vtx.as(); + DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverLsb(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + default: { + vtx.v3fatalSrc("Unhandled DfgVertexVariadic sub type: " + vtx.typeName()); + VL_UNREACHABLE; + break; + } + } + } else { + DfgVertex* const cp = vtxp2clonep.at(&vtx); + const auto oSourceEdges = vtx.sourceEdges(); + auto cSourceEdges = cp->sourceEdges(); + UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, + "Mismatched source count"); + for (size_t i = 0; i < oSourceEdges.second; ++i) { + if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { + cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); + } } } } @@ -313,6 +329,25 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { return; } + if (vtx.is()) { + os << toDotId(vtx); + os << " [label=\"" << vtx.typeName() << "\n"; + if (const DfgSpliceArray* const sp = vtx.cast()) { + const int elements = VN_AS(sp->dtypep(), UnpackArrayDType)->elementsConst(); + os << "_[" << elements << "]"; + } else { + os << "W" << vtx.width(); + } + os << " / F" << vtx.fanout() << '"'; + if (vtx.hasMultipleSinks()) { + os << ", shape=doubleoctagon"; + } else { + os << ", shape=octagon"; + } + os << "]\n"; + return; + } + os << toDotId(vtx); os << " [label=\"" << vtx.typeName() << "\nW" << vtx.width() << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { @@ -336,7 +371,7 @@ static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { // if (edge.sourcep()) { string headLabel; - if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); + if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); dumpDotEdge(os, edge, headLabel); } }); @@ -521,24 +556,34 @@ bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; } V3Hash DfgVertex::selfHash() const { return V3Hash{}; } bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const { + // If same vertex, then equal if (this == &that) return true; + + // If different type, then not equal if (this->type() != that.type()) return false; + + // If different data type, then not equal if (this->dtypep() != that.dtypep()) return false; + + // If different number of inputs, then not equal + auto thisPair = this->sourceEdges(); + const DfgEdge* const thisSrcEdgesp = thisPair.first; + const size_t thisArity = thisPair.second; + auto thatPair = that.sourceEdges(); + const DfgEdge* const thatSrcEdgesp = thatPair.first; + const size_t thatArity = thatPair.second; + if (thisArity != thatArity) return false; + + // Check vertex specifics if (!this->selfEquals(that)) return false; + // Check sources const auto key = (this < &that) ? EqualsCache::key_type{this, &that} // : EqualsCache::key_type{&that, this}; // Note: the recursive invocation can cause a re-hash but that will not invalidate references uint8_t& result = cache[key]; if (!result) { result = 2; // Assume equals - auto thisPair = this->sourceEdges(); - const DfgEdge* const thisSrcEdgesp = thisPair.first; - const size_t thisArity = thisPair.second; - auto thatPair = that.sourceEdges(); - const DfgEdge* const thatSrcEdgesp = thatPair.first; - const size_t thatArity = thatPair.second; - UASSERT_OBJ(thisArity == thatArity, this, "Same type vertices must have same arity!"); for (size_t i = 0; i < thisArity; ++i) { const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep; const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep; @@ -561,7 +606,12 @@ V3Hash DfgVertex::hash() { // variables, which we rely on. if (!is()) { hash += m_type; - hash += width(); // Currently all non-variable vertices are packed, so this is safe + if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) { + hash += adtypep->elementsConst(); + // TODO: maybe include sub-dtype, but not hugely important at the moment + } else { + hash += width(); + } const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; const size_t arity = pair.second; @@ -579,19 +629,16 @@ uint32_t DfgVertex::fanout() const { return result; } -DfgVarPacked* DfgVertex::getResultVar() { - UASSERT_OBJ(!this->is(), this, "Arrays are not supported by " << __FUNCTION__); - +DfgVertexVar* DfgVertex::getResultVar() { // It's easy if the vertex is already a variable ... - if (DfgVarPacked* const varp = this->cast()) return varp; + if (DfgVertexVar* const varp = this->cast()) return varp; - // Inspect existing variables fully written by this vertex, and choose one - DfgVarPacked* resp = nullptr; + // Inspect existing variables written by this vertex, and choose one + DfgVertexVar* resp = nullptr; // cppcheck-has-bug-suppress constParameter this->forEachSink([&resp](DfgVertex& sink) { - DfgVarPacked* const varp = sink.cast(); + DfgVertexVar* const varp = sink.cast(); if (!varp) return; - if (!varp->isDrivenFullyByDfg()) return; // Ignore SystemC variables, they cannot participate in expressions or // be assigned rvalue expressions. if (varp->varp()->isSc()) return; @@ -694,6 +741,42 @@ bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as(); + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) { + if (driverIndex(i) != thatp->driverIndex(i)) return false; + } + return true; +} + +V3Hash DfgSpliceArray::selfHash() const { + V3Hash hash; + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) hash += driverIndex(i); + return hash; +} + +// DfgSplicePacked ---------- + +bool DfgSplicePacked::selfEquals(const DfgVertex& that) const { + const DfgSplicePacked* const thatp = that.as(); + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) { + if (driverLsb(i) != thatp->driverLsb(i)) return false; + } + return true; +} + +V3Hash DfgSplicePacked::selfHash() const { + V3Hash hash; + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) hash += driverLsb(i); + return hash; +} + // DfgVertexVar ---------- bool DfgVertexVar::selfEquals(const DfgVertex& that) const { diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 4101b3573..be198cf47 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -238,8 +238,7 @@ public: // Width of result uint32_t width() const { - // This is a hot enough function that this is an expensive check, so in debug build only. - UDEBUGONLY(UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'");); + UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'"); return dtypep()->width(); } @@ -283,7 +282,7 @@ public: // Return a canonical variable vertex that holds the value of this vertex, // or nullptr if no such variable exists in the graph. This is O(fanout). - DfgVarPacked* getResultVar() VL_MT_DISABLED; + DfgVertexVar* getResultVar() VL_MT_DISABLED; // Cache type for 'scopep' below using ScopeCache = std::unordered_map; @@ -572,7 +571,7 @@ class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex { protected: DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep, - uint32_t initialCapacity = 1) + uint32_t initialCapacity) : DfgVertex{dfg, type, flp, dtypep} , m_srcsp{allocSources(initialCapacity)} , m_srcCap{initialCapacity} {} @@ -914,15 +913,14 @@ bool DfgVertex::isOnes() const { // Inline method definitions - for DfgVertexVar //------------------------------------------------------------------------------ -DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity) - : DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity} +DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) + : DfgVertexUnary{dfg, type, varp->fileline(), dtypeFor(varp)} , m_varp{varp} , m_varScopep{nullptr} { UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph"); } -DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, - uint32_t initialCapacity) - : DfgVertexVariadic{dfg, type, vscp->fileline(), dtypeFor(vscp), initialCapacity} +DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) + : DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)} , m_varp{vscp->varp()} , m_varScopep{vscp} { UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph"); diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index b0532ee95..dc121cf00 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -183,49 +183,37 @@ class AstToDfgVisitor final : public VNVisitor { return m_foundUnhandled; } - // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. - bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) { + DfgVertexSplice* convertLValue(AstNode* nodep) { + FileLine* const flp = nodep->fileline(); + if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { m_foundUnhandled = false; visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, 0, vtxp); - return true; - } - if (AstSel* const selp = VN_CAST(nodep, Sel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); - if (!vrefp || !lsbp) { - ++m_ctx.m_nonRepLhs; - return false; + if (m_foundUnhandled) return nullptr; + DfgVertexVar* const vtxp = getVertex(vrefp)->template as(); + // Ensure driving splice vertex exists + if (!vtxp->srcp()) { + if (VN_IS(vtxp->dtypep(), UnpackArrayDType)) { + vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, vtxp->dtypep()}); + } else { + vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, vtxp->dtypep()}); + } } - m_foundUnhandled = false; - visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, lsbp->toUInt(), vtxp); - return true; - } - if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const idxp = VN_CAST(selp->bitp(), Const); - if (!vrefp || !idxp) { - ++m_ctx.m_nonRepLhs; - return false; - } - m_foundUnhandled = false; - visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, idxp->toUInt(), vtxp); - return true; + return vtxp->srcp()->as(); } + + ++m_ctx.m_nonRepLhs; + return nullptr; + } + + // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. + bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) { + // Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part if (AstConcat* const concatp = VN_CAST(nodep, Concat)) { AstNode* const lhsp = concatp->lhsp(); AstNode* const rhsp = concatp->rhsp(); - { + { // Convet LHS of concat FileLine* const lFlp = lhsp->fileline(); DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)}; lVtxp->fromp(vtxp); @@ -233,7 +221,7 @@ class AstToDfgVisitor final : public VNVisitor { if (!convertAssignment(flp, lhsp, lVtxp)) return false; } - { + { // Convert RHS of concat FileLine* const rFlp = rhsp->fileline(); DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)}; rVtxp->fromp(vtxp); @@ -241,7 +229,37 @@ class AstToDfgVisitor final : public VNVisitor { return convertAssignment(flp, rhsp, rVtxp); } } - ++m_ctx.m_nonRepLhs; + + if (AstSel* const selp = VN_CAST(nodep, Sel)) { + AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); + const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); + if (!vrefp || !lsbp) { + ++m_ctx.m_nonRepLhs; + return false; + } + if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { + splicep->template as()->addDriver(flp, lsbp->toUInt(), vtxp); + return true; + } + } else if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) { + AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); + const AstConst* const idxp = VN_CAST(selp->bitp(), Const); + if (!vrefp || !idxp) { + ++m_ctx.m_nonRepLhs; + return false; + } + if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { + splicep->template as()->addDriver(flp, idxp->toUInt(), vtxp); + return true; + } + } else if (VN_IS(nodep, VarRef)) { + if (DfgVertexSplice* const splicep = convertLValue(nodep)) { + splicep->template as()->addDriver(flp, 0, vtxp); + return true; + } + } else { + ++m_ctx.m_nonRepLhs; + } return false; } @@ -315,18 +333,23 @@ class AstToDfgVisitor final : public VNVisitor { for (DfgVarPacked* const varp : m_varPackedps) { // Delete variables with no sinks nor sources (this can happen due to reverting // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && varp->arity() == 0) { + if (!varp->hasSinks() && !varp->srcp()) { VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); continue; } + // Nothing to do for un-driven (input) variables + if (!varp->srcp()) continue; + + DfgSplicePacked* const splicep = varp->srcp()->as(); + // Gather (and unlink) all drivers std::vector drivers; - drivers.reserve(varp->arity()); - varp->forEachSourceEdge([this, varp, &drivers](DfgEdge& edge, size_t idx) { + drivers.reserve(splicep->arity()); + splicep->forEachSourceEdge([this, splicep, &drivers](DfgEdge& edge, size_t idx) { DfgVertex* const driverp = edge.sourcep(); UASSERT(driverp, "Should not have created undriven sources"); - addDriver(varp->driverFileLine(idx), varp->driverLsb(idx), driverp, drivers); + addDriver(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp, drivers); edge.unlinkSource(); }); @@ -424,10 +447,10 @@ class AstToDfgVisitor final : public VNVisitor { } // Reinsert drivers in order - varp->resetSources(); + splicep->resetSources(); for (const Driver& driver : drivers) { if (!driver.m_vtxp) break; // Stop at end of compacted list - varp->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); + splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); } // Prune vertices potentially unused due to resolving multiple drivers. @@ -442,6 +465,15 @@ class AstToDfgVisitor final : public VNVisitor { vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); }); vtxp->unlinkDelete(*m_dfgp); } + + // If the whole variable is driven, remove the splice node + if (splicep->arity() == 1 // + && splicep->driverLsb(0) == 0 // + && splicep->source(0)->width() == varp->width()) { + varp->srcp(splicep->source(0)); + varp->driverFileLine(splicep->driverFileLine(0)); + splicep->unlinkDelete(*m_dfgp); + } } } @@ -450,7 +482,7 @@ class AstToDfgVisitor final : public VNVisitor { for (DfgVarArray* const varp : m_varArrayps) { // Delete variables with no sinks nor sources (this can happen due to reverting // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && varp->arity() == 0) { + if (!varp->hasSinks() && !varp->srcp()) { VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); } } diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index b85609e04..58405d8b5 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -363,7 +363,7 @@ class TraceDriver final : public DfgVisitor { UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName()); } - void visit(DfgVarPacked* vtxp) override { + void visit(DfgSplicePacked* vtxp) override { // Proceed with the driver that wholly covers the searched bits const auto pair = vtxp->sourceEdges(); for (size_t i = 0; i < pair.second; ++i) { @@ -378,6 +378,13 @@ class TraceDriver final : public DfgVisitor { } } + void visit(DfgVarPacked* vtxp) override { + if (DfgVertex* const srcp = vtxp->srcp()) { + SET_RESULT(trace(srcp, m_msb, m_lsb)); + return; + } + } + void visit(DfgConcat* vtxp) override { DfgVertex* const rhsp = vtxp->rhsp(); DfgVertex* const lhsp = vtxp->lhsp(); @@ -607,10 +614,7 @@ class IndependentBits final : public DfgVisitor { mask(vtxp).setAllBits1(); // intentionally not using MASK here } - void visit(DfgVarPacked* vtxp) override { - // The mask of the traced variable is known to be all ones - if (vtxp == m_varp) return; - + void visit(DfgSplicePacked* vtxp) override { // Combine the masks of all drivers V3Number& m = MASK(vtxp); vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { @@ -619,6 +623,14 @@ class IndependentBits final : public DfgVisitor { }); } + void visit(DfgVarPacked* vtxp) override { + // The mask of the traced variable is known to be all ones + if (vtxp == m_varp) return; + + // Combine the masks of all drivers + if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp); + } + void visit(DfgConcat* vtxp) override { const DfgVertex* const rhsp = vtxp->rhsp(); const DfgVertex* const lhsp = vtxp->lhsp(); @@ -732,6 +744,13 @@ class IndependentBits final : public DfgVisitor { DfgVertex* const currp = m_workList.front(); m_workList.pop_front(); + if (VN_IS(currp->dtypep(), UnpackArrayDType)) { + // For an unpacked array vertex, just enque it's sinks. + // (There can be no loops through arrays directly) + currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + continue; + } + // Grab current mask of item const V3Number& maskCurr = mask(currp); // Remember current mask @@ -764,16 +783,11 @@ public: // so bits reported dependent might not actually be, but all bits reported // independent are known to be so. static V3Number apply(DfgVarPacked* varp) { + UASSERT_OBJ(varp->srcp(), varp, "Don't call on undriven variable"); IndependentBits independentBits{varp}; - // Combine the masks of all drivers of the variable + // The mask represents the dependent bits, so invert it V3Number result{varp->fileline(), static_cast(varp->width()), 0}; - varp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - const DfgVertex* const srcp = edge.sourcep(); - // The mask represents the dependent bits, so invert it - V3Number inverseMask{srcp->fileline(), static_cast(srcp->width()), 0}; - inverseMask.opNot(independentBits.mask(srcp)); - result.opSelInto(inverseMask, varp->driverLsb(i), srcp->width()); - }); + result.opNot(independentBits.mask(varp->srcp())); return result; } }; diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index cc9c3e76a..4647d1484 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -341,32 +341,20 @@ class ExtractCyclicComponents final { return *clonep; } - // Fix up non-variable sources of a DfgVertexVar that are in a different component, - // using the provided 'relink' callback - template - void fixSources(T_Vertex& vtx, std::function relink) { - static_assert(std::is_base_of::value, - "'Vertex' must be a 'DfgVertexVar'"); + // Fix edges that cross components + void fixEdges(DfgVertexVar& vtx) { const size_t component = state(vtx).component; - vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) { - DfgVertex& source = *edge.sourcep(); - // DfgVertexVar sources are fixed up by `fixSinks` on those sources - if (source.is()) return; - const size_t sourceComponent = state(source).component; - // Same component is OK - if (sourceComponent == component) return; - // Unlink the source edge (source is reconnected by 'relink' - edge.unlinkSource(); - // Apply the fixup - // cppcheck-has-bug-suppress constVariable - DfgVertexVar& clone = getClone(vtx, sourceComponent); - relink(*(clone.as()), source, idx); - }); - } - // Fix up sinks of given variable vertex that are in a different component - void fixSinks(DfgVertexVar& vtx) { - const size_t component = state(vtx).component; + // All variable vertices have at most a single source, and only variable + // vertices can have multiple sinks, therefore the source must be either: + // - in the same component as the variable vertex + // - be a variable vertex itself, which might be in a different component + // The later case will be fixed up when handling the source variable + DfgVertex* const srcp = vtx.srcp(); + UASSERT_OBJ(!srcp || srcp->is() || state(*srcp).component == component, &vtx, + "Driver of DfgVertexVar must be in the same component"); + + // Fix up sinks in a differetn component vtx.forEachSinkEdge([&](DfgEdge& edge) { const size_t sinkComponent = state(*edge.sinkp()).component; // Same component is OK @@ -376,49 +364,6 @@ class ExtractCyclicComponents final { }); } - // Fix edges that cross components - void fixEdges(DfgVertexVar& vtx) { - if (DfgVarPacked* const vvtxp = vtx.cast()) { - fixSources( - *vvtxp, [&](DfgVarPacked& clone, DfgVertex& driver, size_t driverIdx) { - clone.addDriver(vvtxp->driverFileLine(driverIdx), // - vvtxp->driverLsb(driverIdx), &driver); - }); - fixSinks(*vvtxp); - return; - } - - if (DfgVarArray* const vvtxp = vtx.cast()) { - fixSources( // - *vvtxp, [&](DfgVarArray& clone, DfgVertex& driver, size_t driverIdx) { - clone.addDriver(vvtxp->driverFileLine(driverIdx), // - vvtxp->driverIndex(driverIdx), &driver); - }); - fixSinks(*vvtxp); - return; - } - } - - static void packSources(DfgGraph& dfg) { - // Remove undriven variable sources - for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) { - if (DfgVarPacked* const varp = vtxp->cast()) { - varp->packSources(); - if (!varp->hasSinks() && varp->arity() == 0) { - VL_DO_DANGLING(varp->unlinkDelete(dfg), varp); - } - continue; - } - if (DfgVarArray* const varp = vtxp->cast()) { - varp->packSources(); - if (!varp->hasSinks() && varp->arity() == 0) { - VL_DO_DANGLING(varp->unlinkDelete(dfg), varp); - } - continue; - } - } - } - template void moveVertices(DfgVertex::List& list) { for (DfgVertex* const vtxp : list.unlinkable()) { @@ -446,11 +391,6 @@ class ExtractCyclicComponents final { UASSERT_OBJ(component == state(snk).component, &vtx, "Edge crossing components without variable involvement"); }); - if (const DfgVertexVar* const vtxp = vtx.cast()) { - vtxp->forEachSourceEdge([](const DfgEdge& edge, size_t) { - UASSERT_OBJ(edge.sourcep(), edge.sinkp(), "Missing source on variable vertex"); - }); - } }); } @@ -489,11 +429,6 @@ class ExtractCyclicComponents final { if (&vtx == lastp) break; } - // Pack sources of variables to remove the now undriven inputs - // (cloning might have unlinked some of the inputs), - packSources(m_dfg); - for (const auto& dfgp : m_components) packSources(*dfgp); - // Check results for consistency if (VL_UNLIKELY(m_doExpensiveChecks)) { checkEdges(m_dfg); diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index f62f3f3a3..7c7ac3d88 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -201,45 +201,52 @@ class DfgToAstVisitor final : DfgVisitor { ++m_ctx.m_resultEquations; } - void convertVarDriver(const DfgVarPacked* dfgVarp) { - if (dfgVarp->isDrivenFullyByDfg()) { - // Whole variable is driven. Render driver and assign directly to whole variable. - FileLine* const flp = dfgVarp->driverFileLine(0); - AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->source(0)); - addResultEquation(dfgVarp, flp, lhsp, rhsp); - } else { + void convertPackedDriver(const DfgVarPacked* dfgVarp) { + if (DfgSplicePacked* const splicep = dfgVarp->srcp()->cast()) { // Variable is driven partially. Render each driver as a separate assignment. - dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { + splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); // Render the rhs expression AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); // Create select LValue - FileLine* const flp = dfgVarp->driverFileLine(idx); + FileLine* const flp = splicep->driverFileLine(idx); AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)}; + AstConst* const lsbp = new AstConst{flp, splicep->driverLsb(idx)}; const int width = static_cast(edge.sourcep()->width()); AstSel* const lhsp = new AstSel{flp, refp, lsbp, width}; // Add assignment of the value to the selected bits addResultEquation(dfgVarp, flp, lhsp, rhsp); }); + return; } + + // Whole variable is driven. Render driver and assign directly to whole variable. + FileLine* const flp + = dfgVarp->driverFileLine() ? dfgVarp->driverFileLine() : dfgVarp->fileline(); + AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->srcp()); + addResultEquation(dfgVarp, flp, lhsp, rhsp); } void convertArrayDiver(const DfgVarArray* dfgVarp) { - // Variable is driven partially. Assign from parts of the canonical var. - dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { - UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); - // Render the rhs expression - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); - // Create select LValue - FileLine* const flp = dfgVarp->driverFileLine(idx); - AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const idxp = new AstConst{flp, dfgVarp->driverIndex(idx)}; - AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp}; - // Add assignment of the value to the selected bits - addResultEquation(dfgVarp, flp, lhsp, rhsp); - }); + if (DfgSpliceArray* const splicep = dfgVarp->srcp()->cast()) { + // Variable is driven partially. Assign from parts of the canonical var. + splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { + UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); + // Render the rhs expression + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); + // Create select LValue + FileLine* const flp = splicep->driverFileLine(idx); + AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; + AstConst* const idxp = new AstConst{flp, splicep->driverIndex(idx)}; + AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp}; + // Add assignment of the value to the selected bits + addResultEquation(dfgVarp, flp, lhsp, rhsp); + }); + return; + } + + UASSERT_OBJ(false, dfgVarp, "Should not have wholly driven arrays in Dfg"); } // VISITORS @@ -285,11 +292,11 @@ class DfgToAstVisitor final : DfgVisitor { // The graph must have been regularized, so we only need to render assignments for (DfgVertexVar& vtx : dfg.varVertices()) { // If there is no driver (this vertex is an input to the graph), then nothing to do. - if (!vtx.isDrivenByDfg()) continue; + if (!vtx.srcp()) continue; // Render packed variable assignments if (const DfgVarPacked* const dfgVarp = vtx.cast()) { - convertVarDriver(dfgVarp); + convertPackedDriver(dfgVarp); continue; } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 5d1e86795..717012a10 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -172,7 +172,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) { // Don't inline SystemC variables, as SystemC types are not interchangeable with // internal types, and hence the variables are not interchangeable either. if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) { - DfgVertex* const driverp = varp->source(0); + DfgVertex* const driverp = varp->srcp(); // We must keep the original driver in certain cases, when swapping them would // not be functionally or technically (implementation reasons) equivalent: @@ -381,12 +381,14 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // The index variable DfgVarPacked* const idxVtxp = [&]() { // If there is an existing result variable, use that, otherwise create a new variable - DfgVarPacked* varp = srcp->getResultVar(); - if (!varp) { + DfgVarPacked* varp = nullptr; + if (DfgVertexVar* const vp = srcp->getResultVar()) { + varp = vp->as(); + } else { const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables); varp = dfg.makeNewVar(flp, name, idxDTypep, nullptr)->as(); varp->varp()->isInternal(true); - varp->addDriver(flp, 0, srcp); + varp->srcp(srcp); } varp->setHasModRefs(); return varp; @@ -554,9 +556,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { if (!varp->hasModRefs()) { // If it is only referenced in this DFG, it can be removed ++ctx.m_varsRemoved; - varp->replaceWith(varp->source(0)); + varp->replaceWith(varp->srcp()); varp->nodep()->unlinkFrBack()->deleteTree(); - } else if (DfgVarPacked* const driverp = varp->source(0)->cast()) { + } else if (DfgVarPacked* const driverp = varp->srcp()->cast()) { // If it's driven from another variable, it can be replaced by that. However, we do not // want to propagate SystemC variables into the design. if (driverp->varp()->isSc()) continue; diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 8cbe90104..0e4f27967 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1202,11 +1202,15 @@ class V3DfgPeephole final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { if (DfgConst* const idxp = vtxp->bitp()->cast()) { if (DfgVarArray* const varp = vtxp->fromp()->cast()) { - const size_t idx = idxp->toSizeT(); - if (DfgVertex* const driverp = varp->driverAt(idx)) { - APPLYING(INLINE_ARRAYSEL) { - replace(vtxp, driverp); - return; + if (varp->srcp()) { + if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { + const size_t idx = idxp->toSizeT(); + if (DfgVertex* const driverp = splicep->driverAt(idx)) { + APPLYING(INLINE_ARRAYSEL) { + replace(vtxp, driverp); + return; + } + } } } } diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 2b9c72b54..0bfa62a08 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -45,12 +45,23 @@ class DfgRegularize final { // Ensure intermediate values used multiple times are written to variables for (DfgVertex& vtx : m_dfg.opVertices()) { const bool needsIntermediateVariable = [&]() { + // Splice vertices represent partial assignments, so they need a variable + // iff and only if they have a non-variable sink. + if (vtx.is()) { + const bool hasNonVarSink + = vtx.findSink([](const DfgVertex& snk) { // + return !snk.is(); + }); + return hasNonVarSink; + } // Anything that drives an SC variable needs an intermediate, // as we can only assign simple variables to SC variables at runtime. const bool hasScSink = vtx.findSink([](const DfgVertexVar& var) { // return var.varp()->isSc(); }); if (hasScSink) return true; + // // Splice vertices always need a variable as they represent partial updates + // if (vtx.is()) return true; // Operations without multiple sinks need no variables if (!vtx.hasMultipleSinks()) return false; // Array selects need no variables, they are just memory references @@ -63,26 +74,21 @@ class DfgRegularize final { // This is an op that requires a result variable. Ensure it is // assigned to one, and redirect other sinks read that variable. - if (DfgVarPacked* const varp = vtx.getResultVar()) { - // A variable already exists - UASSERT_OBJ(varp->arity() == 1, varp, "Result variable with multiple drivers"); - FileLine* const flp = varp->driverFileLine(0); - varp->sourceEdge(0)->unlinkSource(); - varp->resetSources(); + if (DfgVertexVar* const varp = vtx.getResultVar()) { + varp->sourceEdge<0>()->unlinkSource(); vtx.replaceWith(varp); - varp->addDriver(flp, 0, &vtx); + varp->srcp(&vtx); } else { // Need to create an intermediate variable const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps); FileLine* const flp = vtx.fileline(); AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr; - DfgVarPacked* const newp - = m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep)->as(); + DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep); ++m_nTmps; ++m_ctx.m_temporariesIntroduced; // Replace vertex with the variable and add back driver vtx.replaceWith(newp); - newp->addDriver(vtx.fileline(), 0, &vtx); + newp->srcp(&vtx); } } } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 833c3c869..50dd9a070 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -38,22 +38,24 @@ // === Abstract base node types (DfgVertex*) =================================== -class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic { +class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary { AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex) AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned) bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module bool m_hasExtRefs = false; // This AstVar is referenced from outside the module + // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr. + FileLine* m_driverFileLine = nullptr; bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED; V3Hash selfHash() const final VL_MT_DISABLED; public: - inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity); - inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, uint32_t initialCapacity); + inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp); + inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp); ASTGEN_MEMBERS_DfgVertexVar; - bool isDrivenByDfg() const { return arity() > 0; } + const std::string srcName(size_t) const override { return ""; } AstVar* varp() const { return m_varp; } AstVarScope* varScopep() const { return m_varScopep; } @@ -68,6 +70,13 @@ public: void setHasExtRefs() { m_hasExtRefs = true; } bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); } + FileLine* driverFileLine() const { return m_driverFileLine; } + void driverFileLine(FileLine* flp) { m_driverFileLine = flp; } + + bool isDrivenFullyByDfg() const { + return srcp() && !srcp()->is() && !varp()->isForced(); + } + // Variable cannot be removed, even if redundant in the DfgGraph (might be used externally) bool keep() const { // Keep if referenced outside this module @@ -82,6 +91,12 @@ public: return false; } }; +class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic { +public: + DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexVariadic{dfg, type, flp, dtypep, 1u} {} + ASTGEN_MEMBERS_DfgVertexSplice; +}; // === Concrete node types ===================================================== @@ -184,14 +199,49 @@ class DfgVarArray final : public DfgVertexVar { public: DfgVarArray(DfgGraph& dfg, AstVar* varp) - : DfgVertexVar{dfg, dfgType(), varp, 4u} { + : DfgVertexVar{dfg, dfgType(), varp} { UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), varp, "Non array DfgVarArray"); } DfgVarArray(DfgGraph& dfg, AstVarScope* vscp) - : DfgVertexVar{dfg, dfgType(), vscp, 4u} { + : DfgVertexVar{dfg, dfgType(), vscp} { UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), vscp, "Non array DfgVarArray"); } ASTGEN_MEMBERS_DfgVarArray; +}; +class DfgVarPacked final : public DfgVertexVar { + friend class DfgVertex; + friend class DfgVisitor; + +public: + DfgVarPacked(DfgGraph& dfg, AstVar* varp) + : DfgVertexVar{dfg, dfgType(), varp} { + UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), varp, "Array DfgVarPacked"); + } + DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp) + : DfgVertexVar{dfg, dfgType(), vscp} { + UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), vscp, "Array DfgVarPacked"); + } + ASTGEN_MEMBERS_DfgVarPacked; +}; + +// === DfgVertexSplice === +class DfgSpliceArray final : public DfgVertexSplice { + friend class DfgVertex; + friend class DfgVisitor; + + using DriverData = std::pair; + + std::vector m_driverData; // Additional data associated with each driver + + bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; + V3Hash selfHash() const override VL_MT_DISABLED; + +public: + DfgSpliceArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { + UASSERT_OBJ(VN_IS(dtypep, UnpackArrayDType), flp, "Non array DfgSpliceArray"); + } + ASTGEN_MEMBERS_DfgSpliceArray; void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) { m_driverData.emplace_back(flp, index); @@ -203,27 +253,6 @@ public: DfgVertexVariadic::resetSources(); } - // Remove undriven sources - void packSources() { - // Grab and reset the driver data - std::vector driverData; - driverData.swap(m_driverData); - - // Grab and unlink the sources - std::vector sources{arity()}; - forEachSourceEdge([&](DfgEdge& edge, size_t idx) { - sources[idx] = edge.sourcep(); - edge.unlinkSource(); - }); - DfgVertexVariadic::resetSources(); - - // Add back the driven sources - for (size_t i = 0; i < sources.size(); ++i) { - if (!sources[i]) continue; - addDriver(driverData[i].first, driverData[i].second, sources[i]); - } - } - FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; } @@ -234,26 +263,27 @@ public: return edgep ? edgep->sourcep() : nullptr; } - const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); } + const std::string srcName(size_t idx) const override { + return std::to_string(driverIndex(idx)); + } }; -class DfgVarPacked final : public DfgVertexVar { +class DfgSplicePacked final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; using DriverData = std::pair; - std::vector m_driverData; // Additional data associate with each driver + std::vector m_driverData; // Additional data associated with each driver + + bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; + V3Hash selfHash() const override VL_MT_DISABLED; public: - DfgVarPacked(DfgGraph& dfg, AstVar* varp) - : DfgVertexVar{dfg, dfgType(), varp, 1u} {} - DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp) - : DfgVertexVar{dfg, dfgType(), vscp, 1u} {} - ASTGEN_MEMBERS_DfgVarPacked; - - bool isDrivenFullyByDfg() const { - return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced(); + DfgSplicePacked(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { + UASSERT_OBJ(!VN_IS(dtypep, UnpackArrayDType), flp, "Array DfgSplicePacked"); } + ASTGEN_MEMBERS_DfgSplicePacked; void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) { m_driverData.emplace_back(flp, lsb); @@ -265,33 +295,10 @@ public: DfgVertexVariadic::resetSources(); } - // Remove undriven sources - void packSources() { - // Grab and reset the driver data - std::vector driverData; - driverData.swap(m_driverData); - - // Grab and unlink the sources - std::vector sources{arity()}; - forEachSourceEdge([&](DfgEdge& edge, size_t idx) { - sources[idx] = edge.sourcep(); - edge.unlinkSource(); - }); - DfgVertexVariadic::resetSources(); - - // Add back the driven sources - for (size_t i = 0; i < sources.size(); ++i) { - if (!sources[i]) continue; - addDriver(driverData[i].first, driverData[i].second, sources[i]); - } - } - FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; } - const string srcName(size_t idx) const override { - return isDrivenFullyByDfg() ? "" : cvtToStr(driverLsb(idx)); - } + const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); } }; #endif diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index fe46e2055..b7f461835 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -107,4 +107,8 @@ module t ( `signal(REPLICATE, 4); assign REPLICATE = rand_a[3:0] ^ ({2{REPLICATE[3:2]}} >> 2); + `signal(PARTIAL, 4); + assign PARTIAL[0] = rand_a[0]; + // PARTIAL[1] intentionally unconnected + assign PARTIAL[3:2] = rand_a[3:2] ^ {PARTIAL[2], PARTIAL[0]}; endmodule From caf3522364d4b2013c04263350ac18528e716ae2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 14 Jul 2025 19:50:02 -0400 Subject: [PATCH 023/252] Support implicit enum declarations with packed dimensions --- src/verilog.y | 9 +++++---- test_regress/t/t_enum.v | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/verilog.y b/src/verilog.y index bc09282af..0f8c1e417 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2226,10 +2226,11 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_ty new AstDefImplicitDType{$1->fileline(), "__typeimpsu" + cvtToStr(GRAMMARP->s_typeImpNum++), VFlagChildDType{}, $1}, $2, true); } - | enumDecl - { $$ = new AstDefImplicitDType{$1->fileline(), - "__typeimpenum" + cvtToStr(GRAMMARP->s_typeImpNum++), - VFlagChildDType{}, $1}; } + | enumDecl packed_dimensionListE + { $$ = GRAMMARP->createArray( + new AstDefImplicitDType{$1->fileline(), + "__typeimpenum" + cvtToStr(GRAMMARP->s_typeImpNum++), + VFlagChildDType{}, $1}, $2, true); } | ySTRING { $$ = new AstBasicDType{$1, VBasicDTypeKwd::STRING}; } | yCHANDLE diff --git a/test_regress/t/t_enum.v b/test_regress/t/t_enum.v index 887866a37..1a4db38b8 100644 --- a/test_regress/t/t_enum.v +++ b/test_regress/t/t_enum.v @@ -47,6 +47,10 @@ module t (/*AUTOARG*/); var enum logic [3:0] { QINVALID='1, QSEND={2'b0,2'h0}, QOP={2'b0,2'h1}, QCL={2'b0,2'h2}, QPR={2'b0,2'h3 }, QACK, QRSP } inv; + enum logic [7:0] { + ENARRAY = 6 + } [3:2] enarray; + initial begin if (e0 !== 0) $stop; if (e1 !== 1) $stop; @@ -86,6 +90,11 @@ module t (/*AUTOARG*/); if ($size(array5) != 5) $stop; if ($size(array5i) != 5) $stop; + enarray[2] = ENARRAY; + enarray[3] = ENARRAY; + if (enarray[2] !== ENARRAY) $stop; + if (enarray[3] !== ENARRAY) $stop; + $write("*-* All Finished *-*\n"); $finish; end From 371ac07c6fdcb4516e38bed867a04141ec5f4f0e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 14 Jul 2025 20:08:44 -0400 Subject: [PATCH 024/252] Fix CVTREAL not being able to be disabled in e.g. primitive terminals. --- src/V3Width.cpp | 10 +++++----- test_regress/t/t_var_types_bad.out | 14 ++++---------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f35bcebb0..1571a7081 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -7675,11 +7675,11 @@ class WidthVisitor final : public VNVisitor { // SIGNED/DOUBLE METHODS AstNodeExpr* checkCvtUS(AstNodeExpr* nodep) { - if (nodep && nodep->isDouble()) { - nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeNameQ() - << ") input to " - << nodep->backp()->prettyTypeName()); - nodep = spliceCvtS(nodep, true, 32); + if (nodep && nodep->dtypep()->skipRefp()->isDouble()) { + nodep->v3warn(REALCVT, + "Implicit conversion of real to integer; expected integral input to " + << nodep->backp()->prettyTypeName()); + nodep = spliceCvtS(nodep, false, 32); } return nodep; } diff --git a/test_regress/t/t_var_types_bad.out b/test_regress/t/t_var_types_bad.out index f60d10048..89cb32d5e 100644 --- a/test_regress/t/t_var_types_bad.out +++ b/test_regress/t/t_var_types_bad.out @@ -15,11 +15,8 @@ : ... note: In instance 't' 46 | d_real[0] = 1'b1; | ^ -%Error: t/t_var_types_bad.v:46:7: Expected integral (non-'real') input to SEL - : ... note: In instance 't' - 46 | d_real[0] = 1'b1; - | ^~~~~~ -%Warning-REALCVT: t/t_var_types_bad.v:46:7: Implicit conversion of real to integer +%Warning-REALCVT: t/t_var_types_bad.v:46:7: Implicit conversion of real to integer; expected integral input to SEL + : ... note: In instance 't' 46 | d_real[0] = 1'b1; | ^~~~~~ ... For warning description see https://verilator.org/warn/REALCVT?v=latest @@ -28,11 +25,8 @@ : ... note: In instance 't' 47 | d_realtime[0] = 1'b1; | ^ -%Error: t/t_var_types_bad.v:47:7: Expected integral (non-'real') input to SEL - : ... note: In instance 't' - 47 | d_realtime[0] = 1'b1; - | ^~~~~~~~~~ -%Warning-REALCVT: t/t_var_types_bad.v:47:7: Implicit conversion of real to integer +%Warning-REALCVT: t/t_var_types_bad.v:47:7: Implicit conversion of real to integer; expected integral input to SEL + : ... note: In instance 't' 47 | d_realtime[0] = 1'b1; | ^~~~~~~~~~ %Error: Exiting due to From f3e109d8c5fda8a9f79b9bd39c01f013c4e1ebfd Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Tue, 15 Jul 2025 15:31:08 +0200 Subject: [PATCH 025/252] Support covergroup extends, etc. (#6160) --- src/verilog.y | 46 ++++++++++++++----- test_regress/t/t_covergroup_args.py | 16 +++++++ test_regress/t/t_covergroup_args.v | 19 ++++++++ test_regress/t/t_covergroup_extends.py | 16 +++++++ test_regress/t/t_covergroup_extends.v | 39 ++++++++++++++++ .../t/t_covergroup_extends_newfirst.py | 16 +++++++ .../t/t_covergroup_extends_newfirst.v | 39 ++++++++++++++++ .../t/t_covergroup_func_override_bad.out | 5 ++ .../t/t_covergroup_func_override_bad.py | 20 ++++++++ .../t/t_covergroup_func_override_bad.v | 17 +++++++ test_regress/t/t_covergroup_in_class.py | 16 +++++++ test_regress/t/t_covergroup_in_class.v | 18 ++++++++ .../t/t_covergroup_in_class_colliding.py | 16 +++++++ .../t/t_covergroup_in_class_colliding.v | 30 ++++++++++++ .../t/t_covergroup_in_class_duplicate_bad.out | 8 ++++ .../t/t_covergroup_in_class_duplicate_bad.py | 16 +++++++ .../t/t_covergroup_in_class_duplicate_bad.v | 16 +++++++ .../t/t_covergroup_new_override_bad.out | 5 ++ .../t/t_covergroup_new_override_bad.py | 20 ++++++++ .../t/t_covergroup_new_override_bad.v | 14 ++++++ test_regress/t/t_covergroup_unsup.out | 31 +++++-------- test_regress/t/t_covergroup_unsup.v | 14 +++--- 22 files changed, 400 insertions(+), 37 deletions(-) create mode 100755 test_regress/t/t_covergroup_args.py create mode 100644 test_regress/t/t_covergroup_args.v create mode 100755 test_regress/t/t_covergroup_extends.py create mode 100644 test_regress/t/t_covergroup_extends.v create mode 100755 test_regress/t/t_covergroup_extends_newfirst.py create mode 100644 test_regress/t/t_covergroup_extends_newfirst.v create mode 100644 test_regress/t/t_covergroup_func_override_bad.out create mode 100755 test_regress/t/t_covergroup_func_override_bad.py create mode 100644 test_regress/t/t_covergroup_func_override_bad.v create mode 100755 test_regress/t/t_covergroup_in_class.py create mode 100644 test_regress/t/t_covergroup_in_class.v create mode 100755 test_regress/t/t_covergroup_in_class_colliding.py create mode 100644 test_regress/t/t_covergroup_in_class_colliding.v create mode 100755 test_regress/t/t_covergroup_in_class_duplicate_bad.out create mode 100755 test_regress/t/t_covergroup_in_class_duplicate_bad.py create mode 100644 test_regress/t/t_covergroup_in_class_duplicate_bad.v create mode 100644 test_regress/t/t_covergroup_new_override_bad.out create mode 100755 test_regress/t/t_covergroup_new_override_bad.py create mode 100644 test_regress/t/t_covergroup_new_override_bad.v diff --git a/src/verilog.y b/src/verilog.y index 0f8c1e417..b3f468ace 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -6822,21 +6822,38 @@ covergroup_declaration: // ==IEEE: covergroup_declaration | covergroup_declarationFront '(' tf_port_listE ')' /*cont*/ coverage_eventE ';' coverage_spec_or_optionListE /*cont*/ yENDGROUP endLabelE - { $$ = $1; + { AstFunc* const newp = new AstFunc{$1, "new", nullptr, nullptr}; + newp->classMethod(true); + newp->isConstructor(true); + newp->dtypep($1->dtypep()); + newp->addStmtsp($3); + $1->addMembersp(newp); + $$ = $1; GRAMMARP->endLabel($9, $1, $9); } - // // IEEE 1800-2023 added: - | covergroup_declarationFront yEXTENDS idAny/*covergroup_identifier*/ - /*cont*/ ';' coverage_spec_or_optionListE - /*cont*/ yENDGROUP endLabelE - { $$ = $1; - GRAMMARP->endLabel($7, $1, $7); } + ; + +covergroup_extendsE: // IEEE: Part of covergroup_declaration + /* empty */ { $$ = nullptr; } + | yEXTENDS { $$ = $1; } ; covergroup_declarationFront: // IEEE: part of covergroup_declaration - yCOVERGROUP idAny - { $$ = new AstClass{$2, *$2, PARSEP->libname()}; + yCOVERGROUP covergroup_extendsE idAny + { + $$ = new AstClass{$3, *$3, PARSEP->libname()}; + + AstFunc* const sample = new AstFunc{$1, "sample", nullptr, nullptr}; + sample->classMethod(true); + sample->dtypep(sample->findVoidDType()); + $$->addMembersp(sample); + + AstFunc* const getCoverage = new AstFunc{$1, "get_coverage", nullptr, nullptr}; + getCoverage->classMethod(true); + getCoverage->dtypep(getCoverage->findVoidDType()); + $$->addMembersp(getCoverage); + BBCOVERIGN($1, "Ignoring unsupported: covergroup"); } - ; + ; cgexpr: // IEEE-2012: covergroup_expression, before that just expression expr { $$ = $1; } @@ -7006,7 +7023,7 @@ cross_itemList: // IEEE: part of list_of_cross_items ; cross_item: // ==IEEE: cross_item - idAny/*cover_point_identifier or variable_identifier*/ { $$ = nullptr; /*UNSUP*/ } + idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ } ; cross_body: // ==IEEE: cross_body @@ -7552,7 +7569,12 @@ class_item: // ==IEEE: class_item | class_declaration { $$ = $1; } | timeunits_declaration { $$ = $1; } | covergroup_declaration - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); } + { + const string cgName = $1->name(); + $1->name("__vlAnonCG_" + cgName); + AstVar* const newp = new AstVar{$1, VVarType::VAR, cgName, VFlagChildDType{}, new AstRefDType($1, $1->name())}; + $$ = addNextNull($1, newp); + } // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } | ';' { $$ = nullptr; } diff --git a/test_regress/t/t_covergroup_args.py b/test_regress/t/t_covergroup_args.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_covergroup_args.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_covergroup_args.v b/test_regress/t/t_covergroup_args.v new file mode 100644 index 000000000..fdd1eb144 --- /dev/null +++ b/test_regress/t/t_covergroup_args.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +module t; + covergroup cgArgs(int var1, int var2=42); + + endgroup + + cgArgs cov1 = new(69, 77); + cgArgs cov2 = new(69); + function x(); + cov1.sample(); + cov2.get_coverage(); + endfunction; +endmodule diff --git a/test_regress/t/t_covergroup_extends.py b/test_regress/t/t_covergroup_extends.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_covergroup_extends.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_covergroup_extends.v b/test_regress/t/t_covergroup_extends.v new file mode 100644 index 000000000..68404631d --- /dev/null +++ b/test_regress/t/t_covergroup_extends.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +module t; + class base; + enum {red, green, blue} color; + covergroup g1 (bit [3:0] a) with function sample(bit b); + option.weight = 10; + option.per_instance = 1; + coverpoint a; + coverpoint b; + c: coverpoint color; + endgroup + function new(); + g1 = new(0); + endfunction + endclass + + class derived extends base; + bit d; + covergroup extends g1; + option.weight = 1; // overrides the weight from base g1 + // uses per_instance = 1 from base g1 + c: coverpoint color // overrides the c coverpoint in base g1 + { + ignore_bins ignore = {blue}; + } + coverpoint d; // adds new coverpoint + cross a, d; // crosses new coverpoint with inherited one + endgroup :g1 + function new(); + super.new(); + endfunction + endclass +endmodule diff --git a/test_regress/t/t_covergroup_extends_newfirst.py b/test_regress/t/t_covergroup_extends_newfirst.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_covergroup_extends_newfirst.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_covergroup_extends_newfirst.v b/test_regress/t/t_covergroup_extends_newfirst.v new file mode 100644 index 000000000..62a2fe1d6 --- /dev/null +++ b/test_regress/t/t_covergroup_extends_newfirst.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +module t; + class base; + function new(); + g1 = new(0); + endfunction + enum {red, green, blue} color; + covergroup g1 (bit [3:0] a) with function sample(bit b); + option.weight = 10; + option.per_instance = 1; + coverpoint a; + coverpoint b; + c: coverpoint color; + endgroup + endclass + + class derived extends base; + bit d; + function new(); + super.new(); + endfunction + covergroup extends g1; + option.weight = 1; // overrides the weight from base g1 + // uses per_instance = 1 from base g1 + c: coverpoint color // overrides the c coverpoint in base g1 + { + ignore_bins ignore = {blue}; + } + coverpoint d; // adds new coverpoint + cross a, d; // crosses new coverpoint with inherited one + endgroup :g1 + endclass +endmodule diff --git a/test_regress/t/t_covergroup_func_override_bad.out b/test_regress/t/t_covergroup_func_override_bad.out new file mode 100644 index 000000000..a8073404e --- /dev/null +++ b/test_regress/t/t_covergroup_func_override_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_covergroup_func_override_bad.v:10:5: syntax error, unexpected function + 10 | function sample(); + | ^~~~~~~~ + ... 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_covergroup_func_override_bad.py b/test_regress/t/t_covergroup_func_override_bad.py new file mode 100755 index 000000000..e137e7309 --- /dev/null +++ b/test_regress/t/t_covergroup_func_override_bad.py @@ -0,0 +1,20 @@ +#!/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') + +if test.vlt_all: + test.lint(fails=True, expect_filename=test.golden_filename) +else: + test.compile(nc_flags2=["-coverage", "functional"]) + test.execute() + +test.passes() diff --git a/test_regress/t/t_covergroup_func_override_bad.v b/test_regress/t/t_covergroup_func_override_bad.v new file mode 100644 index 000000000..927723573 --- /dev/null +++ b/test_regress/t/t_covergroup_func_override_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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +module t(); + covergroup cg; + function sample(); + + endfunction + function get_coverage(); + + endfunction + endgroup +endmodule diff --git a/test_regress/t/t_covergroup_in_class.py b/test_regress/t/t_covergroup_in_class.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_covergroup_in_class.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_covergroup_in_class.v b/test_regress/t/t_covergroup_in_class.v new file mode 100644 index 000000000..a08670322 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +class myClass; + covergroup embeddedCg; + + endgroup + + function new(); + embeddedCg = new(); + embeddedCg.sample(); + embeddedCg.get_coverage(); + endfunction +endclass diff --git a/test_regress/t/t_covergroup_in_class_colliding.py b/test_regress/t/t_covergroup_in_class_colliding.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_colliding.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_covergroup_in_class_colliding.v b/test_regress/t/t_covergroup_in_class_colliding.v new file mode 100644 index 000000000..acd931744 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_colliding.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +class myClass; + covergroup embeddedCg; + + endgroup + + function new(); + embeddedCg = new(); + embeddedCg.sample(); + embeddedCg.get_coverage(); + endfunction +endclass + +class secondClass; + covergroup embeddedCg; + + endgroup + + function new(); + embeddedCg = new(); + embeddedCg.sample(); + embeddedCg.get_coverage(); + endfunction +endclass diff --git a/test_regress/t/t_covergroup_in_class_duplicate_bad.out b/test_regress/t/t_covergroup_in_class_duplicate_bad.out new file mode 100755 index 000000000..bebff3b94 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_duplicate_bad.out @@ -0,0 +1,8 @@ +%Error: t/t_covergroup_in_class_duplicate_bad.v:13:16: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg' + 13 | covergroup embeddedCg; + | ^~~~~~~~~~ + t/t_covergroup_in_class_duplicate_bad.v:9:16: ... Location of original declaration + 9 | covergroup embeddedCg; + | ^~~~~~~~~~ + ... 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_covergroup_in_class_duplicate_bad.py b/test_regress/t/t_covergroup_in_class_duplicate_bad.py new file mode 100755 index 000000000..dff5516bf --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_duplicate_bad.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(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_in_class_duplicate_bad.v b/test_regress/t/t_covergroup_in_class_duplicate_bad.v new file mode 100644 index 000000000..c94ed0729 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_duplicate_bad.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +class myClass; + covergroup embeddedCg; + + endgroup + + covergroup embeddedCg; + + endgroup +endclass diff --git a/test_regress/t/t_covergroup_new_override_bad.out b/test_regress/t/t_covergroup_new_override_bad.out new file mode 100644 index 000000000..4a8175f0f --- /dev/null +++ b/test_regress/t/t_covergroup_new_override_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_covergroup_new_override_bad.v:10:5: syntax error, unexpected function + 10 | function new(); + | ^~~~~~~~ + ... 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_covergroup_new_override_bad.py b/test_regress/t/t_covergroup_new_override_bad.py new file mode 100755 index 000000000..e137e7309 --- /dev/null +++ b/test_regress/t/t_covergroup_new_override_bad.py @@ -0,0 +1,20 @@ +#!/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') + +if test.vlt_all: + test.lint(fails=True, expect_filename=test.golden_filename) +else: + test.compile(nc_flags2=["-coverage", "functional"]) + test.execute() + +test.passes() diff --git a/test_regress/t/t_covergroup_new_override_bad.v b/test_regress/t/t_covergroup_new_override_bad.v new file mode 100644 index 000000000..a215369e7 --- /dev/null +++ b/test_regress/t/t_covergroup_new_override_bad.v @@ -0,0 +1,14 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off COVERIGN */ +module t(); + covergroup cg; + function new(); + + endfunction + endgroup +endmodule diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 7394aecf4..bd3713710 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -385,28 +385,21 @@ 136 | cross a, b { | ^~~~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:154:4: Ignoring unsupported: covergroup - 154 | covergroup cg_more extends cg_empty; + 154 | covergroup cgArgs(int cg_lim); | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:4: Ignoring unsupported: covergroup - 157 | covergroup cg_args(int cg_lim); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:7: Ignoring unsupported: covergroup - 164 | covergroup cov1 @m_z; +%Warning-COVERIGN: t/t_covergroup_unsup.v:161:7: Ignoring unsupported: covergroup + 161 | covergroup cov1 @m_z; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:23: Ignoring unsupported: coverage clocking event - 164 | covergroup cov1 @m_z; +%Warning-COVERIGN: t/t_covergroup_unsup.v:161:23: Ignoring unsupported: coverage clocking event + 161 | covergroup cov1 @m_z; | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:165:10: Ignoring unsupported: coverpoint - 165 | coverpoint m_x; +%Warning-COVERIGN: t/t_covergroup_unsup.v:162:10: Ignoring unsupported: coverpoint + 162 | coverpoint m_x; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:166:10: Ignoring unsupported: coverpoint - 166 | coverpoint m_y; +%Warning-COVERIGN: t/t_covergroup_unsup.v:163:10: Ignoring unsupported: coverpoint + 163 | coverpoint m_y; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:18: Ignoring unsupported: covergroup within class - 164 | covergroup cov1 @m_z; - | ^~~~ -%Error: t/t_covergroup_unsup.v:169:23: Can't find definition of variable: 'cov1' - 169 | function new(); cov1 = new; endfunction - | ^~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Ignoring unsupported: covergroup + 171 | covergroup extends cg_empty; + | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index 9f20305af..c665b0f5c 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -151,10 +151,7 @@ module t (/*AUTOARG*/ } endgroup - covergroup cg_more extends cg_empty; - endgroup - - covergroup cg_args(int cg_lim); + covergroup cgArgs(int cg_lim); endgroup class CgCls; @@ -170,10 +167,15 @@ module t (/*AUTOARG*/ `endif endclass + class CgEmb; + covergroup extends cg_empty; + endgroup + endclass + always @(posedge clk) begin - cg_more cov1 = new; + cg_empty cov1 = new; `ifndef T_COVERGROUP_UNSUP_IGN - cg_args cov2 = new(2); + cgArgs cov2 = new(2); `endif if (cyc == 10) begin $write("*-* All Finished *-*\n"); From db6b17fdb415e109e06926e1d02328d6c7fee475 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 15 Jul 2025 17:41:08 -0400 Subject: [PATCH 026/252] Fix error message --- src/V3LinkParse.cpp | 2 +- src/verilog.y | 1 + test_regress/t/t_foreach_bad.out | 9 ++++++--- test_regress/t/t_foreach_bad.v | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 77769d5a7..98ee38a5c 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -578,7 +578,7 @@ class LinkParseVisitor final : public VNVisitor { } else if (VN_IS(bracketp, SelLoopVars)) { // Ok } else { - nodep->v3error("Syntax error; foreach missing bracketed loop variable" + nodep->v3error("Foreach missing bracketed loop variable is no-operation" " (IEEE 1800-2023 12.7.3)"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; diff --git a/src/verilog.y b/src/verilog.y index b3f468ace..82ae0137f 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -6112,6 +6112,7 @@ idArrayedForeach: // IEEE: id + select (under foreach expression) id { $$ = new AstParseRef{$1, VParseRefExp::PX_TEXT, *$1, nullptr, nullptr}; } // // IEEE: id + part_select_range/constant_part_select_range + | idArrayed '[' ']' { $$ = $1; } // Or AstArraySel, don't know yet. | idArrayed '[' expr ']' { $$ = new AstSelBit{$2, $1, $3}; } // Or AstArraySel, don't know yet. | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract{$2, $1, $3, $5}; } // // IEEE: id + indexed_range/constant_indexed_range diff --git a/test_regress/t/t_foreach_bad.out b/test_regress/t/t_foreach_bad.out index 0c2524398..4987c12d9 100644 --- a/test_regress/t/t_foreach_bad.out +++ b/test_regress/t/t_foreach_bad.out @@ -1,8 +1,11 @@ -%Error: t/t_foreach_bad.v:14:7: Syntax error; foreach missing bracketed loop variable (IEEE 1800-2023 12.7.3) +%Error: t/t_foreach_bad.v:14:7: Foreach missing bracketed loop variable is no-operation (IEEE 1800-2023 12.7.3) 14 | foreach (array); | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_foreach_bad.v:18:23: 'foreach' loop variable expects simple variable name - 18 | foreach (array[a.b]); +%Error: t/t_foreach_bad.v:16:7: Foreach missing bracketed loop variable is no-operation (IEEE 1800-2023 12.7.3) + 16 | foreach (array[]); + | ^~~~~~~ +%Error: t/t_foreach_bad.v:20:23: 'foreach' loop variable expects simple variable name + 20 | foreach (array[a.b]); | ^ %Error: Exiting due to diff --git a/test_regress/t/t_foreach_bad.v b/test_regress/t/t_foreach_bad.v index ac2d7bd68..1226338c9 100644 --- a/test_regress/t/t_foreach_bad.v +++ b/test_regress/t/t_foreach_bad.v @@ -13,6 +13,8 @@ module t (/*AUTOARG*/); initial begin foreach (array); // no index + foreach (array[]); // no index + foreach (array.array[a]); // not supported foreach (array[a.b]); // no index From 1f0357ba93b8b81191468dcf853c722943487fad Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 16 Jul 2025 08:18:57 -0400 Subject: [PATCH 027/252] Add NOEFFECT warning, replacing previous `foreach` error. --- Changes | 1 + docs/guide/warnings.rst | 15 ++++++++ src/V3Begin.cpp | 6 ++- src/V3Error.h | 49 +++++++++++-------------- src/verilog.y | 4 +- test_regress/t/t_foreach_bad.out | 7 +--- test_regress/t/t_foreach_bad.v | 2 - test_regress/t/t_foreach_noivar.py | 18 +++++++++ test_regress/t/t_foreach_noivar.v | 31 ++++++++++++++++ test_regress/t/t_foreach_noivar_bad.out | 11 ++++++ test_regress/t/t_foreach_noivar_bad.py | 17 +++++++++ 11 files changed, 124 insertions(+), 37 deletions(-) create mode 100755 test_regress/t/t_foreach_noivar.py create mode 100644 test_regress/t/t_foreach_noivar.v create mode 100644 test_regress/t/t_foreach_noivar_bad.out create mode 100755 test_regress/t/t_foreach_noivar_bad.py diff --git a/Changes b/Changes index 7e20b5412..c5323559b 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.039 devel **Other:** * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. +* Add NOEFFECT warning, replacing previous `foreach` error. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index b14612600..a7143fe3b 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1308,6 +1308,21 @@ List Of Warnings simulate correctly. +.. option:: NOEFFECT + + Warns that the statement will have no effect and is roughly equivalent + to not being present. This is only issued when it is "non-obvious", + e.g. a :code:`if (0)` will not result in this warning. + + Faulty example: + + .. code-block:: sv + + foreach (array[]) begin ... end //<--- Warning + + For a fix, remove the statement. + + .. option:: NOLATCH .. TODO better example diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 0484b9038..b1f5ce772 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -530,7 +530,11 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) { } // The parser validates we don't have "foreach (array[,,,])" AstNode* const bodyp = nodep->stmtsp(); - UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable"); + if (!newp) { + nodep->v3warn(NOEFFECT, "foreach with no loop variable has no effect"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return nullptr; + } if (bodyp) { bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext()); } else { diff --git a/src/V3Error.h b/src/V3Error.h index 307de72e8..b8ca71866 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -125,6 +125,7 @@ public: MULTIDRIVEN, // Driven from multiple blocks MULTITOP, // Multiple top level modules NEWERSTD, // Newer language standard required + NOEFFECT, // Statement has no effect NOLATCH, // No latch detected in always_latch block NONSTD, // Non-standard feature present in other sims NULLPORT, // Null port detected in module definition @@ -190,41 +191,33 @@ public: : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning constexpr operator en() const VL_MT_SAFE { return m_e; } const char* ascii() const VL_MT_SAFE { - // clang-format off static const char* const names[] = { // Leading spaces indicate it can't be disabled. " MIN", " INFO", " FATAL", " FATALMANY", " FATALSRC", " ERROR", " FIRST_NAMED", // Boolean - " I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED", + " I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", + " I_TRACING", " I_UNUSED", // Errors "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED", // Warnings - " EC_FIRST_WARN", - "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "BADVLTPRAGMA", - "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", - "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", - "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", - "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", - "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", - "GENCLK", "GENUNNAMED", "HIERBLOCK", - "IFDEPTH", "IGNOREDRETURN", - "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", - "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", - "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", - "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY", - "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", - "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", - "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR", - "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", - "TICKCOUNT", "TIMESCALEMOD", - "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", - "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP" ,"UNUSEDPARAM", "UNUSEDSIGNAL", - "USERERROR", "USERFATAL", "USERINFO", "USERWARN", - "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", - "ZERODLY", "ZEROREPL", - " MAX" - }; - // clang-format on + " EC_FIRST_WARN", "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", + "BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", + "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", + "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME", + "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", + "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "IFDEPTH", + "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", + "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", + "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", + "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY", + "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", + "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", + "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR", "STATICVAR", + "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", + "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", + "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", + "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", + "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; return names[m_e]; } // Warnings that default to off diff --git a/src/verilog.y b/src/verilog.y index 82ae0137f..b25b207f7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4181,6 +4181,7 @@ loop_variables: // IEEE: loop_variables parseRefBase { $$ = $1; } | loop_variables ',' parseRefBase { $$ = $1->addNext($3); } | ',' parseRefBase { $$ = new AstEmpty{$1}; $$->addNext($2); } + | ',' { $$ = new AstEmpty{$1}; } ; //************************************************ @@ -6112,7 +6113,6 @@ idArrayedForeach: // IEEE: id + select (under foreach expression) id { $$ = new AstParseRef{$1, VParseRefExp::PX_TEXT, *$1, nullptr, nullptr}; } // // IEEE: id + part_select_range/constant_part_select_range - | idArrayed '[' ']' { $$ = $1; } // Or AstArraySel, don't know yet. | idArrayed '[' expr ']' { $$ = new AstSelBit{$2, $1, $3}; } // Or AstArraySel, don't know yet. | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract{$2, $1, $3, $5}; } // // IEEE: id + indexed_range/constant_indexed_range @@ -6120,6 +6120,8 @@ idArrayedForeach: // IEEE: id + select (under foreach expression) | idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus{$2, $1, $3, $5}; } // // IEEE: loop_variables (under foreach expression) // // To avoid conflicts we allow expr as first element, must post-check + | idArrayed '[' ']' + { $$ = new AstSelLoopVars{$2, $1, new AstEmpty{$3}}; } | idArrayed '[' expr ',' loop_variables ']' { $$ = new AstSelLoopVars{$2, $1, addNextNull(static_cast($3), $5)}; } | idArrayed '[' ',' loop_variables ']' diff --git a/test_regress/t/t_foreach_bad.out b/test_regress/t/t_foreach_bad.out index 4987c12d9..9c519fec6 100644 --- a/test_regress/t/t_foreach_bad.out +++ b/test_regress/t/t_foreach_bad.out @@ -2,10 +2,7 @@ 14 | foreach (array); | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_foreach_bad.v:16:7: Foreach missing bracketed loop variable is no-operation (IEEE 1800-2023 12.7.3) - 16 | foreach (array[]); - | ^~~~~~~ -%Error: t/t_foreach_bad.v:20:23: 'foreach' loop variable expects simple variable name - 20 | foreach (array[a.b]); +%Error: t/t_foreach_bad.v:18:23: 'foreach' loop variable expects simple variable name + 18 | foreach (array[a.b]); | ^ %Error: Exiting due to diff --git a/test_regress/t/t_foreach_bad.v b/test_regress/t/t_foreach_bad.v index 1226338c9..ac2d7bd68 100644 --- a/test_regress/t/t_foreach_bad.v +++ b/test_regress/t/t_foreach_bad.v @@ -13,8 +13,6 @@ module t (/*AUTOARG*/); initial begin foreach (array); // no index - foreach (array[]); // no index - foreach (array.array[a]); // not supported foreach (array[a.b]); // no index diff --git a/test_regress/t/t_foreach_noivar.py b/test_regress/t/t_foreach_noivar.py new file mode 100755 index 000000000..53f0d4385 --- /dev/null +++ b/test_regress/t/t_foreach_noivar.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=['-Wno-NOEFFECT']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_foreach_noivar.v b/test_regress/t/t_foreach_noivar.v new file mode 100644 index 000000000..7646328d6 --- /dev/null +++ b/test_regress/t/t_foreach_noivar.v @@ -0,0 +1,31 @@ +// 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); + +module t (/*AUTOARG*/); + + reg [63:0] sum; // Checked not in objects + reg [2:1] [4:3] array [5:6] [7:8]; + + initial begin + sum = 0; + foreach (array[]) begin // NOP + ++sum; + end + `checkh(sum, 0); + + sum = 0; + foreach (array[,,]) begin // NOP + ++sum; + end + `checkh(sum, 0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_foreach_noivar_bad.out b/test_regress/t/t_foreach_noivar_bad.out new file mode 100644 index 000000000..2494ada33 --- /dev/null +++ b/test_regress/t/t_foreach_noivar_bad.out @@ -0,0 +1,11 @@ +%Warning-NOEFFECT: t/t_foreach_noivar.v:17:5: foreach with no loop variable has no effect + : ... note: In instance 't' + 17 | foreach (array[]) begin + | ^~~~~~~ + ... For warning description see https://verilator.org/warn/NOEFFECT?v=latest + ... Use "/* verilator lint_off NOEFFECT */" and lint_on around source to disable this message. +%Warning-NOEFFECT: t/t_foreach_noivar.v:23:5: foreach with no loop variable has no effect + : ... note: In instance 't' + 23 | foreach (array[,,]) begin + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_foreach_noivar_bad.py b/test_regress/t/t_foreach_noivar_bad.py new file mode 100755 index 000000000..2101e3902 --- /dev/null +++ b/test_regress/t/t_foreach_noivar_bad.py @@ -0,0 +1,17 @@ +#!/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.top_filename = 't/t_foreach_noivar.v' + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() From 1bf24c7eb454d0b0e3d823d7ad59a335f0c42d3b Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 16 Jul 2025 16:04:17 +0200 Subject: [PATCH 028/252] Add support for disabling begin just under fork from outside that begin (#5432 partial) (#6183) --- src/V3LinkJump.cpp | 124 +++++++++++++--------- test_regress/t/t_disable_empty_outside.py | 18 ++++ test_regress/t/t_disable_empty_outside.v | 28 +++++ test_regress/t/t_disable_outside3.py | 18 ++++ test_regress/t/t_disable_outside3.v | 32 ++++++ test_regress/t/t_disable_outside4.py | 18 ++++ test_regress/t/t_disable_outside4.v | 28 +++++ 7 files changed, 213 insertions(+), 53 deletions(-) create mode 100755 test_regress/t/t_disable_empty_outside.py create mode 100644 test_regress/t/t_disable_empty_outside.v create mode 100755 test_regress/t/t_disable_outside3.py create mode 100644 test_regress/t/t_disable_outside3.v create mode 100755 test_regress/t/t_disable_outside4.py create mode 100644 test_regress/t/t_disable_outside4.v diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index fa65ec3d7..ae8b6d328 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -185,6 +185,56 @@ class LinkJumpVisitor final : public VNVisitor { return new AstStmtExpr{ fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; } + void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { + // The support is limited only to disabling a fork from outside that fork. + // It utilizes the process::kill()` method. For each `disable` a queue of processes is + // declared. At the beginning of each fork that can be disabled, its process handle is + // pushed to the queue. `disable` statement is replaced with calling `kill()` method on + // each element of the queue. + FileLine* const fl = nodep->fileline(); + const std::string targetName = nodep->targetp()->name(); + if (existsBlockAbove(targetName)) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork"); + } + if (m_ftaskp) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); + } + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + // Declare queue of processes (as a global variable for simplicity) + AstVar* const processQueuep = new AstVar{ + fl, VVarType::VAR, m_queueNames.get(targetName), VFlagChildDType{}, + new AstQueueDType{fl, VFlagChildDType{}, + new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; + processQueuep->lifetime(VLifetime::STATIC); + topPkgp->addStmtsp(processQueuep); + + AstVarRef* const queueWriteRefp + = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; + AstStmtExpr* pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); + + for (AstBegin* const beginp : forks) { + if (pushCurrentProcessp->backp()) { + pushCurrentProcessp = pushCurrentProcessp->cloneTree(false); + } + if (beginp->stmtsp()) { + // There is no need to add it to empty block + beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); + } + } + AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; + AstTaskRef* const killQueueCall + = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), + new AstArg{fl, "", queueRefp}}; + killQueueCall->classOrPackagep(processClassp); + nodep->addNextHere(new AstStmtExpr{fl, killQueueCall}); + } + static bool directlyUnderFork(const AstNode* const nodep) { + if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp()); + if (VN_IS(nodep->backp(), Fork)) return true; + return false; + } // VISITORS void visit(AstNodeModule* nodep) override { @@ -369,35 +419,9 @@ class LinkJumpVisitor final : public VNVisitor { if (VN_IS(targetp, Task)) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name"); } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { - // The support is limited only to disabling a fork from outside that fork. - // It utilizes the process::kill()` method. For each `disable` a queue of processes is - // declared. At the beginning of each fork that can be disabled, its process handle is - // pushed to the queue. `disable` statement is replaced with calling `kill()` method on - // each element of the queue. - if (existsBlockAbove(forkp->name())) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork"); - } - if (m_ftaskp) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); - } - AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); - AstClass* const processClassp - = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); - // Declare queue of processes (as a global variable for simplicity) - AstVar* const processQueuep = new AstVar{ - fl, VVarType::VAR, m_queueNames.get(forkp->name()), VFlagChildDType{}, - new AstQueueDType{fl, VFlagChildDType{}, - new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; - processQueuep->lifetime(VLifetime::STATIC); - topPkgp->addStmtsp(processQueuep); - - AstVarRef* const queueWriteRefp - = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; - AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); - + std::vector forks; for (AstNode* forkItemp = forkp->stmtsp(); forkItemp; forkItemp = forkItemp->nextp()) { - // Add push_back statement at the beginning of each fork. - // Wrap into begin block if needed + // Further handling of disable stmt requires all forks to be begin blocks AstBegin* beginp = VN_CAST(forkItemp, Begin); if (!beginp) { beginp = new AstBegin{fl, "", nullptr}; @@ -406,34 +430,28 @@ class LinkJumpVisitor final : public VNVisitor { // In order to continue the iteration forkItemp = beginp; } - if (pushCurrentProcessp->backp()) { - beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp->cloneTree(false)); - } else { - beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); - } + forks.push_back(beginp); } - - AstVarRef* const queueRefp - = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; - AstTaskRef* const killQueueCall - = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), - new AstArg{fl, "", queueRefp}}; - killQueueCall->classOrPackagep(processClassp); - nodep->addNextHere(new AstStmtExpr{fl, killQueueCall}); + handleDisableOnFork(nodep, forks); } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { - const std::string targetName = beginp->name(); - if (existsBlockAbove(targetName)) { - if (beginp->user3()) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: disabling block that contains a fork"); - } else { - // Jump to the end of the named block - AstJumpLabel* const labelp = findAddLabel(beginp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); - } + if (directlyUnderFork(beginp)) { + std::vector forks{beginp}; + handleDisableOnFork(nodep, forks); } else { - nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" - << targetName << "'"); + const std::string targetName = beginp->name(); + if (existsBlockAbove(targetName)) { + if (beginp->user3()) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: disabling block that contains a fork"); + } else { + // Jump to the end of the named block + AstJumpLabel* const labelp = findAddLabel(beginp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + } + } else { + nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" + << targetName << "'"); + } } } else { nodep->v3fatalSrc("Disable linked with node of unhandled type " diff --git a/test_regress/t/t_disable_empty_outside.py b/test_regress/t/t_disable_empty_outside.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_empty_outside.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_empty_outside.v b/test_regress/t/t_disable_empty_outside.v new file mode 100644 index 000000000..781ac4709 --- /dev/null +++ b/test_regress/t/t_disable_empty_outside.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + begin : blk + int x = 0; + fork : fork_blk + begin + end + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk; + #2; + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_disable_outside3.py b/test_regress/t/t_disable_outside3.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_outside3.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_outside3.v b/test_regress/t/t_disable_outside3.v new file mode 100644 index 000000000..f386699ee --- /dev/null +++ b/test_regress/t/t_disable_outside3.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + begin : blk + int x = 0; + fork : fork_blk + begin + #4; + x = 3; + end + begin : begin_blk + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk.begin_blk; + #2; + if (x != 1) $stop; + #2; + if (x != 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_disable_outside4.py b/test_regress/t/t_disable_outside4.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_outside4.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_outside4.v b/test_regress/t/t_disable_outside4.v new file mode 100644 index 000000000..22c7d9f75 --- /dev/null +++ b/test_regress/t/t_disable_outside4.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + begin : blk + int x = 0; + fork + begin + #1; + disable begin_blk; + end + begin : begin_blk + x = 1; + #2; + x = 2; + end + join_none + #3; + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule From abd509ce53c3d4e1043d3a7b55579318577a05b7 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Wed, 16 Jul 2025 17:52:56 +0200 Subject: [PATCH 029/252] Support delays in emitted Verilog (#6177) --- src/V3EmitV.cpp | 14 ++++++++++++++ test_regress/t/t_debug_emitv.out | 2 ++ test_regress/t/t_debug_emitv.py | 2 +- test_regress/t/t_debug_emitv.v | 1 + test_regress/t/t_debug_emitv_addrids.py | 2 +- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index df122080b..d0973120d 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -922,6 +922,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { m_sensesp = nodep->sensesp(); iterateAndNextConstNull(nodep->stmtsp()); } + void visit(AstDelay* nodep) override { + puts(""); // this is for proper alignment + puts("#"); + iterateConst(nodep->lhsp()); + puts(";\n"); + iterateAndNextConstNull(nodep->stmtsp()); + } + void visit(AstCAwait* nodep) override { + AstCMethodHard* methodp = VN_CAST(nodep->exprp(), CMethodHard); + UASSERT_OBJ(methodp, nodep, "AstCAwait expression must be an AstCMethodHard"); + puts(""); // this is for proper alignment + puts("#"); + iterateConst(methodp->pinsp()); + } void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); } void visit(AstNodeText*) override {} void visit(AstVarScope*) override {} diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 4976ecd1e..dfb607cbe 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -74,6 +74,8 @@ module Vt_debug_emitv_t; if (the_ifaces[2].ifsig) begin $write(""); end + #64'h1; + $write("After #1 delay"); end end bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; diff --git a/test_regress/t/t_debug_emitv.py b/test_regress/t/t_debug_emitv.py index f8d03f1e7..15e089a81 100755 --- a/test_regress/t/t_debug_emitv.py +++ b/test_regress/t/t_debug_emitv.py @@ -14,7 +14,7 @@ test.scenarios("vlt") test.lint( # We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions # Likewise XML - v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"]) + v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv --timing"]) output_vs = test.glob_some(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v") diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index f6111f33c..36761f2dc 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -86,6 +86,7 @@ module t (/*AUTOARG*/ if (|downto_32[55+:3]) $write(""); if (|downto_32[60-:7]) $write(""); if (the_ifaces[2].ifsig) $write(""); + #1 $write("After #1 delay"); end bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; diff --git a/test_regress/t/t_debug_emitv_addrids.py b/test_regress/t/t_debug_emitv_addrids.py index 84634c936..20bf40b0c 100755 --- a/test_regress/t/t_debug_emitv_addrids.py +++ b/test_regress/t/t_debug_emitv_addrids.py @@ -15,6 +15,6 @@ test.top_filename = "t/t_debug_emitv.v" test.lint( # We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions # Likewise XML - v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids"]) + v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids --timing"]) test.passes() From 826e5b08266273af70b2be9b0da2f41c6352a379 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Wed, 16 Jul 2025 18:07:34 +0200 Subject: [PATCH 030/252] Fix `--coverage-expr` null pointer dereference (#6181) --- src/V3Coverage.cpp | 20 ++++++++++---- test_regress/t/t_cover_expr_array_class.py | 18 +++++++++++++ test_regress/t/t_cover_expr_array_class.v | 26 +++++++++++++++++++ .../t/t_cover_expr_associative_array_class.py | 18 +++++++++++++ .../t/t_cover_expr_associative_array_class.v | 26 +++++++++++++++++++ .../t/t_cover_expr_dyn_array_class.py | 18 +++++++++++++ test_regress/t/t_cover_expr_dyn_array_class.v | 26 +++++++++++++++++++ test_regress/t/t_cover_expr_queue_class.py | 18 +++++++++++++ test_regress/t/t_cover_expr_queue_class.v | 26 +++++++++++++++++++ 9 files changed, 191 insertions(+), 5 deletions(-) create mode 100755 test_regress/t/t_cover_expr_array_class.py create mode 100644 test_regress/t/t_cover_expr_array_class.v create mode 100755 test_regress/t/t_cover_expr_associative_array_class.py create mode 100644 test_regress/t/t_cover_expr_associative_array_class.v create mode 100755 test_regress/t/t_cover_expr_dyn_array_class.py create mode 100644 test_regress/t/t_cover_expr_dyn_array_class.v create mode 100755 test_regress/t/t_cover_expr_queue_class.py create mode 100644 test_regress/t/t_cover_expr_queue_class.v diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 3ec67eb85..68e98835c 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -39,17 +39,28 @@ class ExprCoverageEligibleVisitor final : public VNVisitor { // STATE bool m_eligible = true; + static bool elemDTypeEligible(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + if (AstNodeDType* const dtp = dtypep->virtRefDTypep()) { + if (!elemDTypeEligible(dtp)) return false; + } + if (AstNodeDType* const dtp = dtypep->virtRefDType2p()) { + if (!elemDTypeEligible(dtp)) return false; + } + return !VN_IS(dtypep, ClassRefDType); + } + void visit(AstNodeVarRef* nodep) override { AstNodeDType* dtypep = nodep->varp()->dtypep(); - // Class objecs and references not supported for expression coverage + // Class objects and references not supported for expression coverage // because the object may not persist until the point at which // coverage data is gathered // This could be resolved in the future by protecting against dereferrencing // null pointers when cloning the expression for expression coverage - if (VN_CAST(dtypep, ClassRefDType)) { - m_eligible = false; - } else { + if (dtypep && elemDTypeEligible(dtypep)) { iterateChildren(nodep); + } else { + m_eligible = false; } } @@ -858,7 +869,6 @@ class CoverageVisitor final : public VNVisitor { != strs[!term.m_objective].end()) impossible = true; } - if (!redundant) expr.push_back(term); } if (!impossible) m_exprs.push_back(std::move(expr)); diff --git a/test_regress/t/t_cover_expr_array_class.py b/test_regress/t/t_cover_expr_array_class.py new file mode 100755 index 000000000..27aec629f --- /dev/null +++ b/test_regress/t/t_cover_expr_array_class.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=['--coverage-expr']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cover_expr_array_class.v b/test_regress/t/t_cover_expr_array_class.v new file mode 100644 index 000000000..1c82e6a45 --- /dev/null +++ b/test_regress/t/t_cover_expr_array_class.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1; + int value0 = 7; +endclass + +module t; + initial begin + int i = 0; + Class1 q[15]; + for (int j = 0; j < 15; j = j + 1) begin + Class1 x = new; + q[j] = x; + end + while (i < 15) begin + if ((q[i].value0 > 8) || (q[i].value0 < 5)) $stop; + i += 1; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_cover_expr_associative_array_class.py b/test_regress/t/t_cover_expr_associative_array_class.py new file mode 100755 index 000000000..27aec629f --- /dev/null +++ b/test_regress/t/t_cover_expr_associative_array_class.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=['--coverage-expr']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cover_expr_associative_array_class.v b/test_regress/t/t_cover_expr_associative_array_class.v new file mode 100644 index 000000000..2c1057a3b --- /dev/null +++ b/test_regress/t/t_cover_expr_associative_array_class.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1; + int value0 = 7; +endclass + +module t; + initial begin + int i = 0; + Class1 q[int] = '{}; + for (int j = 0; j < 15; j = j + 1) begin + Class1 x = new; + q[j] = x; + end + while (i < 15) begin + if ((q[i].value0 > 8) || (q[i].value0 < 5)) $stop; + i += 1; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_cover_expr_dyn_array_class.py b/test_regress/t/t_cover_expr_dyn_array_class.py new file mode 100755 index 000000000..27aec629f --- /dev/null +++ b/test_regress/t/t_cover_expr_dyn_array_class.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=['--coverage-expr']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cover_expr_dyn_array_class.v b/test_regress/t/t_cover_expr_dyn_array_class.v new file mode 100644 index 000000000..5cd5ab1cc --- /dev/null +++ b/test_regress/t/t_cover_expr_dyn_array_class.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1; + int value0 = 7; +endclass + +module t; + initial begin + int i = 0; + Class1 q[] = new [15]; + for (int j = 0; j < 15; j = j + 1) begin + Class1 x = new; + q[j] = x; + end + while (i < 15) begin + if ((q[i].value0 > 8) || (q[i].value0 < 5)) $stop; + i += 1; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_cover_expr_queue_class.py b/test_regress/t/t_cover_expr_queue_class.py new file mode 100755 index 000000000..27aec629f --- /dev/null +++ b/test_regress/t/t_cover_expr_queue_class.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=['--coverage-expr']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cover_expr_queue_class.v b/test_regress/t/t_cover_expr_queue_class.v new file mode 100644 index 000000000..fefcb0f36 --- /dev/null +++ b/test_regress/t/t_cover_expr_queue_class.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1; + int value0 = 7; +endclass + +module t; + initial begin + int i = 0; + Class1 q[$]; + repeat(15) begin + Class1 x = new; + q = { q, x }; + end + while (i < q.size()) begin + if ((q[i].value0 > 8) || (q[i].value0 < 5)) $stop; + i += 1; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 8c5ba3a0d7d20682294600c459accffb034a31c7 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Wed, 16 Jul 2025 23:25:01 +0200 Subject: [PATCH 031/252] Fix conflicting function/class name linking error (#6182) --- src/V3LinkDot.cpp | 39 ++++++++++++++++++- .../t/t_class_reference_name_colision.py | 16 ++++++++ .../t/t_class_reference_name_colision.v | 17 ++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_class_reference_name_colision.py create mode 100644 test_regress/t/t_class_reference_name_colision.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 1b39283f8..a654141b8 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -779,14 +779,51 @@ public: if (!foundp) baddot = dotname; return foundp; } + static bool checkIfClassOrPackage(const VSymEnt* const symp) { + if (VN_IS(symp->nodep(), Class) || VN_IS(symp->nodep(), Package)) return true; + const AstRefDType* refDTypep = nullptr; + if (const AstTypedef* const typedefp = VN_CAST(symp->nodep(), Typedef)) { + if (VN_IS(typedefp->childDTypep(), ClassRefDType)) return true; + if (const AstRefDType* const refp = VN_CAST(typedefp->childDTypep(), RefDType)) { + refDTypep = refp; + } + } else if (const AstParamTypeDType* const paramTypep + = VN_CAST(symp->nodep(), ParamTypeDType)) { + if (const AstRequireDType* const requireDTypep + = VN_CAST(paramTypep->childDTypep(), RequireDType)) { + if (const AstRefDType* const refp = VN_CAST(requireDTypep->lhsp(), RefDType)) { + refDTypep = refp; + } else if (VN_IS(requireDTypep->lhsp(), VoidDType) + || VN_IS(requireDTypep->lhsp(), BasicDType) + || VN_IS(requireDTypep->lhsp(), ClassRefDType)) { + return true; + } + } + } + // TODO: this should be handled properly - case when it is known what type is + // referenced by AstRefDType (refDTypep->typeofp() is null or + // refDTypep->classOrPackageOpp() is null) + if (refDTypep && !refDTypep->typeofp() && !refDTypep->classOrPackageOpp()) { + // When still unknown - return because it may be a class, classes may not be + // linked at this point. Return in case it gets resolved to a class in the future + return true; + } + return false; + } VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool fallback, bool classOnly, const string& forWhat) { if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp()); VSymEnt* foundp; if (fallback) { - foundp = lookSymp->findIdFallback(nodep->name()); + VSymEnt* currentLookSymp = lookSymp; + do { + foundp = currentLookSymp->findIdFlat(nodep->name()); + if (foundp && !checkIfClassOrPackage(foundp)) foundp = nullptr; + if (!foundp) currentLookSymp = currentLookSymp->fallbackp(); + } while (!foundp && currentLookSymp); } else { foundp = lookSymp->findIdFlat(nodep->name()); + if (foundp && !checkIfClassOrPackage(foundp)) foundp = nullptr; } if (!foundp && v3Global.rootp()->stdPackagep()) { // Look under implied std:: foundp = getNodeSym(v3Global.rootp()->stdPackagep())->findIdFlat(nodep->name()); diff --git a/test_regress/t/t_class_reference_name_colision.py b/test_regress/t/t_class_reference_name_colision.py new file mode 100755 index 000000000..147fe6faf --- /dev/null +++ b/test_regress/t/t_class_reference_name_colision.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_class_reference_name_colision.v b/test_regress/t/t_class_reference_name_colision.v new file mode 100644 index 000000000..6ddd27f78 --- /dev/null +++ b/test_regress/t/t_class_reference_name_colision.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class setup_coefficients; + static function int create(); + return 1; + endfunction +endclass + +class biquad_vseq; + int c_setup = setup_coefficients::create(); + function void setup_coefficients(); + endfunction +endclass: biquad_vseq From 94b043d6c95cbb0fcc4d54555795c169d838bac2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 16 Jul 2025 17:20:34 -0400 Subject: [PATCH 032/252] Internals: Print versions with `make format` --- Makefile.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.in b/Makefile.in index f22aec998..f98a03a49 100644 --- a/Makefile.in +++ b/Makefile.in @@ -470,6 +470,7 @@ CLANGFORMAT_FLAGS = -i CLANGFORMAT_FILES = $(CHECK_CPP) $(CHECK_H) $(CHECK_YL) test_regress/t/*.c* test_regress/t/*.h format-c clang-format: + $(CLANGFORMAT) --version @$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \ || echo "*** You are not using clang-format-14, indents may differ from master's ***" $(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES) @@ -538,6 +539,7 @@ YAPF = yapf3 YAPF_FLAGS = -i --parallel format-py yapf: + $(YAPF) --version $(YAPF) $(YAPF_FLAGS) $(PY_FILES) GERSEMI = gersemi From 7a32771c7ec4f47edbf83f27a58ec6f9ddd0d64c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 16 Jul 2025 17:26:01 -0400 Subject: [PATCH 033/252] Commentary: Changes update --- Changes | 6 +++++- docs/Makefile | 7 ++++++- docs/spelling.txt | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index c5323559b..44758dd21 100644 --- a/Changes +++ b/Changes @@ -16,18 +16,22 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] +* Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]] +* Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] * Fix class extends dotted error (#6162). [Igor Zaworski, Antmicro Ltd.] * Fix genvar error with `-O0` (#6165). [Max Wipfli] * Fix uninitialized thread PGO counters (#6167). [Bartłomiej Chmiel, Antmicro Ltd.] -* Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173). [Geza Lore] +* Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173) (#6176). [Geza Lore] * Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.]] * Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.] +* Fix `--coverage-expr` null pointer dereference (#6181). [Igor Zaworski, Antmicro Ltd.] +* Fix conflicting function/class name linking error (#6182). [Igor Zaworski, Antmicro Ltd.] Verilator 5.038 2025-07-08 diff --git a/docs/Makefile b/docs/Makefile index 98b2f47d8..9ee03aa73 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -68,11 +68,16 @@ pdf: ###################################################################### -html latex linkcheck spelling:: +html latex linkcheck:: $(MAKE) vl-extract $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(PYTHON3) bin/vl_sphinx_fix _build +spelling:: + $(MAKE) vl-extract + $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + sort -o guide/spelling.txt guide/spelling.txt + help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) diff --git a/docs/spelling.txt b/docs/spelling.txt index b8cdf4132..118898847 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -601,6 +601,7 @@ coroutines countbits countones cout +covergroup cpp cppstyle cpu From 7f1011e5f7d194e62e25ceae090d935baf223d0c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 16 Jul 2025 18:07:07 -0400 Subject: [PATCH 034/252] Make some CVTREAL fatal where IEEE requires it. --- src/V3Width.cpp | 47 ++++++++++++++--------- test_regress/t/t_lint_realcvt_bad.out | 55 +++++++++++++++++++++------ test_regress/t/t_lint_realcvt_bad.v | 28 +++++++++----- 3 files changed, 89 insertions(+), 41 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 1571a7081..7153c441a 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -629,8 +629,8 @@ class WidthVisitor final : public VNVisitor { iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); nodep->dtypeSetString(); } else { - iterateCheckSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); - iterateCheckSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + iterateCheckIntegralSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + iterateCheckIntegralSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); if (m_streamConcat) { packIfUnpacked(nodep->lhsp()); @@ -837,7 +837,7 @@ class WidthVisitor final : public VNVisitor { if (vdtypep && vdtypep->isString()) { iterateCheckString(nodep, "LHS", nodep->srcp(), BOTH); } else { - iterateCheckSelf(nodep, "LHS", nodep->srcp(), SELF, BOTH); + iterateCheckIntegralSelf(nodep, "LHS", nodep->srcp(), SELF, BOTH); } if ((vdtypep && vdtypep->isString()) || nodep->srcp()->isString()) { @@ -971,7 +971,7 @@ class WidthVisitor final : public VNVisitor { if (debug() >= 9) nodep->dumpTree("- selWidth: "); userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->lsbp(), WidthVP{SELF, PRELIM}.p()); - checkCvtUS(nodep->fromp()); + checkCvtUS(nodep->fromp(), false); iterateCheckSizedSelf(nodep, "Select LHS", nodep->fromp(), SELF, BOTH); int width = nodep->widthConst(); if (width <= 0) { @@ -1510,7 +1510,7 @@ class WidthVisitor final : public VNVisitor { return; } - checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->lhsp(), false); iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); nodep->dtypeFrom(nodep->lhsp()); } @@ -6826,7 +6826,7 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!"); if (m_vup->prelim()) { userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); - if (!real_ok) checkCvtUS(nodep->lhsp()); + if (!real_ok) checkCvtUS(nodep->lhsp(), false); } if (real_ok && nodep->lhsp()->isDouble()) { spliceCvtD(nodep->lhsp()); @@ -6867,7 +6867,7 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!"); if (m_vup->prelim()) { userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); - checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->lhsp(), true); const int width = nodep->lhsp()->width(); AstNodeDType* const expDTypep = nodep->findLogicDType(width, width, rs_out); nodep->dtypep(expDTypep); @@ -6895,7 +6895,7 @@ class WidthVisitor final : public VNVisitor { // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. if (m_vup->prelim()) { userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); - checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->lhsp(), false); iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); nodep->dtypeFrom(nodep->lhsp()); } @@ -6955,8 +6955,8 @@ class WidthVisitor final : public VNVisitor { // Determine expression widths only relying on what's in the subops userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); - checkCvtUS(nodep->lhsp()); - checkCvtUS(nodep->rhsp()); + checkCvtUS(nodep->lhsp(), false); + checkCvtUS(nodep->rhsp(), false); const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); @@ -6990,8 +6990,8 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); if (!real_ok) { - checkCvtUS(nodep->lhsp()); - checkCvtUS(nodep->rhsp()); + checkCvtUS(nodep->lhsp(), false); + checkCvtUS(nodep->rhsp(), false); } if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { spliceCvtD(nodep->lhsp()); @@ -7330,8 +7330,12 @@ class WidthVisitor final : public VNVisitor { } (void)underp; // cppcheck } + void iterateCheckIntegralSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ, + Stage stage) { + iterateCheckSelf(nodep, side, underp, determ, stage, true); + } void iterateCheckSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ, - Stage stage) { + Stage stage, bool integralOnly = false) { // Coerce child to any data type; child is self-determined // i.e. isolated from expected type. // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs) @@ -7341,7 +7345,8 @@ class WidthVisitor final : public VNVisitor { if (stage & PRELIM) { underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); } - underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr)) : underp; + underp + = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr), integralOnly) : underp; AstNodeDType* const expDTypep = underp->dtypep(); underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); (void)underp; // cppcheck @@ -7357,7 +7362,7 @@ class WidthVisitor final : public VNVisitor { if (stage & PRELIM) { underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); } - underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr)) : underp; + underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr), false) : underp; AstNodeDType* const expDTypep = underp->dtypep(); underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); AstNodeDType* const checkDtp = expDTypep->skipRefToEnump(); @@ -7674,11 +7679,15 @@ class WidthVisitor final : public VNVisitor { //---------------------------------------------------------------------- // SIGNED/DOUBLE METHODS - AstNodeExpr* checkCvtUS(AstNodeExpr* nodep) { + AstNodeExpr* checkCvtUS(AstNodeExpr* nodep, bool fatal) { if (nodep && nodep->dtypep()->skipRefp()->isDouble()) { - nodep->v3warn(REALCVT, - "Implicit conversion of real to integer; expected integral input to " - << nodep->backp()->prettyTypeName()); + if (fatal) { + nodep->v3error("Expected integral input to " << nodep->backp()->prettyTypeName()); + } else { + nodep->v3warn(REALCVT, + "Implicit conversion of real to integer; expected integral input to " + << nodep->backp()->prettyTypeName()); + } nodep = spliceCvtS(nodep, false, 32); } return nodep; diff --git a/test_regress/t/t_lint_realcvt_bad.out b/test_regress/t/t_lint_realcvt_bad.out index b68e7e362..d4cc2c9a6 100644 --- a/test_regress/t/t_lint_realcvt_bad.out +++ b/test_regress/t/t_lint_realcvt_bad.out @@ -1,15 +1,46 @@ -%Warning-REALCVT: t/t_lint_realcvt_bad.v:12:18: Implicit conversion of real to integer - 12 | time t_bad1 = 9.001ns; - | ^~~~~~~ +%Warning-REALCVT: t/t_lint_realcvt_bad.v:12:17: Implicit conversion of real to integer + 12 | time t_bad1 = 9.001ns; + | ^~~~~~~ ... For warning description see https://verilator.org/warn/REALCVT?v=latest ... Use "/* verilator lint_off REALCVT */" and lint_on around source to disable this message. -%Warning-REALCVT: t/t_lint_realcvt_bad.v:13:18: Implicit conversion of real to integer - 13 | time t_bad2 = 9.999ns; - | ^~~~~~~ -%Warning-REALCVT: t/t_lint_realcvt_bad.v:17:18: Implicit conversion of real to integer - 17 | time t_bad3 = 9ps; - | ^~~ -%Warning-REALCVT: t/t_lint_realcvt_bad.v:23:22: Implicit conversion of real to integer - 23 | integer i_bad21 = 23.1; - | ^~~~ +%Warning-REALCVT: t/t_lint_realcvt_bad.v:13:17: Implicit conversion of real to integer + 13 | time t_bad2 = 9.999ns; + | ^~~~~~~ +%Warning-REALCVT: t/t_lint_realcvt_bad.v:17:17: Implicit conversion of real to integer + 17 | time t_bad3 = 9ps; + | ^~~ +%Warning-REALCVT: t/t_lint_realcvt_bad.v:23:21: Implicit conversion of real to integer + 23 | integer i_bad21 = 23.1; + | ^~~~ +%Error: t/t_lint_realcvt_bad.v:27:17: Expected integral input to SIGNED + : ... note: In instance 'sub' + 27 | i = $signed(1.0); + | ^~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_lint_realcvt_bad.v:28:19: Expected integral input to UNSIGNED + : ... note: In instance 'sub' + 28 | i = $unsigned(1.0); + | ^~~ +%Error: t/t_lint_realcvt_bad.v:29:10: Expected integral input to CONCAT + : ... note: In instance 'sub' + 29 | i = {1.2, 1.3}; + | ^~~ +%Error: t/t_lint_realcvt_bad.v:29:15: Expected integral input to CONCAT + : ... note: In instance 'sub' + 29 | i = {1.2, 1.3}; + | ^~~ +%Warning-WIDTHTRUNC: t/t_lint_realcvt_bad.v:29:7: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's REPLICATE generates 64 bits. + : ... note: In instance 'sub' + 29 | i = {1.2, 1.3}; + | ^ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Error: t/t_lint_realcvt_bad.v:30:12: Expected integral input to REPLICATE + : ... note: In instance 'sub' + 30 | i = {6{1.2}}; + | ^~~ +%Warning-WIDTHTRUNC: t/t_lint_realcvt_bad.v:30:7: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's REPLICATE generates 192 bits. + : ... note: In instance 'sub' + 30 | i = {6{1.2}}; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_lint_realcvt_bad.v b/test_regress/t/t_lint_realcvt_bad.v index daefdda26..819f218ab 100644 --- a/test_regress/t/t_lint_realcvt_bad.v +++ b/test_regress/t/t_lint_realcvt_bad.v @@ -4,22 +4,30 @@ // any use, without warranty, 2011 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -`timescale 1ns/1ps +`timescale 1ns / 1ps module sub; - time t_ok1 = 9ns; // > 1ns units - time t_bad1 = 9.001ns; // < 1ns units - time t_bad2 = 9.999ns; // < 1ns units + time t_ok1 = 9ns; // > 1ns units + time t_bad1 = 9.001ns; // < 1ns units + time t_bad2 = 9.999ns; // < 1ns units - time t_ok2 = 9.001us; // > 1ns units + time t_ok2 = 9.001us; // > 1ns units - time t_bad3 = 9ps; // < 1ns units + time t_bad3 = 9ps; // < 1ns units - realtime rt_ok10 = 9.001ns; // < 1ns units - realtime rt_ok11 = 9ps; // < 1ns units + realtime rt_ok10 = 9.001ns; // < 1ns units + realtime rt_ok11 = 9ps; // < 1ns units - integer i_ok20 = 23.0; // No warning - integer i_bad21 = 23.1; + integer i_ok20 = 23.0; // No warning + integer i_bad21 = 23.1; + + initial begin + int i; + i = $signed(1.0); // Error, doesn't support real, not just warning + i = $unsigned(1.0); // Error, doesn't support real, not just warning + i = {1.2, 1.3}; // Error, doesn't support real, not just warning + i = {6{1.2}}; // Error, doesn't support real, not just warning + end endmodule From fb1373b85462b8bd508334c96b0f75eaeb072fe8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 16 Jul 2025 21:31:40 -0400 Subject: [PATCH 035/252] Tests: Reformat. Ignore whitespace if comparing. No test functional change. --- test_regress/t/t_case_huge_sub.v | 560 ++++++++++--------- test_regress/t/t_case_huge_sub2.v | 558 ++++++++++--------- test_regress/t/t_case_huge_sub3.v | 558 ++++++++++--------- test_regress/t/t_case_huge_sub4.v | 101 ++-- test_regress/t/t_case_unique_many.v | 636 +++++++++++----------- test_regress/t/t_if_deep.v | 222 ++++---- test_regress/t/t_math_concat64.v | 217 ++++---- test_regress/t/t_math_cond_huge.v | 778 ++++++++++++++------------- test_regress/t/t_opt_localize_deep.v | 132 ++--- 9 files changed, 1877 insertions(+), 1885 deletions(-) diff --git a/test_regress/t/t_case_huge_sub.v b/test_regress/t/t_case_huge_sub.v index b0d8e72c5..34d8514d5 100644 --- a/test_regress/t/t_case_huge_sub.v +++ b/test_regress/t/t_case_huge_sub.v @@ -5,290 +5,284 @@ // SPDX-License-Identifier: CC0-1.0 module t_case_huge_sub (/*AUTOARG*/ - // Outputs - outa, outb, outc, - // Inputs - index - ); + // Outputs + outa, outb, outc, + // Inputs + index + ); - input [7:0] index; - output [9:0] outa; - output [1:0] outb; - output outc; + input [7:0] index; + output logic [9:0] outa; + output logic [1:0] outb; + output logic outc; - // ============================= - /*AUTOREG*/ - // Beginning of automatic regs (for this module's undeclared outputs) - reg [9:0] outa; - reg [1:0] outb; - reg outc; - // End of automatics + // ============================= + // Created from Python3: + // for i in range(1024): + // print(" 10'h%03x: begin outa = 10'h%03x; outb = 2'b%d%d; outc = 1'b%d; end" + // % (i, random.randint(0,1024), random.randint(0,1), + // random.randint(0,1), random.randint(0,1))) - // ============================= - // Created from Python3: - // for i in range(1024): - // print(" 10'h%03x: begin outa = 10'h%03x; outb = 2'b%d%d; outc = 1'b%d; end" - // % (i, random.randint(0,1024), random.randint(0,1), - // random.randint(0,1), random.randint(0,1))) - - always @(/*AS*/index) begin - case (index) - 8'h00: begin outa = 10'h152; outb = 2'b00; outc = 1'b1; end - 8'h01: begin outa = 10'h318; outb = 2'b11; outc = 1'b1; end - 8'h02: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end - 8'h03: begin outa = 10'h392; outb = 2'b01; outc = 1'b1; end - 8'h04: begin outa = 10'h1ef; outb = 2'b00; outc = 1'b0; end - 8'h05: begin outa = 10'h06c; outb = 2'b10; outc = 1'b1; end - 8'h06: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end - 8'h07: begin outa = 10'h29a; outb = 2'b10; outc = 1'b0; end - 8'h08: begin outa = 10'h3ce; outb = 2'b01; outc = 1'b0; end - 8'h09: begin outa = 10'h37c; outb = 2'b01; outc = 1'b0; end - 8'h0a: begin outa = 10'h058; outb = 2'b10; outc = 1'b0; end - 8'h0b: begin outa = 10'h3b2; outb = 2'b01; outc = 1'b1; end - 8'h0c: begin outa = 10'h36f; outb = 2'b11; outc = 1'b0; end - 8'h0d: begin outa = 10'h2c5; outb = 2'b11; outc = 1'b0; end - 8'h0e: begin outa = 10'h23a; outb = 2'b00; outc = 1'b0; end - 8'h0f: begin outa = 10'h222; outb = 2'b01; outc = 1'b1; end - 8'h10: begin outa = 10'h328; outb = 2'b00; outc = 1'b1; end - 8'h11: begin outa = 10'h3c3; outb = 2'b00; outc = 1'b1; end - 8'h12: begin outa = 10'h12c; outb = 2'b01; outc = 1'b0; end - 8'h13: begin outa = 10'h1d0; outb = 2'b00; outc = 1'b1; end - 8'h14: begin outa = 10'h3ff; outb = 2'b01; outc = 1'b1; end - 8'h15: begin outa = 10'h115; outb = 2'b11; outc = 1'b1; end - 8'h16: begin outa = 10'h3ba; outb = 2'b10; outc = 1'b0; end - 8'h17: begin outa = 10'h3ba; outb = 2'b00; outc = 1'b0; end - 8'h18: begin outa = 10'h10d; outb = 2'b00; outc = 1'b1; end - 8'h19: begin outa = 10'h13b; outb = 2'b01; outc = 1'b1; end - 8'h1a: begin outa = 10'h0a0; outb = 2'b10; outc = 1'b1; end - 8'h1b: begin outa = 10'h264; outb = 2'b11; outc = 1'b0; end - 8'h1c: begin outa = 10'h3a2; outb = 2'b10; outc = 1'b0; end - 8'h1d: begin outa = 10'h07c; outb = 2'b00; outc = 1'b1; end - 8'h1e: begin outa = 10'h291; outb = 2'b00; outc = 1'b0; end - 8'h1f: begin outa = 10'h1d1; outb = 2'b10; outc = 1'b0; end - 8'h20: begin outa = 10'h354; outb = 2'b11; outc = 1'b1; end - 8'h21: begin outa = 10'h0c0; outb = 2'b00; outc = 1'b1; end - 8'h22: begin outa = 10'h191; outb = 2'b00; outc = 1'b0; end - 8'h23: begin outa = 10'h379; outb = 2'b01; outc = 1'b0; end - 8'h24: begin outa = 10'h073; outb = 2'b00; outc = 1'b0; end - 8'h25: begin outa = 10'h2fd; outb = 2'b11; outc = 1'b1; end - 8'h26: begin outa = 10'h2e0; outb = 2'b11; outc = 1'b1; end - 8'h27: begin outa = 10'h337; outb = 2'b01; outc = 1'b1; end - 8'h28: begin outa = 10'h2c7; outb = 2'b11; outc = 1'b1; end - 8'h29: begin outa = 10'h19e; outb = 2'b11; outc = 1'b0; end - 8'h2a: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end - 8'h2b: begin outa = 10'h06a; outb = 2'b01; outc = 1'b1; end - 8'h2c: begin outa = 10'h1c7; outb = 2'b01; outc = 1'b1; end - 8'h2d: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end - 8'h2e: begin outa = 10'h0cf; outb = 2'b01; outc = 1'b1; end - 8'h2f: begin outa = 10'h009; outb = 2'b11; outc = 1'b1; end - 8'h30: begin outa = 10'h09d; outb = 2'b00; outc = 1'b1; end - 8'h31: begin outa = 10'h28e; outb = 2'b00; outc = 1'b0; end - 8'h32: begin outa = 10'h010; outb = 2'b01; outc = 1'b0; end - 8'h33: begin outa = 10'h1e0; outb = 2'b10; outc = 1'b0; end - 8'h34: begin outa = 10'h079; outb = 2'b01; outc = 1'b1; end - 8'h35: begin outa = 10'h13e; outb = 2'b10; outc = 1'b1; end - 8'h36: begin outa = 10'h282; outb = 2'b11; outc = 1'b0; end - 8'h37: begin outa = 10'h21c; outb = 2'b11; outc = 1'b1; end - 8'h38: begin outa = 10'h148; outb = 2'b00; outc = 1'b1; end - 8'h39: begin outa = 10'h3c0; outb = 2'b10; outc = 1'b0; end - 8'h3a: begin outa = 10'h176; outb = 2'b01; outc = 1'b1; end - 8'h3b: begin outa = 10'h3fc; outb = 2'b10; outc = 1'b1; end - 8'h3c: begin outa = 10'h295; outb = 2'b11; outc = 1'b1; end - 8'h3d: begin outa = 10'h113; outb = 2'b10; outc = 1'b1; end - 8'h3e: begin outa = 10'h354; outb = 2'b01; outc = 1'b1; end - 8'h3f: begin outa = 10'h0db; outb = 2'b11; outc = 1'b0; end - 8'h40: begin outa = 10'h238; outb = 2'b01; outc = 1'b0; end - 8'h41: begin outa = 10'h12b; outb = 2'b01; outc = 1'b1; end - 8'h42: begin outa = 10'h1dc; outb = 2'b10; outc = 1'b0; end - 8'h43: begin outa = 10'h137; outb = 2'b01; outc = 1'b1; end - 8'h44: begin outa = 10'h1e2; outb = 2'b01; outc = 1'b1; end - 8'h45: begin outa = 10'h3d5; outb = 2'b11; outc = 1'b1; end - 8'h46: begin outa = 10'h30c; outb = 2'b11; outc = 1'b0; end - 8'h47: begin outa = 10'h298; outb = 2'b11; outc = 1'b0; end - 8'h48: begin outa = 10'h080; outb = 2'b00; outc = 1'b1; end - 8'h49: begin outa = 10'h35a; outb = 2'b11; outc = 1'b1; end - 8'h4a: begin outa = 10'h01b; outb = 2'b00; outc = 1'b0; end - 8'h4b: begin outa = 10'h0a3; outb = 2'b11; outc = 1'b0; end - 8'h4c: begin outa = 10'h0b3; outb = 2'b11; outc = 1'b1; end - 8'h4d: begin outa = 10'h17a; outb = 2'b00; outc = 1'b0; end - 8'h4e: begin outa = 10'h3ae; outb = 2'b11; outc = 1'b0; end - 8'h4f: begin outa = 10'h078; outb = 2'b11; outc = 1'b0; end - 8'h50: begin outa = 10'h322; outb = 2'b00; outc = 1'b1; end - 8'h51: begin outa = 10'h213; outb = 2'b11; outc = 1'b0; end - 8'h52: begin outa = 10'h11a; outb = 2'b11; outc = 1'b0; end - 8'h53: begin outa = 10'h1a7; outb = 2'b00; outc = 1'b0; end - 8'h54: begin outa = 10'h35a; outb = 2'b00; outc = 1'b1; end - 8'h55: begin outa = 10'h233; outb = 2'b00; outc = 1'b0; end - 8'h56: begin outa = 10'h01d; outb = 2'b01; outc = 1'b1; end - 8'h57: begin outa = 10'h2d5; outb = 2'b00; outc = 1'b0; end - 8'h58: begin outa = 10'h1a0; outb = 2'b00; outc = 1'b1; end - 8'h59: begin outa = 10'h3d0; outb = 2'b00; outc = 1'b1; end - 8'h5a: begin outa = 10'h181; outb = 2'b01; outc = 1'b1; end - 8'h5b: begin outa = 10'h219; outb = 2'b01; outc = 1'b1; end - 8'h5c: begin outa = 10'h26a; outb = 2'b01; outc = 1'b1; end - 8'h5d: begin outa = 10'h050; outb = 2'b10; outc = 1'b0; end - 8'h5e: begin outa = 10'h189; outb = 2'b10; outc = 1'b0; end - 8'h5f: begin outa = 10'h1eb; outb = 2'b01; outc = 1'b1; end - 8'h60: begin outa = 10'h224; outb = 2'b00; outc = 1'b1; end - 8'h61: begin outa = 10'h2fe; outb = 2'b00; outc = 1'b0; end - 8'h62: begin outa = 10'h0ae; outb = 2'b00; outc = 1'b1; end - 8'h63: begin outa = 10'h1cd; outb = 2'b00; outc = 1'b0; end - 8'h64: begin outa = 10'h273; outb = 2'b10; outc = 1'b1; end - 8'h65: begin outa = 10'h268; outb = 2'b10; outc = 1'b0; end - 8'h66: begin outa = 10'h111; outb = 2'b01; outc = 1'b0; end - 8'h67: begin outa = 10'h1f9; outb = 2'b00; outc = 1'b0; end - 8'h68: begin outa = 10'h232; outb = 2'b00; outc = 1'b1; end - 8'h69: begin outa = 10'h255; outb = 2'b11; outc = 1'b0; end - 8'h6a: begin outa = 10'h34c; outb = 2'b01; outc = 1'b1; end - 8'h6b: begin outa = 10'h049; outb = 2'b01; outc = 1'b1; end - 8'h6c: begin outa = 10'h197; outb = 2'b11; outc = 1'b0; end - 8'h6d: begin outa = 10'h0fe; outb = 2'b11; outc = 1'b0; end - 8'h6e: begin outa = 10'h253; outb = 2'b01; outc = 1'b1; end - 8'h6f: begin outa = 10'h2de; outb = 2'b11; outc = 1'b0; end - 8'h70: begin outa = 10'h13b; outb = 2'b10; outc = 1'b1; end - 8'h71: begin outa = 10'h040; outb = 2'b10; outc = 1'b0; end - 8'h72: begin outa = 10'h0b4; outb = 2'b00; outc = 1'b1; end - 8'h73: begin outa = 10'h233; outb = 2'b11; outc = 1'b1; end - 8'h74: begin outa = 10'h198; outb = 2'b00; outc = 1'b1; end - 8'h75: begin outa = 10'h018; outb = 2'b00; outc = 1'b1; end - 8'h76: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end - 8'h77: begin outa = 10'h134; outb = 2'b11; outc = 1'b0; end - 8'h78: begin outa = 10'h1ca; outb = 2'b10; outc = 1'b0; end - 8'h79: begin outa = 10'h286; outb = 2'b10; outc = 1'b1; end - 8'h7a: begin outa = 10'h0e6; outb = 2'b11; outc = 1'b1; end - 8'h7b: begin outa = 10'h064; outb = 2'b10; outc = 1'b1; end - 8'h7c: begin outa = 10'h257; outb = 2'b00; outc = 1'b1; end - 8'h7d: begin outa = 10'h31a; outb = 2'b10; outc = 1'b1; end - 8'h7e: begin outa = 10'h247; outb = 2'b01; outc = 1'b0; end - 8'h7f: begin outa = 10'h299; outb = 2'b00; outc = 1'b0; end - 8'h80: begin outa = 10'h02c; outb = 2'b00; outc = 1'b0; end - 8'h81: begin outa = 10'h2bb; outb = 2'b11; outc = 1'b0; end - 8'h82: begin outa = 10'h180; outb = 2'b10; outc = 1'b0; end - 8'h83: begin outa = 10'h245; outb = 2'b01; outc = 1'b1; end - 8'h84: begin outa = 10'h0da; outb = 2'b10; outc = 1'b0; end - 8'h85: begin outa = 10'h367; outb = 2'b10; outc = 1'b0; end - 8'h86: begin outa = 10'h304; outb = 2'b01; outc = 1'b0; end - 8'h87: begin outa = 10'h38b; outb = 2'b11; outc = 1'b0; end - 8'h88: begin outa = 10'h09f; outb = 2'b01; outc = 1'b0; end - 8'h89: begin outa = 10'h1f0; outb = 2'b10; outc = 1'b1; end - 8'h8a: begin outa = 10'h281; outb = 2'b10; outc = 1'b1; end - 8'h8b: begin outa = 10'h019; outb = 2'b00; outc = 1'b0; end - 8'h8c: begin outa = 10'h1f2; outb = 2'b10; outc = 1'b0; end - 8'h8d: begin outa = 10'h0b1; outb = 2'b01; outc = 1'b1; end - 8'h8e: begin outa = 10'h058; outb = 2'b01; outc = 1'b1; end - 8'h8f: begin outa = 10'h39b; outb = 2'b00; outc = 1'b1; end - 8'h90: begin outa = 10'h2ec; outb = 2'b10; outc = 1'b1; end - 8'h91: begin outa = 10'h250; outb = 2'b00; outc = 1'b1; end - 8'h92: begin outa = 10'h3f4; outb = 2'b10; outc = 1'b1; end - 8'h93: begin outa = 10'h057; outb = 2'b10; outc = 1'b1; end - 8'h94: begin outa = 10'h18f; outb = 2'b01; outc = 1'b1; end - 8'h95: begin outa = 10'h105; outb = 2'b01; outc = 1'b1; end - 8'h96: begin outa = 10'h1ae; outb = 2'b00; outc = 1'b1; end - 8'h97: begin outa = 10'h04e; outb = 2'b10; outc = 1'b0; end - 8'h98: begin outa = 10'h240; outb = 2'b11; outc = 1'b0; end - 8'h99: begin outa = 10'h3e4; outb = 2'b01; outc = 1'b0; end - 8'h9a: begin outa = 10'h3c6; outb = 2'b01; outc = 1'b0; end - 8'h9b: begin outa = 10'h109; outb = 2'b00; outc = 1'b1; end - 8'h9c: begin outa = 10'h073; outb = 2'b10; outc = 1'b1; end - 8'h9d: begin outa = 10'h19f; outb = 2'b01; outc = 1'b0; end - 8'h9e: begin outa = 10'h3b8; outb = 2'b01; outc = 1'b0; end - 8'h9f: begin outa = 10'h00e; outb = 2'b00; outc = 1'b1; end - 8'ha0: begin outa = 10'h1b3; outb = 2'b11; outc = 1'b1; end - 8'ha1: begin outa = 10'h2bd; outb = 2'b11; outc = 1'b0; end - 8'ha2: begin outa = 10'h324; outb = 2'b00; outc = 1'b1; end - 8'ha3: begin outa = 10'h343; outb = 2'b10; outc = 1'b0; end - 8'ha4: begin outa = 10'h1c9; outb = 2'b01; outc = 1'b0; end - 8'ha5: begin outa = 10'h185; outb = 2'b00; outc = 1'b1; end - 8'ha6: begin outa = 10'h37a; outb = 2'b00; outc = 1'b1; end - 8'ha7: begin outa = 10'h0e0; outb = 2'b01; outc = 1'b1; end - 8'ha8: begin outa = 10'h0a3; outb = 2'b10; outc = 1'b0; end - 8'ha9: begin outa = 10'h019; outb = 2'b11; outc = 1'b0; end - 8'haa: begin outa = 10'h099; outb = 2'b00; outc = 1'b1; end - 8'hab: begin outa = 10'h376; outb = 2'b01; outc = 1'b1; end - 8'hac: begin outa = 10'h077; outb = 2'b00; outc = 1'b1; end - 8'had: begin outa = 10'h2b1; outb = 2'b11; outc = 1'b1; end - 8'hae: begin outa = 10'h27f; outb = 2'b00; outc = 1'b0; end - 8'haf: begin outa = 10'h265; outb = 2'b11; outc = 1'b0; end - 8'hb0: begin outa = 10'h156; outb = 2'b10; outc = 1'b1; end - 8'hb1: begin outa = 10'h1ce; outb = 2'b00; outc = 1'b0; end - 8'hb2: begin outa = 10'h008; outb = 2'b01; outc = 1'b0; end - 8'hb3: begin outa = 10'h12e; outb = 2'b11; outc = 1'b1; end - 8'hb4: begin outa = 10'h199; outb = 2'b11; outc = 1'b0; end - 8'hb5: begin outa = 10'h330; outb = 2'b10; outc = 1'b0; end - 8'hb6: begin outa = 10'h1ab; outb = 2'b01; outc = 1'b1; end - 8'hb7: begin outa = 10'h3bd; outb = 2'b00; outc = 1'b0; end - 8'hb8: begin outa = 10'h0ca; outb = 2'b10; outc = 1'b0; end - 8'hb9: begin outa = 10'h367; outb = 2'b00; outc = 1'b0; end - 8'hba: begin outa = 10'h334; outb = 2'b00; outc = 1'b0; end - 8'hbb: begin outa = 10'h040; outb = 2'b00; outc = 1'b1; end - 8'hbc: begin outa = 10'h1a7; outb = 2'b10; outc = 1'b1; end - 8'hbd: begin outa = 10'h036; outb = 2'b11; outc = 1'b1; end - 8'hbe: begin outa = 10'h223; outb = 2'b11; outc = 1'b1; end - 8'hbf: begin outa = 10'h075; outb = 2'b01; outc = 1'b0; end - 8'hc0: begin outa = 10'h3c4; outb = 2'b00; outc = 1'b1; end - 8'hc1: begin outa = 10'h2cc; outb = 2'b01; outc = 1'b0; end - 8'hc2: begin outa = 10'h123; outb = 2'b01; outc = 1'b0; end - 8'hc3: begin outa = 10'h3fd; outb = 2'b01; outc = 1'b1; end - 8'hc4: begin outa = 10'h11e; outb = 2'b00; outc = 1'b0; end - 8'hc5: begin outa = 10'h27c; outb = 2'b11; outc = 1'b1; end - 8'hc6: begin outa = 10'h1e2; outb = 2'b11; outc = 1'b0; end - 8'hc7: begin outa = 10'h377; outb = 2'b11; outc = 1'b0; end - 8'hc8: begin outa = 10'h33a; outb = 2'b11; outc = 1'b0; end - 8'hc9: begin outa = 10'h32d; outb = 2'b11; outc = 1'b1; end - 8'hca: begin outa = 10'h014; outb = 2'b11; outc = 1'b0; end - 8'hcb: begin outa = 10'h332; outb = 2'b10; outc = 1'b0; end - 8'hcc: begin outa = 10'h359; outb = 2'b00; outc = 1'b0; end - 8'hcd: begin outa = 10'h0a4; outb = 2'b10; outc = 1'b1; end - 8'hce: begin outa = 10'h348; outb = 2'b00; outc = 1'b1; end - 8'hcf: begin outa = 10'h04b; outb = 2'b11; outc = 1'b1; end - 8'hd0: begin outa = 10'h147; outb = 2'b10; outc = 1'b1; end - 8'hd1: begin outa = 10'h026; outb = 2'b00; outc = 1'b1; end - 8'hd2: begin outa = 10'h103; outb = 2'b00; outc = 1'b0; end - 8'hd3: begin outa = 10'h106; outb = 2'b00; outc = 1'b1; end - 8'hd4: begin outa = 10'h35a; outb = 2'b00; outc = 1'b0; end - 8'hd5: begin outa = 10'h254; outb = 2'b01; outc = 1'b0; end - 8'hd6: begin outa = 10'h0cd; outb = 2'b01; outc = 1'b0; end - 8'hd7: begin outa = 10'h17c; outb = 2'b11; outc = 1'b1; end - 8'hd8: begin outa = 10'h37e; outb = 2'b10; outc = 1'b1; end - 8'hd9: begin outa = 10'h0a9; outb = 2'b11; outc = 1'b1; end - 8'hda: begin outa = 10'h0fe; outb = 2'b01; outc = 1'b0; end - 8'hdb: begin outa = 10'h3c0; outb = 2'b11; outc = 1'b1; end - 8'hdc: begin outa = 10'h1d9; outb = 2'b10; outc = 1'b1; end - 8'hdd: begin outa = 10'h10e; outb = 2'b00; outc = 1'b1; end - 8'hde: begin outa = 10'h394; outb = 2'b01; outc = 1'b0; end - 8'hdf: begin outa = 10'h316; outb = 2'b01; outc = 1'b0; end - 8'he0: begin outa = 10'h05b; outb = 2'b11; outc = 1'b0; end - 8'he1: begin outa = 10'h126; outb = 2'b01; outc = 1'b1; end - 8'he2: begin outa = 10'h369; outb = 2'b11; outc = 1'b0; end - 8'he3: begin outa = 10'h291; outb = 2'b10; outc = 1'b1; end - 8'he4: begin outa = 10'h2ca; outb = 2'b00; outc = 1'b1; end - 8'he5: begin outa = 10'h25b; outb = 2'b01; outc = 1'b1; end - 8'he6: begin outa = 10'h106; outb = 2'b00; outc = 1'b0; end - 8'he7: begin outa = 10'h172; outb = 2'b11; outc = 1'b1; end - 8'he8: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end - 8'he9: begin outa = 10'h2d3; outb = 2'b11; outc = 1'b1; end - 8'hea: begin outa = 10'h182; outb = 2'b00; outc = 1'b0; end - 8'heb: begin outa = 10'h327; outb = 2'b00; outc = 1'b1; end - 8'hec: begin outa = 10'h1d0; outb = 2'b10; outc = 1'b0; end - 8'hed: begin outa = 10'h204; outb = 2'b00; outc = 1'b1; end - 8'hee: begin outa = 10'h11f; outb = 2'b00; outc = 1'b1; end - 8'hef: begin outa = 10'h365; outb = 2'b11; outc = 1'b1; end - 8'hf0: begin outa = 10'h2c2; outb = 2'b01; outc = 1'b1; end - 8'hf1: begin outa = 10'h2b5; outb = 2'b10; outc = 1'b0; end - 8'hf2: begin outa = 10'h1f8; outb = 2'b10; outc = 1'b1; end - 8'hf3: begin outa = 10'h2a7; outb = 2'b01; outc = 1'b1; end - 8'hf4: begin outa = 10'h1be; outb = 2'b10; outc = 1'b1; end - 8'hf5: begin outa = 10'h25e; outb = 2'b10; outc = 1'b1; end - 8'hf6: begin outa = 10'h032; outb = 2'b10; outc = 1'b0; end - 8'hf7: begin outa = 10'h2ef; outb = 2'b00; outc = 1'b0; end - 8'hf8: begin outa = 10'h02f; outb = 2'b00; outc = 1'b1; end - 8'hf9: begin outa = 10'h201; outb = 2'b10; outc = 1'b0; end - 8'hfa: begin outa = 10'h054; outb = 2'b01; outc = 1'b1; end - 8'hfb: begin outa = 10'h013; outb = 2'b10; outc = 1'b0; end - 8'hfc: begin outa = 10'h249; outb = 2'b01; outc = 1'b0; end - 8'hfd: begin outa = 10'h09a; outb = 2'b10; outc = 1'b0; end - 8'hfe: begin outa = 10'h012; outb = 2'b00; outc = 1'b0; end - 8'hff: begin outa = 10'h114; outb = 2'b10; outc = 1'b1; end - endcase - end + always @* begin + // verilog_format: off + case (index) + 8'h00: begin outa = 10'h152; outb = 2'b00; outc = 1'b1; end + 8'h01: begin outa = 10'h318; outb = 2'b11; outc = 1'b1; end + 8'h02: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end + 8'h03: begin outa = 10'h392; outb = 2'b01; outc = 1'b1; end + 8'h04: begin outa = 10'h1ef; outb = 2'b00; outc = 1'b0; end + 8'h05: begin outa = 10'h06c; outb = 2'b10; outc = 1'b1; end + 8'h06: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end + 8'h07: begin outa = 10'h29a; outb = 2'b10; outc = 1'b0; end + 8'h08: begin outa = 10'h3ce; outb = 2'b01; outc = 1'b0; end + 8'h09: begin outa = 10'h37c; outb = 2'b01; outc = 1'b0; end + 8'h0a: begin outa = 10'h058; outb = 2'b10; outc = 1'b0; end + 8'h0b: begin outa = 10'h3b2; outb = 2'b01; outc = 1'b1; end + 8'h0c: begin outa = 10'h36f; outb = 2'b11; outc = 1'b0; end + 8'h0d: begin outa = 10'h2c5; outb = 2'b11; outc = 1'b0; end + 8'h0e: begin outa = 10'h23a; outb = 2'b00; outc = 1'b0; end + 8'h0f: begin outa = 10'h222; outb = 2'b01; outc = 1'b1; end + 8'h10: begin outa = 10'h328; outb = 2'b00; outc = 1'b1; end + 8'h11: begin outa = 10'h3c3; outb = 2'b00; outc = 1'b1; end + 8'h12: begin outa = 10'h12c; outb = 2'b01; outc = 1'b0; end + 8'h13: begin outa = 10'h1d0; outb = 2'b00; outc = 1'b1; end + 8'h14: begin outa = 10'h3ff; outb = 2'b01; outc = 1'b1; end + 8'h15: begin outa = 10'h115; outb = 2'b11; outc = 1'b1; end + 8'h16: begin outa = 10'h3ba; outb = 2'b10; outc = 1'b0; end + 8'h17: begin outa = 10'h3ba; outb = 2'b00; outc = 1'b0; end + 8'h18: begin outa = 10'h10d; outb = 2'b00; outc = 1'b1; end + 8'h19: begin outa = 10'h13b; outb = 2'b01; outc = 1'b1; end + 8'h1a: begin outa = 10'h0a0; outb = 2'b10; outc = 1'b1; end + 8'h1b: begin outa = 10'h264; outb = 2'b11; outc = 1'b0; end + 8'h1c: begin outa = 10'h3a2; outb = 2'b10; outc = 1'b0; end + 8'h1d: begin outa = 10'h07c; outb = 2'b00; outc = 1'b1; end + 8'h1e: begin outa = 10'h291; outb = 2'b00; outc = 1'b0; end + 8'h1f: begin outa = 10'h1d1; outb = 2'b10; outc = 1'b0; end + 8'h20: begin outa = 10'h354; outb = 2'b11; outc = 1'b1; end + 8'h21: begin outa = 10'h0c0; outb = 2'b00; outc = 1'b1; end + 8'h22: begin outa = 10'h191; outb = 2'b00; outc = 1'b0; end + 8'h23: begin outa = 10'h379; outb = 2'b01; outc = 1'b0; end + 8'h24: begin outa = 10'h073; outb = 2'b00; outc = 1'b0; end + 8'h25: begin outa = 10'h2fd; outb = 2'b11; outc = 1'b1; end + 8'h26: begin outa = 10'h2e0; outb = 2'b11; outc = 1'b1; end + 8'h27: begin outa = 10'h337; outb = 2'b01; outc = 1'b1; end + 8'h28: begin outa = 10'h2c7; outb = 2'b11; outc = 1'b1; end + 8'h29: begin outa = 10'h19e; outb = 2'b11; outc = 1'b0; end + 8'h2a: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end + 8'h2b: begin outa = 10'h06a; outb = 2'b01; outc = 1'b1; end + 8'h2c: begin outa = 10'h1c7; outb = 2'b01; outc = 1'b1; end + 8'h2d: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end + 8'h2e: begin outa = 10'h0cf; outb = 2'b01; outc = 1'b1; end + 8'h2f: begin outa = 10'h009; outb = 2'b11; outc = 1'b1; end + 8'h30: begin outa = 10'h09d; outb = 2'b00; outc = 1'b1; end + 8'h31: begin outa = 10'h28e; outb = 2'b00; outc = 1'b0; end + 8'h32: begin outa = 10'h010; outb = 2'b01; outc = 1'b0; end + 8'h33: begin outa = 10'h1e0; outb = 2'b10; outc = 1'b0; end + 8'h34: begin outa = 10'h079; outb = 2'b01; outc = 1'b1; end + 8'h35: begin outa = 10'h13e; outb = 2'b10; outc = 1'b1; end + 8'h36: begin outa = 10'h282; outb = 2'b11; outc = 1'b0; end + 8'h37: begin outa = 10'h21c; outb = 2'b11; outc = 1'b1; end + 8'h38: begin outa = 10'h148; outb = 2'b00; outc = 1'b1; end + 8'h39: begin outa = 10'h3c0; outb = 2'b10; outc = 1'b0; end + 8'h3a: begin outa = 10'h176; outb = 2'b01; outc = 1'b1; end + 8'h3b: begin outa = 10'h3fc; outb = 2'b10; outc = 1'b1; end + 8'h3c: begin outa = 10'h295; outb = 2'b11; outc = 1'b1; end + 8'h3d: begin outa = 10'h113; outb = 2'b10; outc = 1'b1; end + 8'h3e: begin outa = 10'h354; outb = 2'b01; outc = 1'b1; end + 8'h3f: begin outa = 10'h0db; outb = 2'b11; outc = 1'b0; end + 8'h40: begin outa = 10'h238; outb = 2'b01; outc = 1'b0; end + 8'h41: begin outa = 10'h12b; outb = 2'b01; outc = 1'b1; end + 8'h42: begin outa = 10'h1dc; outb = 2'b10; outc = 1'b0; end + 8'h43: begin outa = 10'h137; outb = 2'b01; outc = 1'b1; end + 8'h44: begin outa = 10'h1e2; outb = 2'b01; outc = 1'b1; end + 8'h45: begin outa = 10'h3d5; outb = 2'b11; outc = 1'b1; end + 8'h46: begin outa = 10'h30c; outb = 2'b11; outc = 1'b0; end + 8'h47: begin outa = 10'h298; outb = 2'b11; outc = 1'b0; end + 8'h48: begin outa = 10'h080; outb = 2'b00; outc = 1'b1; end + 8'h49: begin outa = 10'h35a; outb = 2'b11; outc = 1'b1; end + 8'h4a: begin outa = 10'h01b; outb = 2'b00; outc = 1'b0; end + 8'h4b: begin outa = 10'h0a3; outb = 2'b11; outc = 1'b0; end + 8'h4c: begin outa = 10'h0b3; outb = 2'b11; outc = 1'b1; end + 8'h4d: begin outa = 10'h17a; outb = 2'b00; outc = 1'b0; end + 8'h4e: begin outa = 10'h3ae; outb = 2'b11; outc = 1'b0; end + 8'h4f: begin outa = 10'h078; outb = 2'b11; outc = 1'b0; end + 8'h50: begin outa = 10'h322; outb = 2'b00; outc = 1'b1; end + 8'h51: begin outa = 10'h213; outb = 2'b11; outc = 1'b0; end + 8'h52: begin outa = 10'h11a; outb = 2'b11; outc = 1'b0; end + 8'h53: begin outa = 10'h1a7; outb = 2'b00; outc = 1'b0; end + 8'h54: begin outa = 10'h35a; outb = 2'b00; outc = 1'b1; end + 8'h55: begin outa = 10'h233; outb = 2'b00; outc = 1'b0; end + 8'h56: begin outa = 10'h01d; outb = 2'b01; outc = 1'b1; end + 8'h57: begin outa = 10'h2d5; outb = 2'b00; outc = 1'b0; end + 8'h58: begin outa = 10'h1a0; outb = 2'b00; outc = 1'b1; end + 8'h59: begin outa = 10'h3d0; outb = 2'b00; outc = 1'b1; end + 8'h5a: begin outa = 10'h181; outb = 2'b01; outc = 1'b1; end + 8'h5b: begin outa = 10'h219; outb = 2'b01; outc = 1'b1; end + 8'h5c: begin outa = 10'h26a; outb = 2'b01; outc = 1'b1; end + 8'h5d: begin outa = 10'h050; outb = 2'b10; outc = 1'b0; end + 8'h5e: begin outa = 10'h189; outb = 2'b10; outc = 1'b0; end + 8'h5f: begin outa = 10'h1eb; outb = 2'b01; outc = 1'b1; end + 8'h60: begin outa = 10'h224; outb = 2'b00; outc = 1'b1; end + 8'h61: begin outa = 10'h2fe; outb = 2'b00; outc = 1'b0; end + 8'h62: begin outa = 10'h0ae; outb = 2'b00; outc = 1'b1; end + 8'h63: begin outa = 10'h1cd; outb = 2'b00; outc = 1'b0; end + 8'h64: begin outa = 10'h273; outb = 2'b10; outc = 1'b1; end + 8'h65: begin outa = 10'h268; outb = 2'b10; outc = 1'b0; end + 8'h66: begin outa = 10'h111; outb = 2'b01; outc = 1'b0; end + 8'h67: begin outa = 10'h1f9; outb = 2'b00; outc = 1'b0; end + 8'h68: begin outa = 10'h232; outb = 2'b00; outc = 1'b1; end + 8'h69: begin outa = 10'h255; outb = 2'b11; outc = 1'b0; end + 8'h6a: begin outa = 10'h34c; outb = 2'b01; outc = 1'b1; end + 8'h6b: begin outa = 10'h049; outb = 2'b01; outc = 1'b1; end + 8'h6c: begin outa = 10'h197; outb = 2'b11; outc = 1'b0; end + 8'h6d: begin outa = 10'h0fe; outb = 2'b11; outc = 1'b0; end + 8'h6e: begin outa = 10'h253; outb = 2'b01; outc = 1'b1; end + 8'h6f: begin outa = 10'h2de; outb = 2'b11; outc = 1'b0; end + 8'h70: begin outa = 10'h13b; outb = 2'b10; outc = 1'b1; end + 8'h71: begin outa = 10'h040; outb = 2'b10; outc = 1'b0; end + 8'h72: begin outa = 10'h0b4; outb = 2'b00; outc = 1'b1; end + 8'h73: begin outa = 10'h233; outb = 2'b11; outc = 1'b1; end + 8'h74: begin outa = 10'h198; outb = 2'b00; outc = 1'b1; end + 8'h75: begin outa = 10'h018; outb = 2'b00; outc = 1'b1; end + 8'h76: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end + 8'h77: begin outa = 10'h134; outb = 2'b11; outc = 1'b0; end + 8'h78: begin outa = 10'h1ca; outb = 2'b10; outc = 1'b0; end + 8'h79: begin outa = 10'h286; outb = 2'b10; outc = 1'b1; end + 8'h7a: begin outa = 10'h0e6; outb = 2'b11; outc = 1'b1; end + 8'h7b: begin outa = 10'h064; outb = 2'b10; outc = 1'b1; end + 8'h7c: begin outa = 10'h257; outb = 2'b00; outc = 1'b1; end + 8'h7d: begin outa = 10'h31a; outb = 2'b10; outc = 1'b1; end + 8'h7e: begin outa = 10'h247; outb = 2'b01; outc = 1'b0; end + 8'h7f: begin outa = 10'h299; outb = 2'b00; outc = 1'b0; end + 8'h80: begin outa = 10'h02c; outb = 2'b00; outc = 1'b0; end + 8'h81: begin outa = 10'h2bb; outb = 2'b11; outc = 1'b0; end + 8'h82: begin outa = 10'h180; outb = 2'b10; outc = 1'b0; end + 8'h83: begin outa = 10'h245; outb = 2'b01; outc = 1'b1; end + 8'h84: begin outa = 10'h0da; outb = 2'b10; outc = 1'b0; end + 8'h85: begin outa = 10'h367; outb = 2'b10; outc = 1'b0; end + 8'h86: begin outa = 10'h304; outb = 2'b01; outc = 1'b0; end + 8'h87: begin outa = 10'h38b; outb = 2'b11; outc = 1'b0; end + 8'h88: begin outa = 10'h09f; outb = 2'b01; outc = 1'b0; end + 8'h89: begin outa = 10'h1f0; outb = 2'b10; outc = 1'b1; end + 8'h8a: begin outa = 10'h281; outb = 2'b10; outc = 1'b1; end + 8'h8b: begin outa = 10'h019; outb = 2'b00; outc = 1'b0; end + 8'h8c: begin outa = 10'h1f2; outb = 2'b10; outc = 1'b0; end + 8'h8d: begin outa = 10'h0b1; outb = 2'b01; outc = 1'b1; end + 8'h8e: begin outa = 10'h058; outb = 2'b01; outc = 1'b1; end + 8'h8f: begin outa = 10'h39b; outb = 2'b00; outc = 1'b1; end + 8'h90: begin outa = 10'h2ec; outb = 2'b10; outc = 1'b1; end + 8'h91: begin outa = 10'h250; outb = 2'b00; outc = 1'b1; end + 8'h92: begin outa = 10'h3f4; outb = 2'b10; outc = 1'b1; end + 8'h93: begin outa = 10'h057; outb = 2'b10; outc = 1'b1; end + 8'h94: begin outa = 10'h18f; outb = 2'b01; outc = 1'b1; end + 8'h95: begin outa = 10'h105; outb = 2'b01; outc = 1'b1; end + 8'h96: begin outa = 10'h1ae; outb = 2'b00; outc = 1'b1; end + 8'h97: begin outa = 10'h04e; outb = 2'b10; outc = 1'b0; end + 8'h98: begin outa = 10'h240; outb = 2'b11; outc = 1'b0; end + 8'h99: begin outa = 10'h3e4; outb = 2'b01; outc = 1'b0; end + 8'h9a: begin outa = 10'h3c6; outb = 2'b01; outc = 1'b0; end + 8'h9b: begin outa = 10'h109; outb = 2'b00; outc = 1'b1; end + 8'h9c: begin outa = 10'h073; outb = 2'b10; outc = 1'b1; end + 8'h9d: begin outa = 10'h19f; outb = 2'b01; outc = 1'b0; end + 8'h9e: begin outa = 10'h3b8; outb = 2'b01; outc = 1'b0; end + 8'h9f: begin outa = 10'h00e; outb = 2'b00; outc = 1'b1; end + 8'ha0: begin outa = 10'h1b3; outb = 2'b11; outc = 1'b1; end + 8'ha1: begin outa = 10'h2bd; outb = 2'b11; outc = 1'b0; end + 8'ha2: begin outa = 10'h324; outb = 2'b00; outc = 1'b1; end + 8'ha3: begin outa = 10'h343; outb = 2'b10; outc = 1'b0; end + 8'ha4: begin outa = 10'h1c9; outb = 2'b01; outc = 1'b0; end + 8'ha5: begin outa = 10'h185; outb = 2'b00; outc = 1'b1; end + 8'ha6: begin outa = 10'h37a; outb = 2'b00; outc = 1'b1; end + 8'ha7: begin outa = 10'h0e0; outb = 2'b01; outc = 1'b1; end + 8'ha8: begin outa = 10'h0a3; outb = 2'b10; outc = 1'b0; end + 8'ha9: begin outa = 10'h019; outb = 2'b11; outc = 1'b0; end + 8'haa: begin outa = 10'h099; outb = 2'b00; outc = 1'b1; end + 8'hab: begin outa = 10'h376; outb = 2'b01; outc = 1'b1; end + 8'hac: begin outa = 10'h077; outb = 2'b00; outc = 1'b1; end + 8'had: begin outa = 10'h2b1; outb = 2'b11; outc = 1'b1; end + 8'hae: begin outa = 10'h27f; outb = 2'b00; outc = 1'b0; end + 8'haf: begin outa = 10'h265; outb = 2'b11; outc = 1'b0; end + 8'hb0: begin outa = 10'h156; outb = 2'b10; outc = 1'b1; end + 8'hb1: begin outa = 10'h1ce; outb = 2'b00; outc = 1'b0; end + 8'hb2: begin outa = 10'h008; outb = 2'b01; outc = 1'b0; end + 8'hb3: begin outa = 10'h12e; outb = 2'b11; outc = 1'b1; end + 8'hb4: begin outa = 10'h199; outb = 2'b11; outc = 1'b0; end + 8'hb5: begin outa = 10'h330; outb = 2'b10; outc = 1'b0; end + 8'hb6: begin outa = 10'h1ab; outb = 2'b01; outc = 1'b1; end + 8'hb7: begin outa = 10'h3bd; outb = 2'b00; outc = 1'b0; end + 8'hb8: begin outa = 10'h0ca; outb = 2'b10; outc = 1'b0; end + 8'hb9: begin outa = 10'h367; outb = 2'b00; outc = 1'b0; end + 8'hba: begin outa = 10'h334; outb = 2'b00; outc = 1'b0; end + 8'hbb: begin outa = 10'h040; outb = 2'b00; outc = 1'b1; end + 8'hbc: begin outa = 10'h1a7; outb = 2'b10; outc = 1'b1; end + 8'hbd: begin outa = 10'h036; outb = 2'b11; outc = 1'b1; end + 8'hbe: begin outa = 10'h223; outb = 2'b11; outc = 1'b1; end + 8'hbf: begin outa = 10'h075; outb = 2'b01; outc = 1'b0; end + 8'hc0: begin outa = 10'h3c4; outb = 2'b00; outc = 1'b1; end + 8'hc1: begin outa = 10'h2cc; outb = 2'b01; outc = 1'b0; end + 8'hc2: begin outa = 10'h123; outb = 2'b01; outc = 1'b0; end + 8'hc3: begin outa = 10'h3fd; outb = 2'b01; outc = 1'b1; end + 8'hc4: begin outa = 10'h11e; outb = 2'b00; outc = 1'b0; end + 8'hc5: begin outa = 10'h27c; outb = 2'b11; outc = 1'b1; end + 8'hc6: begin outa = 10'h1e2; outb = 2'b11; outc = 1'b0; end + 8'hc7: begin outa = 10'h377; outb = 2'b11; outc = 1'b0; end + 8'hc8: begin outa = 10'h33a; outb = 2'b11; outc = 1'b0; end + 8'hc9: begin outa = 10'h32d; outb = 2'b11; outc = 1'b1; end + 8'hca: begin outa = 10'h014; outb = 2'b11; outc = 1'b0; end + 8'hcb: begin outa = 10'h332; outb = 2'b10; outc = 1'b0; end + 8'hcc: begin outa = 10'h359; outb = 2'b00; outc = 1'b0; end + 8'hcd: begin outa = 10'h0a4; outb = 2'b10; outc = 1'b1; end + 8'hce: begin outa = 10'h348; outb = 2'b00; outc = 1'b1; end + 8'hcf: begin outa = 10'h04b; outb = 2'b11; outc = 1'b1; end + 8'hd0: begin outa = 10'h147; outb = 2'b10; outc = 1'b1; end + 8'hd1: begin outa = 10'h026; outb = 2'b00; outc = 1'b1; end + 8'hd2: begin outa = 10'h103; outb = 2'b00; outc = 1'b0; end + 8'hd3: begin outa = 10'h106; outb = 2'b00; outc = 1'b1; end + 8'hd4: begin outa = 10'h35a; outb = 2'b00; outc = 1'b0; end + 8'hd5: begin outa = 10'h254; outb = 2'b01; outc = 1'b0; end + 8'hd6: begin outa = 10'h0cd; outb = 2'b01; outc = 1'b0; end + 8'hd7: begin outa = 10'h17c; outb = 2'b11; outc = 1'b1; end + 8'hd8: begin outa = 10'h37e; outb = 2'b10; outc = 1'b1; end + 8'hd9: begin outa = 10'h0a9; outb = 2'b11; outc = 1'b1; end + 8'hda: begin outa = 10'h0fe; outb = 2'b01; outc = 1'b0; end + 8'hdb: begin outa = 10'h3c0; outb = 2'b11; outc = 1'b1; end + 8'hdc: begin outa = 10'h1d9; outb = 2'b10; outc = 1'b1; end + 8'hdd: begin outa = 10'h10e; outb = 2'b00; outc = 1'b1; end + 8'hde: begin outa = 10'h394; outb = 2'b01; outc = 1'b0; end + 8'hdf: begin outa = 10'h316; outb = 2'b01; outc = 1'b0; end + 8'he0: begin outa = 10'h05b; outb = 2'b11; outc = 1'b0; end + 8'he1: begin outa = 10'h126; outb = 2'b01; outc = 1'b1; end + 8'he2: begin outa = 10'h369; outb = 2'b11; outc = 1'b0; end + 8'he3: begin outa = 10'h291; outb = 2'b10; outc = 1'b1; end + 8'he4: begin outa = 10'h2ca; outb = 2'b00; outc = 1'b1; end + 8'he5: begin outa = 10'h25b; outb = 2'b01; outc = 1'b1; end + 8'he6: begin outa = 10'h106; outb = 2'b00; outc = 1'b0; end + 8'he7: begin outa = 10'h172; outb = 2'b11; outc = 1'b1; end + 8'he8: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end + 8'he9: begin outa = 10'h2d3; outb = 2'b11; outc = 1'b1; end + 8'hea: begin outa = 10'h182; outb = 2'b00; outc = 1'b0; end + 8'heb: begin outa = 10'h327; outb = 2'b00; outc = 1'b1; end + 8'hec: begin outa = 10'h1d0; outb = 2'b10; outc = 1'b0; end + 8'hed: begin outa = 10'h204; outb = 2'b00; outc = 1'b1; end + 8'hee: begin outa = 10'h11f; outb = 2'b00; outc = 1'b1; end + 8'hef: begin outa = 10'h365; outb = 2'b11; outc = 1'b1; end + 8'hf0: begin outa = 10'h2c2; outb = 2'b01; outc = 1'b1; end + 8'hf1: begin outa = 10'h2b5; outb = 2'b10; outc = 1'b0; end + 8'hf2: begin outa = 10'h1f8; outb = 2'b10; outc = 1'b1; end + 8'hf3: begin outa = 10'h2a7; outb = 2'b01; outc = 1'b1; end + 8'hf4: begin outa = 10'h1be; outb = 2'b10; outc = 1'b1; end + 8'hf5: begin outa = 10'h25e; outb = 2'b10; outc = 1'b1; end + 8'hf6: begin outa = 10'h032; outb = 2'b10; outc = 1'b0; end + 8'hf7: begin outa = 10'h2ef; outb = 2'b00; outc = 1'b0; end + 8'hf8: begin outa = 10'h02f; outb = 2'b00; outc = 1'b1; end + 8'hf9: begin outa = 10'h201; outb = 2'b10; outc = 1'b0; end + 8'hfa: begin outa = 10'h054; outb = 2'b01; outc = 1'b1; end + 8'hfb: begin outa = 10'h013; outb = 2'b10; outc = 1'b0; end + 8'hfc: begin outa = 10'h249; outb = 2'b01; outc = 1'b0; end + 8'hfd: begin outa = 10'h09a; outb = 2'b10; outc = 1'b0; end + 8'hfe: begin outa = 10'h012; outb = 2'b00; outc = 1'b0; end + 8'hff: begin outa = 10'h114; outb = 2'b10; outc = 1'b1; end + endcase + // verilog_format: on + end endmodule diff --git a/test_regress/t/t_case_huge_sub2.v b/test_regress/t/t_case_huge_sub2.v index c03c39889..5e5c4720f 100644 --- a/test_regress/t/t_case_huge_sub2.v +++ b/test_regress/t/t_case_huge_sub2.v @@ -5,290 +5,286 @@ // SPDX-License-Identifier: CC0-1.0 module t_case_huge_sub2 (/*AUTOARG*/ - // Outputs - outa, - // Inputs - index - ); + // Outputs + outa, + // Inputs + index + ); - input [9:0] index; - output [9:0] outa; + input [9:0] index; + output logic [9:0] outa; - // ============================= - /*AUTOREG*/ - // Beginning of automatic regs (for this module's undeclared outputs) - reg [9:0] outa; - // End of automatics + // ============================= + // Created from Python3: + // for i in range(1024): + // print(" 10'h%03x: begin outa = 10'h%03x; outb = 2'b%d%d; outc = 1'b%d; end" + // % (i, random.randint(0,1024), random.randint(0,1), + // random.randint(0,1), random.randint(0,1))) - // ============================= - // Created from Python3: - // for i in range(1024): - // print(" 10'h%03x: begin outa = 10'h%03x; outb = 2'b%d%d; outc = 1'b%d; end" - // % (i, random.randint(0,1024), random.randint(0,1), - // random.randint(0,1), random.randint(0,1))) - - always @(/*AS*/index) begin - case (index[7:0]) -`ifdef VERILATOR // Harder test - 8'h00: begin outa = $c("0"); end // Makes whole table non-optimizable + always @(/*AS*/index) begin + case (index[7:0]) +`ifdef VERILATOR // Harder test + 8'h00: begin + outa = $c("0"); + end // Makes whole table non-optimizable `else - 8'h00: begin outa = 10'h0; end + 8'h00: outa = 10'h0; `endif - 8'h01: begin outa = 10'h318; end - 8'h02: begin outa = 10'h29f; end - 8'h03: begin outa = 10'h392; end - 8'h04: begin outa = 10'h1ef; end - 8'h05: begin outa = 10'h06c; end - 8'h06: begin outa = 10'h29f; end - 8'h07: begin outa = 10'h29a; end - 8'h08: begin outa = 10'h3ce; end - 8'h09: begin outa = 10'h37c; end - 8'h0a: begin outa = 10'h058; end - 8'h0b: begin outa = 10'h3b2; end - 8'h0c: begin outa = 10'h36f; end - 8'h0d: begin outa = 10'h2c5; end - 8'h0e: begin outa = 10'h23a; end - 8'h0f: begin outa = 10'h222; end - 8'h10: begin outa = 10'h328; end - 8'h11: begin outa = 10'h3c3; end - 8'h12: begin outa = 10'h12c; end - 8'h13: begin outa = 10'h1d0; end - 8'h14: begin outa = 10'h3ff; end - 8'h15: begin outa = 10'h115; end - 8'h16: begin outa = 10'h3ba; end - 8'h17: begin outa = 10'h3ba; end - 8'h18: begin outa = 10'h10d; end - 8'h19: begin outa = 10'h13b; end - 8'h1a: begin outa = 10'h0a0; end - 8'h1b: begin outa = 10'h264; end - 8'h1c: begin outa = 10'h3a2; end - 8'h1d: begin outa = 10'h07c; end - 8'h1e: begin outa = 10'h291; end - 8'h1f: begin outa = 10'h1d1; end - 8'h20: begin outa = 10'h354; end - 8'h21: begin outa = 10'h0c0; end - 8'h22: begin outa = 10'h191; end - 8'h23: begin outa = 10'h379; end - 8'h24: begin outa = 10'h073; end - 8'h25: begin outa = 10'h2fd; end - 8'h26: begin outa = 10'h2e0; end - 8'h27: begin outa = 10'h337; end - 8'h28: begin outa = 10'h2c7; end - 8'h29: begin outa = 10'h19e; end - 8'h2a: begin outa = 10'h107; end - 8'h2b: begin outa = 10'h06a; end - 8'h2c: begin outa = 10'h1c7; end - 8'h2d: begin outa = 10'h107; end - 8'h2e: begin outa = 10'h0cf; end - 8'h2f: begin outa = 10'h009; end - 8'h30: begin outa = 10'h09d; end - 8'h31: begin outa = 10'h28e; end - 8'h32: begin outa = 10'h010; end - 8'h33: begin outa = 10'h1e0; end - 8'h34: begin outa = 10'h079; end - 8'h35: begin outa = 10'h13e; end - 8'h36: begin outa = 10'h282; end - 8'h37: begin outa = 10'h21c; end - 8'h38: begin outa = 10'h148; end - 8'h39: begin outa = 10'h3c0; end - 8'h3a: begin outa = 10'h176; end - 8'h3b: begin outa = 10'h3fc; end - 8'h3c: begin outa = 10'h295; end - 8'h3d: begin outa = 10'h113; end - 8'h3e: begin outa = 10'h354; end - 8'h3f: begin outa = 10'h0db; end - 8'h40: begin outa = 10'h238; end - 8'h41: begin outa = 10'h12b; end - 8'h42: begin outa = 10'h1dc; end - 8'h43: begin outa = 10'h137; end - 8'h44: begin outa = 10'h1e2; end - 8'h45: begin outa = 10'h3d5; end - 8'h46: begin outa = 10'h30c; end - 8'h47: begin outa = 10'h298; end - 8'h48: begin outa = 10'h080; end - 8'h49: begin outa = 10'h35a; end - 8'h4a: begin outa = 10'h01b; end - 8'h4b: begin outa = 10'h0a3; end - 8'h4c: begin outa = 10'h0b3; end - 8'h4d: begin outa = 10'h17a; end - 8'h4e: begin outa = 10'h3ae; end - 8'h4f: begin outa = 10'h078; end - 8'h50: begin outa = 10'h322; end - 8'h51: begin outa = 10'h213; end - 8'h52: begin outa = 10'h11a; end - 8'h53: begin outa = 10'h1a7; end - 8'h54: begin outa = 10'h35a; end - 8'h55: begin outa = 10'h233; end - 8'h56: begin outa = 10'h01d; end - 8'h57: begin outa = 10'h2d5; end - 8'h58: begin outa = 10'h1a0; end - 8'h59: begin outa = 10'h3d0; end - 8'h5a: begin outa = 10'h181; end - 8'h5b: begin outa = 10'h219; end - 8'h5c: begin outa = 10'h26a; end - 8'h5d: begin outa = 10'h050; end - 8'h5e: begin outa = 10'h189; end - 8'h5f: begin outa = 10'h1eb; end - 8'h60: begin outa = 10'h224; end - 8'h61: begin outa = 10'h2fe; end - 8'h62: begin outa = 10'h0ae; end - 8'h63: begin outa = 10'h1cd; end - 8'h64: begin outa = 10'h273; end - 8'h65: begin outa = 10'h268; end - 8'h66: begin outa = 10'h111; end - 8'h67: begin outa = 10'h1f9; end - 8'h68: begin outa = 10'h232; end - 8'h69: begin outa = 10'h255; end - 8'h6a: begin outa = 10'h34c; end - 8'h6b: begin outa = 10'h049; end - 8'h6c: begin outa = 10'h197; end - 8'h6d: begin outa = 10'h0fe; end - 8'h6e: begin outa = 10'h253; end - 8'h6f: begin outa = 10'h2de; end - 8'h70: begin outa = 10'h13b; end - 8'h71: begin outa = 10'h040; end - 8'h72: begin outa = 10'h0b4; end - 8'h73: begin outa = 10'h233; end - 8'h74: begin outa = 10'h198; end - 8'h75: begin outa = 10'h018; end - 8'h76: begin outa = 10'h2f7; end - 8'h77: begin outa = 10'h134; end - 8'h78: begin outa = 10'h1ca; end - 8'h79: begin outa = 10'h286; end - 8'h7a: begin outa = 10'h0e6; end - 8'h7b: begin outa = 10'h064; end - 8'h7c: begin outa = 10'h257; end - 8'h7d: begin outa = 10'h31a; end - 8'h7e: begin outa = 10'h247; end - 8'h7f: begin outa = 10'h299; end - 8'h80: begin outa = 10'h02c; end - 8'h81: begin outa = 10'h2bb; end - 8'h82: begin outa = 10'h180; end - 8'h83: begin outa = 10'h245; end - 8'h84: begin outa = 10'h0da; end - 8'h85: begin outa = 10'h367; end - 8'h86: begin outa = 10'h304; end - 8'h87: begin outa = 10'h38b; end - 8'h88: begin outa = 10'h09f; end - 8'h89: begin outa = 10'h1f0; end - 8'h8a: begin outa = 10'h281; end - 8'h8b: begin outa = 10'h019; end - 8'h8c: begin outa = 10'h1f2; end - 8'h8d: begin outa = 10'h0b1; end - 8'h8e: begin outa = 10'h058; end - 8'h8f: begin outa = 10'h39b; end - 8'h90: begin outa = 10'h2ec; end - 8'h91: begin outa = 10'h250; end - 8'h92: begin outa = 10'h3f4; end - 8'h93: begin outa = 10'h057; end - 8'h94: begin outa = 10'h18f; end - 8'h95: begin outa = 10'h105; end - 8'h96: begin outa = 10'h1ae; end - 8'h97: begin outa = 10'h04e; end - 8'h98: begin outa = 10'h240; end - 8'h99: begin outa = 10'h3e4; end - 8'h9a: begin outa = 10'h3c6; end - 8'h9b: begin outa = 10'h109; end - 8'h9c: begin outa = 10'h073; end - 8'h9d: begin outa = 10'h19f; end - 8'h9e: begin outa = 10'h3b8; end - 8'h9f: begin outa = 10'h00e; end - 8'ha0: begin outa = 10'h1b3; end - 8'ha1: begin outa = 10'h2bd; end - 8'ha2: begin outa = 10'h324; end - 8'ha3: begin outa = 10'h343; end - 8'ha4: begin outa = 10'h1c9; end - 8'ha5: begin outa = 10'h185; end - 8'ha6: begin outa = 10'h37a; end - 8'ha7: begin outa = 10'h0e0; end - 8'ha8: begin outa = 10'h0a3; end - 8'ha9: begin outa = 10'h019; end - 8'haa: begin outa = 10'h099; end - 8'hab: begin outa = 10'h376; end - 8'hac: begin outa = 10'h077; end - 8'had: begin outa = 10'h2b1; end - 8'hae: begin outa = 10'h27f; end - 8'haf: begin outa = 10'h265; end - 8'hb0: begin outa = 10'h156; end - 8'hb1: begin outa = 10'h1ce; end - 8'hb2: begin outa = 10'h008; end - 8'hb3: begin outa = 10'h12e; end - 8'hb4: begin outa = 10'h199; end - 8'hb5: begin outa = 10'h330; end - 8'hb6: begin outa = 10'h1ab; end - 8'hb7: begin outa = 10'h3bd; end - 8'hb8: begin outa = 10'h0ca; end - 8'hb9: begin outa = 10'h367; end - 8'hba: begin outa = 10'h334; end - 8'hbb: begin outa = 10'h040; end - 8'hbc: begin outa = 10'h1a7; end - 8'hbd: begin outa = 10'h036; end - 8'hbe: begin outa = 10'h223; end - 8'hbf: begin outa = 10'h075; end - 8'hc0: begin outa = 10'h3c4; end - 8'hc1: begin outa = 10'h2cc; end - 8'hc2: begin outa = 10'h123; end - 8'hc3: begin outa = 10'h3fd; end - 8'hc4: begin outa = 10'h11e; end - 8'hc5: begin outa = 10'h27c; end - 8'hc6: begin outa = 10'h1e2; end - 8'hc7: begin outa = 10'h377; end - 8'hc8: begin outa = 10'h33a; end - 8'hc9: begin outa = 10'h32d; end - 8'hca: begin outa = 10'h014; end - 8'hcb: begin outa = 10'h332; end - 8'hcc: begin outa = 10'h359; end - 8'hcd: begin outa = 10'h0a4; end - 8'hce: begin outa = 10'h348; end - 8'hcf: begin outa = 10'h04b; end - 8'hd0: begin outa = 10'h147; end - 8'hd1: begin outa = 10'h026; end - 8'hd2: begin outa = 10'h103; end - 8'hd3: begin outa = 10'h106; end - 8'hd4: begin outa = 10'h35a; end - 8'hd5: begin outa = 10'h254; end - 8'hd6: begin outa = 10'h0cd; end - 8'hd7: begin outa = 10'h17c; end - 8'hd8: begin outa = 10'h37e; end - 8'hd9: begin outa = 10'h0a9; end - 8'hda: begin outa = 10'h0fe; end - 8'hdb: begin outa = 10'h3c0; end - 8'hdc: begin outa = 10'h1d9; end - 8'hdd: begin outa = 10'h10e; end - 8'hde: begin outa = 10'h394; end - 8'hdf: begin outa = 10'h316; end - 8'he0: begin outa = 10'h05b; end - 8'he1: begin outa = 10'h126; end - 8'he2: begin outa = 10'h369; end - 8'he3: begin outa = 10'h291; end - 8'he4: begin outa = 10'h2ca; end - 8'he5: begin outa = 10'h25b; end - 8'he6: begin outa = 10'h106; end - 8'he7: begin outa = 10'h172; end - 8'he8: begin outa = 10'h2f7; end - 8'he9: begin outa = 10'h2d3; end - 8'hea: begin outa = 10'h182; end - 8'heb: begin outa = 10'h327; end - 8'hec: begin outa = 10'h1d0; end - 8'hed: begin outa = 10'h204; end - 8'hee: begin outa = 10'h11f; end - 8'hef: begin outa = 10'h365; end - 8'hf0: begin outa = 10'h2c2; end - 8'hf1: begin outa = 10'h2b5; end - 8'hf2: begin outa = 10'h1f8; end - 8'hf3: begin outa = 10'h2a7; end - 8'hf4: begin outa = 10'h1be; end - 8'hf5: begin outa = 10'h25e; end - 8'hf6: begin outa = 10'h032; end - 8'hf7: begin outa = 10'h2ef; end - 8'hf8: begin outa = 10'h02f; end - 8'hf9: begin outa = 10'h201; end - 8'hfa: begin outa = 10'h054; end - 8'hfb: begin outa = 10'h013; end - 8'hfc: begin outa = 10'h249; end - 8'hfd: begin outa = 10'h09a; end - 8'hfe: begin outa = 10'h012; end - 8'hff: begin outa = 10'h114; end - endcase - end + 8'h01: outa = 10'h318; + 8'h02: outa = 10'h29f; + 8'h03: outa = 10'h392; + 8'h04: outa = 10'h1ef; + 8'h05: outa = 10'h06c; + 8'h06: outa = 10'h29f; + 8'h07: outa = 10'h29a; + 8'h08: outa = 10'h3ce; + 8'h09: outa = 10'h37c; + 8'h0a: outa = 10'h058; + 8'h0b: outa = 10'h3b2; + 8'h0c: outa = 10'h36f; + 8'h0d: outa = 10'h2c5; + 8'h0e: outa = 10'h23a; + 8'h0f: outa = 10'h222; + 8'h10: outa = 10'h328; + 8'h11: outa = 10'h3c3; + 8'h12: outa = 10'h12c; + 8'h13: outa = 10'h1d0; + 8'h14: outa = 10'h3ff; + 8'h15: outa = 10'h115; + 8'h16: outa = 10'h3ba; + 8'h17: outa = 10'h3ba; + 8'h18: outa = 10'h10d; + 8'h19: outa = 10'h13b; + 8'h1a: outa = 10'h0a0; + 8'h1b: outa = 10'h264; + 8'h1c: outa = 10'h3a2; + 8'h1d: outa = 10'h07c; + 8'h1e: outa = 10'h291; + 8'h1f: outa = 10'h1d1; + 8'h20: outa = 10'h354; + 8'h21: outa = 10'h0c0; + 8'h22: outa = 10'h191; + 8'h23: outa = 10'h379; + 8'h24: outa = 10'h073; + 8'h25: outa = 10'h2fd; + 8'h26: outa = 10'h2e0; + 8'h27: outa = 10'h337; + 8'h28: outa = 10'h2c7; + 8'h29: outa = 10'h19e; + 8'h2a: outa = 10'h107; + 8'h2b: outa = 10'h06a; + 8'h2c: outa = 10'h1c7; + 8'h2d: outa = 10'h107; + 8'h2e: outa = 10'h0cf; + 8'h2f: outa = 10'h009; + 8'h30: outa = 10'h09d; + 8'h31: outa = 10'h28e; + 8'h32: outa = 10'h010; + 8'h33: outa = 10'h1e0; + 8'h34: outa = 10'h079; + 8'h35: outa = 10'h13e; + 8'h36: outa = 10'h282; + 8'h37: outa = 10'h21c; + 8'h38: outa = 10'h148; + 8'h39: outa = 10'h3c0; + 8'h3a: outa = 10'h176; + 8'h3b: outa = 10'h3fc; + 8'h3c: outa = 10'h295; + 8'h3d: outa = 10'h113; + 8'h3e: outa = 10'h354; + 8'h3f: outa = 10'h0db; + 8'h40: outa = 10'h238; + 8'h41: outa = 10'h12b; + 8'h42: outa = 10'h1dc; + 8'h43: outa = 10'h137; + 8'h44: outa = 10'h1e2; + 8'h45: outa = 10'h3d5; + 8'h46: outa = 10'h30c; + 8'h47: outa = 10'h298; + 8'h48: outa = 10'h080; + 8'h49: outa = 10'h35a; + 8'h4a: outa = 10'h01b; + 8'h4b: outa = 10'h0a3; + 8'h4c: outa = 10'h0b3; + 8'h4d: outa = 10'h17a; + 8'h4e: outa = 10'h3ae; + 8'h4f: outa = 10'h078; + 8'h50: outa = 10'h322; + 8'h51: outa = 10'h213; + 8'h52: outa = 10'h11a; + 8'h53: outa = 10'h1a7; + 8'h54: outa = 10'h35a; + 8'h55: outa = 10'h233; + 8'h56: outa = 10'h01d; + 8'h57: outa = 10'h2d5; + 8'h58: outa = 10'h1a0; + 8'h59: outa = 10'h3d0; + 8'h5a: outa = 10'h181; + 8'h5b: outa = 10'h219; + 8'h5c: outa = 10'h26a; + 8'h5d: outa = 10'h050; + 8'h5e: outa = 10'h189; + 8'h5f: outa = 10'h1eb; + 8'h60: outa = 10'h224; + 8'h61: outa = 10'h2fe; + 8'h62: outa = 10'h0ae; + 8'h63: outa = 10'h1cd; + 8'h64: outa = 10'h273; + 8'h65: outa = 10'h268; + 8'h66: outa = 10'h111; + 8'h67: outa = 10'h1f9; + 8'h68: outa = 10'h232; + 8'h69: outa = 10'h255; + 8'h6a: outa = 10'h34c; + 8'h6b: outa = 10'h049; + 8'h6c: outa = 10'h197; + 8'h6d: outa = 10'h0fe; + 8'h6e: outa = 10'h253; + 8'h6f: outa = 10'h2de; + 8'h70: outa = 10'h13b; + 8'h71: outa = 10'h040; + 8'h72: outa = 10'h0b4; + 8'h73: outa = 10'h233; + 8'h74: outa = 10'h198; + 8'h75: outa = 10'h018; + 8'h76: outa = 10'h2f7; + 8'h77: outa = 10'h134; + 8'h78: outa = 10'h1ca; + 8'h79: outa = 10'h286; + 8'h7a: outa = 10'h0e6; + 8'h7b: outa = 10'h064; + 8'h7c: outa = 10'h257; + 8'h7d: outa = 10'h31a; + 8'h7e: outa = 10'h247; + 8'h7f: outa = 10'h299; + 8'h80: outa = 10'h02c; + 8'h81: outa = 10'h2bb; + 8'h82: outa = 10'h180; + 8'h83: outa = 10'h245; + 8'h84: outa = 10'h0da; + 8'h85: outa = 10'h367; + 8'h86: outa = 10'h304; + 8'h87: outa = 10'h38b; + 8'h88: outa = 10'h09f; + 8'h89: outa = 10'h1f0; + 8'h8a: outa = 10'h281; + 8'h8b: outa = 10'h019; + 8'h8c: outa = 10'h1f2; + 8'h8d: outa = 10'h0b1; + 8'h8e: outa = 10'h058; + 8'h8f: outa = 10'h39b; + 8'h90: outa = 10'h2ec; + 8'h91: outa = 10'h250; + 8'h92: outa = 10'h3f4; + 8'h93: outa = 10'h057; + 8'h94: outa = 10'h18f; + 8'h95: outa = 10'h105; + 8'h96: outa = 10'h1ae; + 8'h97: outa = 10'h04e; + 8'h98: outa = 10'h240; + 8'h99: outa = 10'h3e4; + 8'h9a: outa = 10'h3c6; + 8'h9b: outa = 10'h109; + 8'h9c: outa = 10'h073; + 8'h9d: outa = 10'h19f; + 8'h9e: outa = 10'h3b8; + 8'h9f: outa = 10'h00e; + 8'ha0: outa = 10'h1b3; + 8'ha1: outa = 10'h2bd; + 8'ha2: outa = 10'h324; + 8'ha3: outa = 10'h343; + 8'ha4: outa = 10'h1c9; + 8'ha5: outa = 10'h185; + 8'ha6: outa = 10'h37a; + 8'ha7: outa = 10'h0e0; + 8'ha8: outa = 10'h0a3; + 8'ha9: outa = 10'h019; + 8'haa: outa = 10'h099; + 8'hab: outa = 10'h376; + 8'hac: outa = 10'h077; + 8'had: outa = 10'h2b1; + 8'hae: outa = 10'h27f; + 8'haf: outa = 10'h265; + 8'hb0: outa = 10'h156; + 8'hb1: outa = 10'h1ce; + 8'hb2: outa = 10'h008; + 8'hb3: outa = 10'h12e; + 8'hb4: outa = 10'h199; + 8'hb5: outa = 10'h330; + 8'hb6: outa = 10'h1ab; + 8'hb7: outa = 10'h3bd; + 8'hb8: outa = 10'h0ca; + 8'hb9: outa = 10'h367; + 8'hba: outa = 10'h334; + 8'hbb: outa = 10'h040; + 8'hbc: outa = 10'h1a7; + 8'hbd: outa = 10'h036; + 8'hbe: outa = 10'h223; + 8'hbf: outa = 10'h075; + 8'hc0: outa = 10'h3c4; + 8'hc1: outa = 10'h2cc; + 8'hc2: outa = 10'h123; + 8'hc3: outa = 10'h3fd; + 8'hc4: outa = 10'h11e; + 8'hc5: outa = 10'h27c; + 8'hc6: outa = 10'h1e2; + 8'hc7: outa = 10'h377; + 8'hc8: outa = 10'h33a; + 8'hc9: outa = 10'h32d; + 8'hca: outa = 10'h014; + 8'hcb: outa = 10'h332; + 8'hcc: outa = 10'h359; + 8'hcd: outa = 10'h0a4; + 8'hce: outa = 10'h348; + 8'hcf: outa = 10'h04b; + 8'hd0: outa = 10'h147; + 8'hd1: outa = 10'h026; + 8'hd2: outa = 10'h103; + 8'hd3: outa = 10'h106; + 8'hd4: outa = 10'h35a; + 8'hd5: outa = 10'h254; + 8'hd6: outa = 10'h0cd; + 8'hd7: outa = 10'h17c; + 8'hd8: outa = 10'h37e; + 8'hd9: outa = 10'h0a9; + 8'hda: outa = 10'h0fe; + 8'hdb: outa = 10'h3c0; + 8'hdc: outa = 10'h1d9; + 8'hdd: outa = 10'h10e; + 8'hde: outa = 10'h394; + 8'hdf: outa = 10'h316; + 8'he0: outa = 10'h05b; + 8'he1: outa = 10'h126; + 8'he2: outa = 10'h369; + 8'he3: outa = 10'h291; + 8'he4: outa = 10'h2ca; + 8'he5: outa = 10'h25b; + 8'he6: outa = 10'h106; + 8'he7: outa = 10'h172; + 8'he8: outa = 10'h2f7; + 8'he9: outa = 10'h2d3; + 8'hea: outa = 10'h182; + 8'heb: outa = 10'h327; + 8'hec: outa = 10'h1d0; + 8'hed: outa = 10'h204; + 8'hee: outa = 10'h11f; + 8'hef: outa = 10'h365; + 8'hf0: outa = 10'h2c2; + 8'hf1: outa = 10'h2b5; + 8'hf2: outa = 10'h1f8; + 8'hf3: outa = 10'h2a7; + 8'hf4: outa = 10'h1be; + 8'hf5: outa = 10'h25e; + 8'hf6: outa = 10'h032; + 8'hf7: outa = 10'h2ef; + 8'hf8: outa = 10'h02f; + 8'hf9: outa = 10'h201; + 8'hfa: outa = 10'h054; + 8'hfb: outa = 10'h013; + 8'hfc: outa = 10'h249; + 8'hfd: outa = 10'h09a; + 8'hfe: outa = 10'h012; + 8'hff: outa = 10'h114; + endcase + end endmodule diff --git a/test_regress/t/t_case_huge_sub3.v b/test_regress/t/t_case_huge_sub3.v index aba38bf15..956324dc0 100644 --- a/test_regress/t/t_case_huge_sub3.v +++ b/test_regress/t/t_case_huge_sub3.v @@ -5,289 +5,285 @@ // SPDX-License-Identifier: CC0-1.0 module t_case_huge_sub3 (/*AUTOARG*/ - // Outputs - outr, - // Inputs - clk, index - ); + // Outputs + outr, + // Inputs + clk, index + ); - input clk; - input [9:0] index; - output [3:0] outr; + input clk; + input [9:0] index; + output logic [3:0] outr; - // ============================= - /*AUTOREG*/ - // Beginning of automatic regs (for this module's undeclared outputs) - reg [3:0] outr; - // End of automatics + // ============================= + // Created from Pthon3: + // for i in range(256): + // print(" 8'h%02x: begin outr <= outr^index[8:5]^4'h%01x; end" + // % (i, random.randint(0,15))) - // ============================= - // Created from Pthon3: - // for i in range(256): - // print(" 8'h%02x: begin outr <= outr^index[8:5]^4'h%01x; end" - // % (i, random.randint(0,15))) + // Reset cheating + initial outr = 4'b0; - // Reset cheating - initial outr = 4'b0; - - always @(posedge clk) begin - case (index[7:0]) - 8'h00: begin outr <= 4'h0; end - 8'h01: begin /*No Change*/ end - 8'h02: begin outr <= outr^index[8:5]^4'ha; end - 8'h03: begin outr <= outr^index[8:5]^4'h4; end - 8'h04: begin outr <= outr^index[8:5]^4'hd; end - 8'h05: begin outr <= outr^index[8:5]^4'h1; end - 8'h06: begin outr <= outr^index[8:5]^4'hf; end - 8'h07: begin outr <= outr^index[8:5]^4'he; end - 8'h08: begin outr <= outr^index[8:5]^4'h0; end - 8'h09: begin outr <= outr^index[8:5]^4'h4; end - 8'h0a: begin outr <= outr^index[8:5]^4'h5; end - 8'h0b: begin outr <= outr^index[8:5]^4'ha; end - 8'h0c: begin outr <= outr^index[8:5]^4'h2; end - 8'h0d: begin outr <= outr^index[8:5]^4'hf; end - 8'h0e: begin outr <= outr^index[8:5]^4'h5; end - 8'h0f: begin outr <= outr^index[8:5]^4'h0; end - 8'h10: begin outr <= outr^index[8:5]^4'h3; end - 8'h11: begin outr <= outr^index[8:5]^4'hb; end - 8'h12: begin outr <= outr^index[8:5]^4'h0; end - 8'h13: begin outr <= outr^index[8:5]^4'hf; end - 8'h14: begin outr <= outr^index[8:5]^4'h3; end - 8'h15: begin outr <= outr^index[8:5]^4'h5; end - 8'h16: begin outr <= outr^index[8:5]^4'h7; end - 8'h17: begin outr <= outr^index[8:5]^4'h2; end - 8'h18: begin outr <= outr^index[8:5]^4'h3; end - 8'h19: begin outr <= outr^index[8:5]^4'hb; end - 8'h1a: begin outr <= outr^index[8:5]^4'h5; end - 8'h1b: begin outr <= outr^index[8:5]^4'h4; end - 8'h1c: begin outr <= outr^index[8:5]^4'h2; end - 8'h1d: begin outr <= outr^index[8:5]^4'hf; end - 8'h1e: begin outr <= outr^index[8:5]^4'h0; end - 8'h1f: begin outr <= outr^index[8:5]^4'h4; end - 8'h20: begin outr <= outr^index[8:5]^4'h6; end - 8'h21: begin outr <= outr^index[8:5]^4'ha; end - 8'h22: begin outr <= outr^index[8:5]^4'h6; end - 8'h23: begin outr <= outr^index[8:5]^4'hb; end - 8'h24: begin outr <= outr^index[8:5]^4'ha; end - 8'h25: begin outr <= outr^index[8:5]^4'he; end - 8'h26: begin outr <= outr^index[8:5]^4'h7; end - 8'h27: begin outr <= outr^index[8:5]^4'ha; end - 8'h28: begin outr <= outr^index[8:5]^4'h3; end - 8'h29: begin outr <= outr^index[8:5]^4'h8; end - 8'h2a: begin outr <= outr^index[8:5]^4'h1; end - 8'h2b: begin outr <= outr^index[8:5]^4'h8; end - 8'h2c: begin outr <= outr^index[8:5]^4'h4; end - 8'h2d: begin outr <= outr^index[8:5]^4'h4; end - 8'h2e: begin outr <= outr^index[8:5]^4'he; end - 8'h2f: begin outr <= outr^index[8:5]^4'h8; end - 8'h30: begin outr <= outr^index[8:5]^4'ha; end - 8'h31: begin outr <= outr^index[8:5]^4'h7; end - 8'h32: begin outr <= outr^index[8:5]^4'h0; end - 8'h33: begin outr <= outr^index[8:5]^4'h3; end - 8'h34: begin outr <= outr^index[8:5]^4'h1; end - 8'h35: begin outr <= outr^index[8:5]^4'h3; end - 8'h36: begin outr <= outr^index[8:5]^4'h4; end - 8'h37: begin outr <= outr^index[8:5]^4'h6; end - 8'h38: begin outr <= outr^index[8:5]^4'h4; end - 8'h39: begin outr <= outr^index[8:5]^4'hb; end - 8'h3a: begin outr <= outr^index[8:5]^4'h7; end - 8'h3b: begin outr <= outr^index[8:5]^4'h1; end - 8'h3c: begin outr <= outr^index[8:5]^4'h2; end - 8'h3d: begin outr <= outr^index[8:5]^4'h0; end - 8'h3e: begin outr <= outr^index[8:5]^4'h2; end - 8'h3f: begin outr <= outr^index[8:5]^4'ha; end - 8'h40: begin outr <= outr^index[8:5]^4'h7; end - 8'h41: begin outr <= outr^index[8:5]^4'h5; end - 8'h42: begin outr <= outr^index[8:5]^4'h5; end - 8'h43: begin outr <= outr^index[8:5]^4'h4; end - 8'h44: begin outr <= outr^index[8:5]^4'h8; end - 8'h45: begin outr <= outr^index[8:5]^4'h5; end - 8'h46: begin outr <= outr^index[8:5]^4'hf; end - 8'h47: begin outr <= outr^index[8:5]^4'h6; end - 8'h48: begin outr <= outr^index[8:5]^4'h7; end - 8'h49: begin outr <= outr^index[8:5]^4'h4; end - 8'h4a: begin outr <= outr^index[8:5]^4'ha; end - 8'h4b: begin outr <= outr^index[8:5]^4'hd; end - 8'h4c: begin outr <= outr^index[8:5]^4'hb; end - 8'h4d: begin outr <= outr^index[8:5]^4'hf; end - 8'h4e: begin outr <= outr^index[8:5]^4'hd; end - 8'h4f: begin outr <= outr^index[8:5]^4'h7; end - 8'h50: begin outr <= outr^index[8:5]^4'h9; end - 8'h51: begin outr <= outr^index[8:5]^4'ha; end - 8'h52: begin outr <= outr^index[8:5]^4'hf; end - 8'h53: begin outr <= outr^index[8:5]^4'h3; end - 8'h54: begin outr <= outr^index[8:5]^4'h1; end - 8'h55: begin outr <= outr^index[8:5]^4'h0; end - 8'h56: begin outr <= outr^index[8:5]^4'h2; end - 8'h57: begin outr <= outr^index[8:5]^4'h9; end - 8'h58: begin outr <= outr^index[8:5]^4'h2; end - 8'h59: begin outr <= outr^index[8:5]^4'h4; end - 8'h5a: begin outr <= outr^index[8:5]^4'hc; end - 8'h5b: begin outr <= outr^index[8:5]^4'hd; end - 8'h5c: begin outr <= outr^index[8:5]^4'h3; end - 8'h5d: begin outr <= outr^index[8:5]^4'hb; end - 8'h5e: begin outr <= outr^index[8:5]^4'hd; end - 8'h5f: begin outr <= outr^index[8:5]^4'h7; end - 8'h60: begin outr <= outr^index[8:5]^4'h7; end - 8'h61: begin outr <= outr^index[8:5]^4'h3; end - 8'h62: begin outr <= outr^index[8:5]^4'h3; end - 8'h63: begin outr <= outr^index[8:5]^4'hb; end - 8'h64: begin outr <= outr^index[8:5]^4'h9; end - 8'h65: begin outr <= outr^index[8:5]^4'h4; end - 8'h66: begin outr <= outr^index[8:5]^4'h3; end - 8'h67: begin outr <= outr^index[8:5]^4'h6; end - 8'h68: begin outr <= outr^index[8:5]^4'h7; end - 8'h69: begin outr <= outr^index[8:5]^4'h7; end - 8'h6a: begin outr <= outr^index[8:5]^4'hf; end - 8'h6b: begin outr <= outr^index[8:5]^4'h6; end - 8'h6c: begin outr <= outr^index[8:5]^4'h8; end - 8'h6d: begin outr <= outr^index[8:5]^4'he; end - 8'h6e: begin outr <= outr^index[8:5]^4'h4; end - 8'h6f: begin outr <= outr^index[8:5]^4'h6; end - 8'h70: begin outr <= outr^index[8:5]^4'hc; end - 8'h71: begin outr <= outr^index[8:5]^4'h9; end - 8'h72: begin outr <= outr^index[8:5]^4'h5; end - 8'h73: begin outr <= outr^index[8:5]^4'ha; end - 8'h74: begin outr <= outr^index[8:5]^4'h7; end - 8'h75: begin outr <= outr^index[8:5]^4'h0; end - 8'h76: begin outr <= outr^index[8:5]^4'h1; end - 8'h77: begin outr <= outr^index[8:5]^4'he; end - 8'h78: begin outr <= outr^index[8:5]^4'ha; end - 8'h79: begin outr <= outr^index[8:5]^4'h7; end - 8'h7a: begin outr <= outr^index[8:5]^4'hf; end - 8'h7b: begin outr <= outr^index[8:5]^4'he; end - 8'h7c: begin outr <= outr^index[8:5]^4'h6; end - 8'h7d: begin outr <= outr^index[8:5]^4'hc; end - 8'h7e: begin outr <= outr^index[8:5]^4'hc; end - 8'h7f: begin outr <= outr^index[8:5]^4'h0; end - 8'h80: begin outr <= outr^index[8:5]^4'h0; end - 8'h81: begin outr <= outr^index[8:5]^4'hd; end - 8'h82: begin outr <= outr^index[8:5]^4'hb; end - 8'h83: begin outr <= outr^index[8:5]^4'hc; end - 8'h84: begin outr <= outr^index[8:5]^4'h2; end - 8'h85: begin outr <= outr^index[8:5]^4'h8; end - 8'h86: begin outr <= outr^index[8:5]^4'h3; end - 8'h87: begin outr <= outr^index[8:5]^4'ha; end - 8'h88: begin outr <= outr^index[8:5]^4'he; end - 8'h89: begin outr <= outr^index[8:5]^4'h9; end - 8'h8a: begin outr <= outr^index[8:5]^4'h1; end - 8'h8b: begin outr <= outr^index[8:5]^4'h1; end - 8'h8c: begin outr <= outr^index[8:5]^4'hc; end - 8'h8d: begin outr <= outr^index[8:5]^4'h2; end - 8'h8e: begin outr <= outr^index[8:5]^4'h2; end - 8'h8f: begin outr <= outr^index[8:5]^4'hd; end - 8'h90: begin outr <= outr^index[8:5]^4'h0; end - 8'h91: begin outr <= outr^index[8:5]^4'h6; end - 8'h92: begin outr <= outr^index[8:5]^4'h7; end - 8'h93: begin outr <= outr^index[8:5]^4'hc; end - 8'h94: begin outr <= outr^index[8:5]^4'hb; end - 8'h95: begin outr <= outr^index[8:5]^4'h3; end - 8'h96: begin outr <= outr^index[8:5]^4'h0; end - 8'h97: begin outr <= outr^index[8:5]^4'hc; end - 8'h98: begin outr <= outr^index[8:5]^4'hc; end - 8'h99: begin outr <= outr^index[8:5]^4'hb; end - 8'h9a: begin outr <= outr^index[8:5]^4'h6; end - 8'h9b: begin outr <= outr^index[8:5]^4'h5; end - 8'h9c: begin outr <= outr^index[8:5]^4'h5; end - 8'h9d: begin outr <= outr^index[8:5]^4'h4; end - 8'h9e: begin outr <= outr^index[8:5]^4'h7; end - 8'h9f: begin outr <= outr^index[8:5]^4'he; end - 8'ha0: begin outr <= outr^index[8:5]^4'hc; end - 8'ha1: begin outr <= outr^index[8:5]^4'hc; end - 8'ha2: begin outr <= outr^index[8:5]^4'h0; end - 8'ha3: begin outr <= outr^index[8:5]^4'h1; end - 8'ha4: begin outr <= outr^index[8:5]^4'hd; end - 8'ha5: begin outr <= outr^index[8:5]^4'h3; end - 8'ha6: begin outr <= outr^index[8:5]^4'hc; end - 8'ha7: begin outr <= outr^index[8:5]^4'h2; end - 8'ha8: begin outr <= outr^index[8:5]^4'h3; end - 8'ha9: begin outr <= outr^index[8:5]^4'hd; end - 8'haa: begin outr <= outr^index[8:5]^4'h5; end - 8'hab: begin outr <= outr^index[8:5]^4'hb; end - 8'hac: begin outr <= outr^index[8:5]^4'he; end - 8'had: begin outr <= outr^index[8:5]^4'h0; end - 8'hae: begin outr <= outr^index[8:5]^4'hf; end - 8'haf: begin outr <= outr^index[8:5]^4'h9; end - 8'hb0: begin outr <= outr^index[8:5]^4'hf; end - 8'hb1: begin outr <= outr^index[8:5]^4'h7; end - 8'hb2: begin outr <= outr^index[8:5]^4'h9; end - 8'hb3: begin outr <= outr^index[8:5]^4'hf; end - 8'hb4: begin outr <= outr^index[8:5]^4'he; end - 8'hb5: begin outr <= outr^index[8:5]^4'h3; end - 8'hb6: begin outr <= outr^index[8:5]^4'he; end - 8'hb7: begin outr <= outr^index[8:5]^4'h8; end - 8'hb8: begin outr <= outr^index[8:5]^4'hf; end - 8'hb9: begin outr <= outr^index[8:5]^4'hd; end - 8'hba: begin outr <= outr^index[8:5]^4'h3; end - 8'hbb: begin outr <= outr^index[8:5]^4'h5; end - 8'hbc: begin outr <= outr^index[8:5]^4'hd; end - 8'hbd: begin outr <= outr^index[8:5]^4'ha; end - 8'hbe: begin outr <= outr^index[8:5]^4'h7; end - 8'hbf: begin outr <= outr^index[8:5]^4'he; end - 8'hc0: begin outr <= outr^index[8:5]^4'h2; end - 8'hc1: begin outr <= outr^index[8:5]^4'he; end - 8'hc2: begin outr <= outr^index[8:5]^4'h9; end - 8'hc3: begin outr <= outr^index[8:5]^4'hb; end - 8'hc4: begin outr <= outr^index[8:5]^4'h0; end - 8'hc5: begin outr <= outr^index[8:5]^4'h5; end - 8'hc6: begin outr <= outr^index[8:5]^4'h9; end - 8'hc7: begin outr <= outr^index[8:5]^4'h6; end - 8'hc8: begin outr <= outr^index[8:5]^4'ha; end - 8'hc9: begin outr <= outr^index[8:5]^4'hf; end - 8'hca: begin outr <= outr^index[8:5]^4'h3; end - 8'hcb: begin outr <= outr^index[8:5]^4'hb; end - 8'hcc: begin outr <= outr^index[8:5]^4'he; end - 8'hcd: begin outr <= outr^index[8:5]^4'h2; end - 8'hce: begin outr <= outr^index[8:5]^4'h5; end - 8'hcf: begin outr <= outr^index[8:5]^4'hf; end - 8'hd0: begin outr <= outr^index[8:5]^4'h2; end - 8'hd1: begin outr <= outr^index[8:5]^4'h9; end - 8'hd2: begin outr <= outr^index[8:5]^4'hb; end - 8'hd3: begin outr <= outr^index[8:5]^4'h8; end - 8'hd4: begin outr <= outr^index[8:5]^4'h0; end - 8'hd5: begin outr <= outr^index[8:5]^4'h2; end - 8'hd6: begin outr <= outr^index[8:5]^4'hb; end - 8'hd7: begin outr <= outr^index[8:5]^4'h2; end - 8'hd8: begin outr <= outr^index[8:5]^4'ha; end - 8'hd9: begin outr <= outr^index[8:5]^4'hf; end - 8'hda: begin outr <= outr^index[8:5]^4'h8; end - 8'hdb: begin outr <= outr^index[8:5]^4'h4; end - 8'hdc: begin outr <= outr^index[8:5]^4'he; end - 8'hdd: begin outr <= outr^index[8:5]^4'h6; end - 8'hde: begin outr <= outr^index[8:5]^4'h9; end - 8'hdf: begin outr <= outr^index[8:5]^4'h9; end - 8'he0: begin outr <= outr^index[8:5]^4'h7; end - 8'he1: begin outr <= outr^index[8:5]^4'h0; end - 8'he2: begin outr <= outr^index[8:5]^4'h9; end - 8'he3: begin outr <= outr^index[8:5]^4'h3; end - 8'he4: begin outr <= outr^index[8:5]^4'h2; end - 8'he5: begin outr <= outr^index[8:5]^4'h4; end - 8'he6: begin outr <= outr^index[8:5]^4'h5; end - 8'he7: begin outr <= outr^index[8:5]^4'h5; end - 8'he8: begin outr <= outr^index[8:5]^4'hf; end - 8'he9: begin outr <= outr^index[8:5]^4'ha; end - 8'hea: begin outr <= outr^index[8:5]^4'hc; end - 8'heb: begin outr <= outr^index[8:5]^4'hd; end - 8'hec: begin outr <= outr^index[8:5]^4'h1; end - 8'hed: begin outr <= outr^index[8:5]^4'h5; end - 8'hee: begin outr <= outr^index[8:5]^4'h9; end - 8'hef: begin outr <= outr^index[8:5]^4'h0; end - 8'hf0: begin outr <= outr^index[8:5]^4'hd; end - 8'hf1: begin outr <= outr^index[8:5]^4'hf; end - 8'hf2: begin outr <= outr^index[8:5]^4'h4; end - 8'hf3: begin outr <= outr^index[8:5]^4'ha; end - 8'hf4: begin outr <= outr^index[8:5]^4'h8; end - 8'hf5: begin outr <= outr^index[8:5]^4'he; end - 8'hf6: begin outr <= outr^index[8:5]^4'he; end - 8'hf7: begin outr <= outr^index[8:5]^4'h1; end - 8'hf8: begin outr <= outr^index[8:5]^4'h6; end - 8'hf9: begin outr <= outr^index[8:5]^4'h0; end - 8'hfa: begin outr <= outr^index[8:5]^4'h5; end - 8'hfb: begin outr <= outr^index[8:5]^4'h1; end - 8'hfc: begin outr <= outr^index[8:5]^4'h8; end - 8'hfd: begin outr <= outr^index[8:5]^4'h6; end - 8'hfe: begin outr <= outr^index[8:5]^4'h1; end - default: begin outr <= outr^index[8:5]^4'h6; end - endcase - end + always @(posedge clk) begin + // verilog_format: off + case (index[7:0]) + 8'h00: begin outr <= 4'h0; end + 8'h01: begin /*No Change*/ end + 8'h02: begin outr <= outr^index[8:5]^4'ha; end + 8'h03: begin outr <= outr^index[8:5]^4'h4; end + 8'h04: begin outr <= outr^index[8:5]^4'hd; end + 8'h05: begin outr <= outr^index[8:5]^4'h1; end + 8'h06: begin outr <= outr^index[8:5]^4'hf; end + 8'h07: begin outr <= outr^index[8:5]^4'he; end + 8'h08: begin outr <= outr^index[8:5]^4'h0; end + 8'h09: begin outr <= outr^index[8:5]^4'h4; end + 8'h0a: begin outr <= outr^index[8:5]^4'h5; end + 8'h0b: begin outr <= outr^index[8:5]^4'ha; end + 8'h0c: begin outr <= outr^index[8:5]^4'h2; end + 8'h0d: begin outr <= outr^index[8:5]^4'hf; end + 8'h0e: begin outr <= outr^index[8:5]^4'h5; end + 8'h0f: begin outr <= outr^index[8:5]^4'h0; end + 8'h10: begin outr <= outr^index[8:5]^4'h3; end + 8'h11: begin outr <= outr^index[8:5]^4'hb; end + 8'h12: begin outr <= outr^index[8:5]^4'h0; end + 8'h13: begin outr <= outr^index[8:5]^4'hf; end + 8'h14: begin outr <= outr^index[8:5]^4'h3; end + 8'h15: begin outr <= outr^index[8:5]^4'h5; end + 8'h16: begin outr <= outr^index[8:5]^4'h7; end + 8'h17: begin outr <= outr^index[8:5]^4'h2; end + 8'h18: begin outr <= outr^index[8:5]^4'h3; end + 8'h19: begin outr <= outr^index[8:5]^4'hb; end + 8'h1a: begin outr <= outr^index[8:5]^4'h5; end + 8'h1b: begin outr <= outr^index[8:5]^4'h4; end + 8'h1c: begin outr <= outr^index[8:5]^4'h2; end + 8'h1d: begin outr <= outr^index[8:5]^4'hf; end + 8'h1e: begin outr <= outr^index[8:5]^4'h0; end + 8'h1f: begin outr <= outr^index[8:5]^4'h4; end + 8'h20: begin outr <= outr^index[8:5]^4'h6; end + 8'h21: begin outr <= outr^index[8:5]^4'ha; end + 8'h22: begin outr <= outr^index[8:5]^4'h6; end + 8'h23: begin outr <= outr^index[8:5]^4'hb; end + 8'h24: begin outr <= outr^index[8:5]^4'ha; end + 8'h25: begin outr <= outr^index[8:5]^4'he; end + 8'h26: begin outr <= outr^index[8:5]^4'h7; end + 8'h27: begin outr <= outr^index[8:5]^4'ha; end + 8'h28: begin outr <= outr^index[8:5]^4'h3; end + 8'h29: begin outr <= outr^index[8:5]^4'h8; end + 8'h2a: begin outr <= outr^index[8:5]^4'h1; end + 8'h2b: begin outr <= outr^index[8:5]^4'h8; end + 8'h2c: begin outr <= outr^index[8:5]^4'h4; end + 8'h2d: begin outr <= outr^index[8:5]^4'h4; end + 8'h2e: begin outr <= outr^index[8:5]^4'he; end + 8'h2f: begin outr <= outr^index[8:5]^4'h8; end + 8'h30: begin outr <= outr^index[8:5]^4'ha; end + 8'h31: begin outr <= outr^index[8:5]^4'h7; end + 8'h32: begin outr <= outr^index[8:5]^4'h0; end + 8'h33: begin outr <= outr^index[8:5]^4'h3; end + 8'h34: begin outr <= outr^index[8:5]^4'h1; end + 8'h35: begin outr <= outr^index[8:5]^4'h3; end + 8'h36: begin outr <= outr^index[8:5]^4'h4; end + 8'h37: begin outr <= outr^index[8:5]^4'h6; end + 8'h38: begin outr <= outr^index[8:5]^4'h4; end + 8'h39: begin outr <= outr^index[8:5]^4'hb; end + 8'h3a: begin outr <= outr^index[8:5]^4'h7; end + 8'h3b: begin outr <= outr^index[8:5]^4'h1; end + 8'h3c: begin outr <= outr^index[8:5]^4'h2; end + 8'h3d: begin outr <= outr^index[8:5]^4'h0; end + 8'h3e: begin outr <= outr^index[8:5]^4'h2; end + 8'h3f: begin outr <= outr^index[8:5]^4'ha; end + 8'h40: begin outr <= outr^index[8:5]^4'h7; end + 8'h41: begin outr <= outr^index[8:5]^4'h5; end + 8'h42: begin outr <= outr^index[8:5]^4'h5; end + 8'h43: begin outr <= outr^index[8:5]^4'h4; end + 8'h44: begin outr <= outr^index[8:5]^4'h8; end + 8'h45: begin outr <= outr^index[8:5]^4'h5; end + 8'h46: begin outr <= outr^index[8:5]^4'hf; end + 8'h47: begin outr <= outr^index[8:5]^4'h6; end + 8'h48: begin outr <= outr^index[8:5]^4'h7; end + 8'h49: begin outr <= outr^index[8:5]^4'h4; end + 8'h4a: begin outr <= outr^index[8:5]^4'ha; end + 8'h4b: begin outr <= outr^index[8:5]^4'hd; end + 8'h4c: begin outr <= outr^index[8:5]^4'hb; end + 8'h4d: begin outr <= outr^index[8:5]^4'hf; end + 8'h4e: begin outr <= outr^index[8:5]^4'hd; end + 8'h4f: begin outr <= outr^index[8:5]^4'h7; end + 8'h50: begin outr <= outr^index[8:5]^4'h9; end + 8'h51: begin outr <= outr^index[8:5]^4'ha; end + 8'h52: begin outr <= outr^index[8:5]^4'hf; end + 8'h53: begin outr <= outr^index[8:5]^4'h3; end + 8'h54: begin outr <= outr^index[8:5]^4'h1; end + 8'h55: begin outr <= outr^index[8:5]^4'h0; end + 8'h56: begin outr <= outr^index[8:5]^4'h2; end + 8'h57: begin outr <= outr^index[8:5]^4'h9; end + 8'h58: begin outr <= outr^index[8:5]^4'h2; end + 8'h59: begin outr <= outr^index[8:5]^4'h4; end + 8'h5a: begin outr <= outr^index[8:5]^4'hc; end + 8'h5b: begin outr <= outr^index[8:5]^4'hd; end + 8'h5c: begin outr <= outr^index[8:5]^4'h3; end + 8'h5d: begin outr <= outr^index[8:5]^4'hb; end + 8'h5e: begin outr <= outr^index[8:5]^4'hd; end + 8'h5f: begin outr <= outr^index[8:5]^4'h7; end + 8'h60: begin outr <= outr^index[8:5]^4'h7; end + 8'h61: begin outr <= outr^index[8:5]^4'h3; end + 8'h62: begin outr <= outr^index[8:5]^4'h3; end + 8'h63: begin outr <= outr^index[8:5]^4'hb; end + 8'h64: begin outr <= outr^index[8:5]^4'h9; end + 8'h65: begin outr <= outr^index[8:5]^4'h4; end + 8'h66: begin outr <= outr^index[8:5]^4'h3; end + 8'h67: begin outr <= outr^index[8:5]^4'h6; end + 8'h68: begin outr <= outr^index[8:5]^4'h7; end + 8'h69: begin outr <= outr^index[8:5]^4'h7; end + 8'h6a: begin outr <= outr^index[8:5]^4'hf; end + 8'h6b: begin outr <= outr^index[8:5]^4'h6; end + 8'h6c: begin outr <= outr^index[8:5]^4'h8; end + 8'h6d: begin outr <= outr^index[8:5]^4'he; end + 8'h6e: begin outr <= outr^index[8:5]^4'h4; end + 8'h6f: begin outr <= outr^index[8:5]^4'h6; end + 8'h70: begin outr <= outr^index[8:5]^4'hc; end + 8'h71: begin outr <= outr^index[8:5]^4'h9; end + 8'h72: begin outr <= outr^index[8:5]^4'h5; end + 8'h73: begin outr <= outr^index[8:5]^4'ha; end + 8'h74: begin outr <= outr^index[8:5]^4'h7; end + 8'h75: begin outr <= outr^index[8:5]^4'h0; end + 8'h76: begin outr <= outr^index[8:5]^4'h1; end + 8'h77: begin outr <= outr^index[8:5]^4'he; end + 8'h78: begin outr <= outr^index[8:5]^4'ha; end + 8'h79: begin outr <= outr^index[8:5]^4'h7; end + 8'h7a: begin outr <= outr^index[8:5]^4'hf; end + 8'h7b: begin outr <= outr^index[8:5]^4'he; end + 8'h7c: begin outr <= outr^index[8:5]^4'h6; end + 8'h7d: begin outr <= outr^index[8:5]^4'hc; end + 8'h7e: begin outr <= outr^index[8:5]^4'hc; end + 8'h7f: begin outr <= outr^index[8:5]^4'h0; end + 8'h80: begin outr <= outr^index[8:5]^4'h0; end + 8'h81: begin outr <= outr^index[8:5]^4'hd; end + 8'h82: begin outr <= outr^index[8:5]^4'hb; end + 8'h83: begin outr <= outr^index[8:5]^4'hc; end + 8'h84: begin outr <= outr^index[8:5]^4'h2; end + 8'h85: begin outr <= outr^index[8:5]^4'h8; end + 8'h86: begin outr <= outr^index[8:5]^4'h3; end + 8'h87: begin outr <= outr^index[8:5]^4'ha; end + 8'h88: begin outr <= outr^index[8:5]^4'he; end + 8'h89: begin outr <= outr^index[8:5]^4'h9; end + 8'h8a: begin outr <= outr^index[8:5]^4'h1; end + 8'h8b: begin outr <= outr^index[8:5]^4'h1; end + 8'h8c: begin outr <= outr^index[8:5]^4'hc; end + 8'h8d: begin outr <= outr^index[8:5]^4'h2; end + 8'h8e: begin outr <= outr^index[8:5]^4'h2; end + 8'h8f: begin outr <= outr^index[8:5]^4'hd; end + 8'h90: begin outr <= outr^index[8:5]^4'h0; end + 8'h91: begin outr <= outr^index[8:5]^4'h6; end + 8'h92: begin outr <= outr^index[8:5]^4'h7; end + 8'h93: begin outr <= outr^index[8:5]^4'hc; end + 8'h94: begin outr <= outr^index[8:5]^4'hb; end + 8'h95: begin outr <= outr^index[8:5]^4'h3; end + 8'h96: begin outr <= outr^index[8:5]^4'h0; end + 8'h97: begin outr <= outr^index[8:5]^4'hc; end + 8'h98: begin outr <= outr^index[8:5]^4'hc; end + 8'h99: begin outr <= outr^index[8:5]^4'hb; end + 8'h9a: begin outr <= outr^index[8:5]^4'h6; end + 8'h9b: begin outr <= outr^index[8:5]^4'h5; end + 8'h9c: begin outr <= outr^index[8:5]^4'h5; end + 8'h9d: begin outr <= outr^index[8:5]^4'h4; end + 8'h9e: begin outr <= outr^index[8:5]^4'h7; end + 8'h9f: begin outr <= outr^index[8:5]^4'he; end + 8'ha0: begin outr <= outr^index[8:5]^4'hc; end + 8'ha1: begin outr <= outr^index[8:5]^4'hc; end + 8'ha2: begin outr <= outr^index[8:5]^4'h0; end + 8'ha3: begin outr <= outr^index[8:5]^4'h1; end + 8'ha4: begin outr <= outr^index[8:5]^4'hd; end + 8'ha5: begin outr <= outr^index[8:5]^4'h3; end + 8'ha6: begin outr <= outr^index[8:5]^4'hc; end + 8'ha7: begin outr <= outr^index[8:5]^4'h2; end + 8'ha8: begin outr <= outr^index[8:5]^4'h3; end + 8'ha9: begin outr <= outr^index[8:5]^4'hd; end + 8'haa: begin outr <= outr^index[8:5]^4'h5; end + 8'hab: begin outr <= outr^index[8:5]^4'hb; end + 8'hac: begin outr <= outr^index[8:5]^4'he; end + 8'had: begin outr <= outr^index[8:5]^4'h0; end + 8'hae: begin outr <= outr^index[8:5]^4'hf; end + 8'haf: begin outr <= outr^index[8:5]^4'h9; end + 8'hb0: begin outr <= outr^index[8:5]^4'hf; end + 8'hb1: begin outr <= outr^index[8:5]^4'h7; end + 8'hb2: begin outr <= outr^index[8:5]^4'h9; end + 8'hb3: begin outr <= outr^index[8:5]^4'hf; end + 8'hb4: begin outr <= outr^index[8:5]^4'he; end + 8'hb5: begin outr <= outr^index[8:5]^4'h3; end + 8'hb6: begin outr <= outr^index[8:5]^4'he; end + 8'hb7: begin outr <= outr^index[8:5]^4'h8; end + 8'hb8: begin outr <= outr^index[8:5]^4'hf; end + 8'hb9: begin outr <= outr^index[8:5]^4'hd; end + 8'hba: begin outr <= outr^index[8:5]^4'h3; end + 8'hbb: begin outr <= outr^index[8:5]^4'h5; end + 8'hbc: begin outr <= outr^index[8:5]^4'hd; end + 8'hbd: begin outr <= outr^index[8:5]^4'ha; end + 8'hbe: begin outr <= outr^index[8:5]^4'h7; end + 8'hbf: begin outr <= outr^index[8:5]^4'he; end + 8'hc0: begin outr <= outr^index[8:5]^4'h2; end + 8'hc1: begin outr <= outr^index[8:5]^4'he; end + 8'hc2: begin outr <= outr^index[8:5]^4'h9; end + 8'hc3: begin outr <= outr^index[8:5]^4'hb; end + 8'hc4: begin outr <= outr^index[8:5]^4'h0; end + 8'hc5: begin outr <= outr^index[8:5]^4'h5; end + 8'hc6: begin outr <= outr^index[8:5]^4'h9; end + 8'hc7: begin outr <= outr^index[8:5]^4'h6; end + 8'hc8: begin outr <= outr^index[8:5]^4'ha; end + 8'hc9: begin outr <= outr^index[8:5]^4'hf; end + 8'hca: begin outr <= outr^index[8:5]^4'h3; end + 8'hcb: begin outr <= outr^index[8:5]^4'hb; end + 8'hcc: begin outr <= outr^index[8:5]^4'he; end + 8'hcd: begin outr <= outr^index[8:5]^4'h2; end + 8'hce: begin outr <= outr^index[8:5]^4'h5; end + 8'hcf: begin outr <= outr^index[8:5]^4'hf; end + 8'hd0: begin outr <= outr^index[8:5]^4'h2; end + 8'hd1: begin outr <= outr^index[8:5]^4'h9; end + 8'hd2: begin outr <= outr^index[8:5]^4'hb; end + 8'hd3: begin outr <= outr^index[8:5]^4'h8; end + 8'hd4: begin outr <= outr^index[8:5]^4'h0; end + 8'hd5: begin outr <= outr^index[8:5]^4'h2; end + 8'hd6: begin outr <= outr^index[8:5]^4'hb; end + 8'hd7: begin outr <= outr^index[8:5]^4'h2; end + 8'hd8: begin outr <= outr^index[8:5]^4'ha; end + 8'hd9: begin outr <= outr^index[8:5]^4'hf; end + 8'hda: begin outr <= outr^index[8:5]^4'h8; end + 8'hdb: begin outr <= outr^index[8:5]^4'h4; end + 8'hdc: begin outr <= outr^index[8:5]^4'he; end + 8'hdd: begin outr <= outr^index[8:5]^4'h6; end + 8'hde: begin outr <= outr^index[8:5]^4'h9; end + 8'hdf: begin outr <= outr^index[8:5]^4'h9; end + 8'he0: begin outr <= outr^index[8:5]^4'h7; end + 8'he1: begin outr <= outr^index[8:5]^4'h0; end + 8'he2: begin outr <= outr^index[8:5]^4'h9; end + 8'he3: begin outr <= outr^index[8:5]^4'h3; end + 8'he4: begin outr <= outr^index[8:5]^4'h2; end + 8'he5: begin outr <= outr^index[8:5]^4'h4; end + 8'he6: begin outr <= outr^index[8:5]^4'h5; end + 8'he7: begin outr <= outr^index[8:5]^4'h5; end + 8'he8: begin outr <= outr^index[8:5]^4'hf; end + 8'he9: begin outr <= outr^index[8:5]^4'ha; end + 8'hea: begin outr <= outr^index[8:5]^4'hc; end + 8'heb: begin outr <= outr^index[8:5]^4'hd; end + 8'hec: begin outr <= outr^index[8:5]^4'h1; end + 8'hed: begin outr <= outr^index[8:5]^4'h5; end + 8'hee: begin outr <= outr^index[8:5]^4'h9; end + 8'hef: begin outr <= outr^index[8:5]^4'h0; end + 8'hf0: begin outr <= outr^index[8:5]^4'hd; end + 8'hf1: begin outr <= outr^index[8:5]^4'hf; end + 8'hf2: begin outr <= outr^index[8:5]^4'h4; end + 8'hf3: begin outr <= outr^index[8:5]^4'ha; end + 8'hf4: begin outr <= outr^index[8:5]^4'h8; end + 8'hf5: begin outr <= outr^index[8:5]^4'he; end + 8'hf6: begin outr <= outr^index[8:5]^4'he; end + 8'hf7: begin outr <= outr^index[8:5]^4'h1; end + 8'hf8: begin outr <= outr^index[8:5]^4'h6; end + 8'hf9: begin outr <= outr^index[8:5]^4'h0; end + 8'hfa: begin outr <= outr^index[8:5]^4'h5; end + 8'hfb: begin outr <= outr^index[8:5]^4'h1; end + 8'hfc: begin outr <= outr^index[8:5]^4'h8; end + 8'hfd: begin outr <= outr^index[8:5]^4'h6; end + 8'hfe: begin outr <= outr^index[8:5]^4'h1; end + default: begin outr <= outr^index[8:5]^4'h6; end + endcase + // verilog_format: on + end endmodule diff --git a/test_regress/t/t_case_huge_sub4.v b/test_regress/t/t_case_huge_sub4.v index c642bc65e..5bc42fd3f 100644 --- a/test_regress/t/t_case_huge_sub4.v +++ b/test_regress/t/t_case_huge_sub4.v @@ -7,59 +7,56 @@ // verilator lint_off LATCH module t_case_huge_sub4 (/*AUTOARG*/ - // Outputs - outq, - // Inputs - index - ); + // Outputs + outq, + // Inputs + index + ); - input [7:0] index; - output [9:0] outq; + input [7:0] index; + output logic [9:0] outq; - // ============================= - /*AUTOREG*/ - // Beginning of automatic regs (for this module's undeclared outputs) - reg [9:0] outq; - // End of automatics + // ============================= - // ============================= - always @(/*AS*/index) begin - case (index) - // default below: no change - 8'h00: begin outq = 10'h001; end - 8'he0: begin outq = 10'h05b; end - 8'he1: begin outq = 10'h126; end - 8'he2: begin outq = 10'h369; end - 8'he3: begin outq = 10'h291; end - 8'he4: begin outq = 10'h2ca; end - 8'he5: begin outq = 10'h25b; end - 8'he6: begin outq = 10'h106; end - 8'he7: begin outq = 10'h172; end - 8'he8: begin outq = 10'h2f7; end - 8'he9: begin outq = 10'h2d3; end - 8'hea: begin outq = 10'h182; end - 8'heb: begin outq = 10'h327; end - 8'hec: begin outq = 10'h1d0; end - 8'hed: begin outq = 10'h204; end - 8'hee: begin outq = 10'h11f; end - 8'hef: begin outq = 10'h365; end - 8'hf0: begin outq = 10'h2c2; end - 8'hf1: begin outq = 10'h2b5; end - 8'hf2: begin outq = 10'h1f8; end - 8'hf3: begin outq = 10'h2a7; end - 8'hf4: begin outq = 10'h1be; end - 8'hf5: begin outq = 10'h25e; end - 8'hf6: begin outq = 10'h032; end - 8'hf7: begin outq = 10'h2ef; end - 8'hf8: begin outq = 10'h02f; end - 8'hf9: begin outq = 10'h201; end - 8'hfa: begin outq = 10'h054; end - 8'hfb: begin outq = 10'h013; end - 8'hfc: begin outq = 10'h249; end - 8'hfd: begin outq = 10'h09a; end - 8'hfe: begin outq = 10'h012; end - 8'hff: begin outq = 10'h114; end - default: ; // No change - endcase - end + always @* begin + // verilog_format: off + case (index) + // default below: no change + 8'h00: begin outq = 10'h001; end + 8'he0: begin outq = 10'h05b; end + 8'he1: begin outq = 10'h126; end + 8'he2: begin outq = 10'h369; end + 8'he3: begin outq = 10'h291; end + 8'he4: begin outq = 10'h2ca; end + 8'he5: begin outq = 10'h25b; end + 8'he6: begin outq = 10'h106; end + 8'he7: begin outq = 10'h172; end + 8'he8: begin outq = 10'h2f7; end + 8'he9: begin outq = 10'h2d3; end + 8'hea: begin outq = 10'h182; end + 8'heb: begin outq = 10'h327; end + 8'hec: begin outq = 10'h1d0; end + 8'hed: begin outq = 10'h204; end + 8'hee: begin outq = 10'h11f; end + 8'hef: begin outq = 10'h365; end + 8'hf0: begin outq = 10'h2c2; end + 8'hf1: begin outq = 10'h2b5; end + 8'hf2: begin outq = 10'h1f8; end + 8'hf3: begin outq = 10'h2a7; end + 8'hf4: begin outq = 10'h1be; end + 8'hf5: begin outq = 10'h25e; end + 8'hf6: begin outq = 10'h032; end + 8'hf7: begin outq = 10'h2ef; end + 8'hf8: begin outq = 10'h02f; end + 8'hf9: begin outq = 10'h201; end + 8'hfa: begin outq = 10'h054; end + 8'hfb: begin outq = 10'h013; end + 8'hfc: begin outq = 10'h249; end + 8'hfd: begin outq = 10'h09a; end + 8'hfe: begin outq = 10'h012; end + 8'hff: begin outq = 10'h114; end + default: ; // No change + endcase + // verilog_format: on + end endmodule diff --git a/test_regress/t/t_case_unique_many.v b/test_regress/t/t_case_unique_many.v index d40c70c1b..8c82086d2 100644 --- a/test_regress/t/t_case_unique_many.v +++ b/test_regress/t/t_case_unique_many.v @@ -4,326 +4,326 @@ // any use, without warranty, 2024 by Varun Koyyalagunta, Tenstorrent. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/); +module t; - localparam W = 23; + localparam W = 23; - localparam [W-1:0] R0 = W'('h200000 + 0); - localparam [W-1:0] R1 = W'('h200000 + 1); - localparam [W-1:0] R2 = W'('h200000 + 2); - localparam [W-1:0] R3 = W'('h200000 + 3); - localparam [W-1:0] R4 = W'('h200000 + 4); - localparam [W-1:0] R5 = W'('h200000 + 5); - localparam [W-1:0] R6 = W'('h200000 + 6); - localparam [W-1:0] R7 = W'('h200000 + 7); - localparam [W-1:0] R8 = W'('h200000 + 8); - localparam [W-1:0] R9 = W'('h200000 + 9); - localparam [W-1:0] R10 = W'('h200000 + 10); - localparam [W-1:0] R11 = W'('h200000 + 11); - localparam [W-1:0] R12 = W'('h200000 + 12); - localparam [W-1:0] R13 = W'('h200000 + 13); - localparam [W-1:0] R14 = W'('h200000 + 14); - localparam [W-1:0] R15 = W'('h200000 + 15); - localparam [W-1:0] R16 = W'('h200000 + 16); - localparam [W-1:0] R17 = W'('h200000 + 17); - localparam [W-1:0] R18 = W'('h200000 + 18); - localparam [W-1:0] R19 = W'('h200000 + 19); - localparam [W-1:0] R20 = W'('h200000 + 20); - localparam [W-1:0] R21 = W'('h200000 + 21); - localparam [W-1:0] R22 = W'('h200000 + 22); - localparam [W-1:0] R23 = W'('h200000 + 23); - localparam [W-1:0] R24 = W'('h200000 + 24); - localparam [W-1:0] R25 = W'('h200000 + 25); - localparam [W-1:0] R26 = W'('h200000 + 26); - localparam [W-1:0] R27 = W'('h200000 + 27); - localparam [W-1:0] R28 = W'('h200000 + 28); - localparam [W-1:0] R29 = W'('h200000 + 29); - localparam [W-1:0] R30 = W'('h200000 + 30); - localparam [W-1:0] R31 = W'('h200000 + 31); - localparam [W-1:0] R32 = W'('h200000 + 32); - localparam [W-1:0] R33 = W'('h200000 + 33); - localparam [W-1:0] R34 = W'('h200000 + 34); - localparam [W-1:0] R35 = W'('h200000 + 35); - localparam [W-1:0] R36 = W'('h200000 + 36); - localparam [W-1:0] R37 = W'('h200000 + 37); - localparam [W-1:0] R38 = W'('h200000 + 38); - localparam [W-1:0] R39 = W'('h200000 + 39); - localparam [W-1:0] R40 = W'('h200000 + 40); - localparam [W-1:0] R41 = W'('h200000 + 41); - localparam [W-1:0] R42 = W'('h200000 + 42); - localparam [W-1:0] R43 = W'('h200000 + 43); - localparam [W-1:0] R44 = W'('h200000 + 44); - localparam [W-1:0] R45 = W'('h200000 + 45); - localparam [W-1:0] R46 = W'('h200000 + 46); - localparam [W-1:0] R47 = W'('h200000 + 47); - localparam [W-1:0] R48 = W'('h200000 + 48); - localparam [W-1:0] R49 = W'('h200000 + 49); - localparam [W-1:0] R50 = W'('h200000 + 50); - localparam [W-1:0] R51 = W'('h200000 + 51); - localparam [W-1:0] R52 = W'('h200000 + 52); - localparam [W-1:0] R53 = W'('h200000 + 53); - localparam [W-1:0] R54 = W'('h200000 + 54); - localparam [W-1:0] R55 = W'('h200000 + 55); - localparam [W-1:0] R56 = W'('h200000 + 56); - localparam [W-1:0] R57 = W'('h200000 + 57); - localparam [W-1:0] R58 = W'('h200000 + 58); - localparam [W-1:0] R59 = W'('h200000 + 59); - localparam [W-1:0] R60 = W'('h200000 + 60); - localparam [W-1:0] R61 = W'('h200000 + 61); - localparam [W-1:0] R62 = W'('h200000 + 62); - localparam [W-1:0] R63 = W'('h200000 + 63); - localparam [W-1:0] R64 = W'('h200000 + 64); - localparam [W-1:0] R65 = W'('h200000 + 65); - localparam [W-1:0] R66 = W'('h200000 + 66); - localparam [W-1:0] R67 = W'('h200000 + 67); - localparam [W-1:0] R68 = W'('h200000 + 68); - localparam [W-1:0] R69 = W'('h200000 + 69); - localparam [W-1:0] R70 = W'('h200000 + 70); - localparam [W-1:0] R71 = W'('h200000 + 71); - localparam [W-1:0] R72 = W'('h200000 + 72); - localparam [W-1:0] R73 = W'('h200000 + 73); - localparam [W-1:0] R74 = W'('h200000 + 74); - localparam [W-1:0] R75 = W'('h200000 + 75); - localparam [W-1:0] R76 = W'('h200000 + 76); - localparam [W-1:0] R77 = W'('h200000 + 77); - localparam [W-1:0] R78 = W'('h200000 + 78); - localparam [W-1:0] R79 = W'('h200000 + 79); - localparam [W-1:0] R80 = W'('h200000 + 80); - localparam [W-1:0] R81 = W'('h200000 + 81); - localparam [W-1:0] R82 = W'('h200000 + 82); - localparam [W-1:0] R83 = W'('h200000 + 83); - localparam [W-1:0] R84 = W'('h200000 + 84); - localparam [W-1:0] R85 = W'('h200000 + 85); - localparam [W-1:0] R86 = W'('h200000 + 86); - localparam [W-1:0] R87 = W'('h200000 + 87); - localparam [W-1:0] R88 = W'('h200000 + 88); - localparam [W-1:0] R89 = W'('h200000 + 89); - localparam [W-1:0] R90 = W'('h200000 + 90); - localparam [W-1:0] R91 = W'('h200000 + 91); - localparam [W-1:0] R92 = W'('h200000 + 92); - localparam [W-1:0] R93 = W'('h200000 + 93); - localparam [W-1:0] R94 = W'('h200000 + 94); - localparam [W-1:0] R95 = W'('h200000 + 95); - localparam [W-1:0] R96 = W'('h200000 + 96); - localparam [W-1:0] R97 = W'('h200000 + 97); - localparam [W-1:0] R98 = W'('h200000 + 98); - localparam [W-1:0] R99 = W'('h200000 + 99); - typedef struct packed { - logic r0; - logic r1; - logic r2; - logic r3; - logic r4; - logic r5; - logic r6; - logic r7; - logic r8; - logic r9; - logic r10; - logic r11; - logic r12; - logic r13; - logic r14; - logic r15; - logic r16; - logic r17; - logic r18; - logic r19; - logic r20; - logic r21; - logic r22; - logic r23; - logic r24; - logic r25; - logic r26; - logic r27; - logic r28; - logic r29; - logic r30; - logic r31; - logic r32; - logic r33; - logic r34; - logic r35; - logic r36; - logic r37; - logic r38; - logic r39; - logic r40; - logic r41; - logic r42; - logic r43; - logic r44; - logic r45; - logic r46; - logic r47; - logic r48; - logic r49; - logic r50; - logic r51; - logic r52; - logic r53; - logic r54; - logic r55; - logic r56; - logic r57; - logic r58; - logic r59; - logic r60; - logic r61; - logic r62; - logic r63; - logic r64; - logic r65; - logic r66; - logic r67; - logic r68; - logic r69; - logic r70; - logic r71; - logic r72; - logic r73; - logic r74; - logic r75; - logic r76; - logic r77; - logic r78; - logic r79; - logic r80; - logic r81; - logic r82; - logic r83; - logic r84; - logic r85; - logic r86; - logic r87; - logic r88; - logic r89; - logic r90; - logic r91; - logic r92; - logic r93; - logic r94; - logic r95; - logic r96; - logic r97; - logic r98; - logic r99; - } hit_t; - function automatic hit_t get_hit(input logic [22:0] a); - hit_t hit = '0; - unique case (a) - R0 : begin hit.r0 = 1'b1; end - R1 : begin hit.r1 = 1'b1; end - R2 : begin hit.r2 = 1'b1; end - R3 : begin hit.r3 = 1'b1; end - R4 : begin hit.r4 = 1'b1; end - R5 : begin hit.r5 = 1'b1; end - R6 : begin hit.r6 = 1'b1; end - R7 : begin hit.r7 = 1'b1; end - R8 : begin hit.r8 = 1'b1; end - R9 : begin hit.r9 = 1'b1; end - R10 : begin hit.r10 = 1'b1; end - R11 : begin hit.r11 = 1'b1; end - R12 : begin hit.r12 = 1'b1; end - R13 : begin hit.r13 = 1'b1; end - R14 : begin hit.r14 = 1'b1; end - R15 : begin hit.r15 = 1'b1; end - R16 : begin hit.r16 = 1'b1; end - R17 : begin hit.r17 = 1'b1; end - R18 : begin hit.r18 = 1'b1; end - R19 : begin hit.r19 = 1'b1; end - R20 : begin hit.r20 = 1'b1; end - R21 : begin hit.r21 = 1'b1; end - R22 : begin hit.r22 = 1'b1; end - R23 : begin hit.r23 = 1'b1; end - R24 : begin hit.r24 = 1'b1; end - R25 : begin hit.r25 = 1'b1; end - R26 : begin hit.r26 = 1'b1; end - R27 : begin hit.r27 = 1'b1; end - R28 : begin hit.r28 = 1'b1; end - R29 : begin hit.r29 = 1'b1; end - R30 : begin hit.r30 = 1'b1; end - R31 : begin hit.r31 = 1'b1; end - R32 : begin hit.r32 = 1'b1; end - R33 : begin hit.r33 = 1'b1; end - R34 : begin hit.r34 = 1'b1; end - R35 : begin hit.r35 = 1'b1; end - R36 : begin hit.r36 = 1'b1; end - R37 : begin hit.r37 = 1'b1; end - R38 : begin hit.r38 = 1'b1; end - R39 : begin hit.r39 = 1'b1; end - R40 : begin hit.r40 = 1'b1; end - R41 : begin hit.r41 = 1'b1; end - R42 : begin hit.r42 = 1'b1; end - R43 : begin hit.r43 = 1'b1; end - R44 : begin hit.r44 = 1'b1; end - R45 : begin hit.r45 = 1'b1; end - R46 : begin hit.r46 = 1'b1; end - R47 : begin hit.r47 = 1'b1; end - R48 : begin hit.r48 = 1'b1; end - R49 : begin hit.r49 = 1'b1; end - R50 : begin hit.r50 = 1'b1; end - R51 : begin hit.r51 = 1'b1; end - R52 : begin hit.r52 = 1'b1; end - R53 : begin hit.r53 = 1'b1; end - R54 : begin hit.r54 = 1'b1; end - R55 : begin hit.r55 = 1'b1; end - R56 : begin hit.r56 = 1'b1; end - R57 : begin hit.r57 = 1'b1; end - R58 : begin hit.r58 = 1'b1; end - R59 : begin hit.r59 = 1'b1; end - R60 : begin hit.r60 = 1'b1; end - R61 : begin hit.r61 = 1'b1; end - R62 : begin hit.r62 = 1'b1; end - R63 : begin hit.r63 = 1'b1; end - R64 : begin hit.r64 = 1'b1; end - R65 : begin hit.r65 = 1'b1; end - R66 : begin hit.r66 = 1'b1; end - R67 : begin hit.r67 = 1'b1; end - R68 : begin hit.r68 = 1'b1; end - R69 : begin hit.r69 = 1'b1; end - R70 : begin hit.r70 = 1'b1; end - R71 : begin hit.r71 = 1'b1; end - R72 : begin hit.r72 = 1'b1; end - R73 : begin hit.r73 = 1'b1; end - R74 : begin hit.r74 = 1'b1; end - R75 : begin hit.r75 = 1'b1; end - R76 : begin hit.r76 = 1'b1; end - R77 : begin hit.r77 = 1'b1; end - R78 : begin hit.r78 = 1'b1; end - R79 : begin hit.r79 = 1'b1; end - R80 : begin hit.r80 = 1'b1; end - R81 : begin hit.r81 = 1'b1; end - R82 : begin hit.r82 = 1'b1; end - R83 : begin hit.r83 = 1'b1; end - R84 : begin hit.r84 = 1'b1; end - R85 : begin hit.r85 = 1'b1; end - R86 : begin hit.r86 = 1'b1; end - R87 : begin hit.r87 = 1'b1; end - R88 : begin hit.r88 = 1'b1; end - R89 : begin hit.r89 = 1'b1; end - R90 : begin hit.r90 = 1'b1; end - R91 : begin hit.r91 = 1'b1; end - R92 : begin hit.r92 = 1'b1; end - R93 : begin hit.r93 = 1'b1; end - R94 : begin hit.r94 = 1'b1; end - R95 : begin hit.r95 = 1'b1; end - R96 : begin hit.r96 = 1'b1; end - R97 : begin hit.r97 = 1'b1; end - R98 : begin hit.r98 = 1'b1; end - R99 : begin hit.r99 = 1'b1; end - default: begin hit = '0; end - endcase - return hit; - endfunction + localparam [W-1:0] R0 = W'('h200000 + 0); + localparam [W-1:0] R1 = W'('h200000 + 1); + localparam [W-1:0] R2 = W'('h200000 + 2); + localparam [W-1:0] R3 = W'('h200000 + 3); + localparam [W-1:0] R4 = W'('h200000 + 4); + localparam [W-1:0] R5 = W'('h200000 + 5); + localparam [W-1:0] R6 = W'('h200000 + 6); + localparam [W-1:0] R7 = W'('h200000 + 7); + localparam [W-1:0] R8 = W'('h200000 + 8); + localparam [W-1:0] R9 = W'('h200000 + 9); + localparam [W-1:0] R10 = W'('h200000 + 10); + localparam [W-1:0] R11 = W'('h200000 + 11); + localparam [W-1:0] R12 = W'('h200000 + 12); + localparam [W-1:0] R13 = W'('h200000 + 13); + localparam [W-1:0] R14 = W'('h200000 + 14); + localparam [W-1:0] R15 = W'('h200000 + 15); + localparam [W-1:0] R16 = W'('h200000 + 16); + localparam [W-1:0] R17 = W'('h200000 + 17); + localparam [W-1:0] R18 = W'('h200000 + 18); + localparam [W-1:0] R19 = W'('h200000 + 19); + localparam [W-1:0] R20 = W'('h200000 + 20); + localparam [W-1:0] R21 = W'('h200000 + 21); + localparam [W-1:0] R22 = W'('h200000 + 22); + localparam [W-1:0] R23 = W'('h200000 + 23); + localparam [W-1:0] R24 = W'('h200000 + 24); + localparam [W-1:0] R25 = W'('h200000 + 25); + localparam [W-1:0] R26 = W'('h200000 + 26); + localparam [W-1:0] R27 = W'('h200000 + 27); + localparam [W-1:0] R28 = W'('h200000 + 28); + localparam [W-1:0] R29 = W'('h200000 + 29); + localparam [W-1:0] R30 = W'('h200000 + 30); + localparam [W-1:0] R31 = W'('h200000 + 31); + localparam [W-1:0] R32 = W'('h200000 + 32); + localparam [W-1:0] R33 = W'('h200000 + 33); + localparam [W-1:0] R34 = W'('h200000 + 34); + localparam [W-1:0] R35 = W'('h200000 + 35); + localparam [W-1:0] R36 = W'('h200000 + 36); + localparam [W-1:0] R37 = W'('h200000 + 37); + localparam [W-1:0] R38 = W'('h200000 + 38); + localparam [W-1:0] R39 = W'('h200000 + 39); + localparam [W-1:0] R40 = W'('h200000 + 40); + localparam [W-1:0] R41 = W'('h200000 + 41); + localparam [W-1:0] R42 = W'('h200000 + 42); + localparam [W-1:0] R43 = W'('h200000 + 43); + localparam [W-1:0] R44 = W'('h200000 + 44); + localparam [W-1:0] R45 = W'('h200000 + 45); + localparam [W-1:0] R46 = W'('h200000 + 46); + localparam [W-1:0] R47 = W'('h200000 + 47); + localparam [W-1:0] R48 = W'('h200000 + 48); + localparam [W-1:0] R49 = W'('h200000 + 49); + localparam [W-1:0] R50 = W'('h200000 + 50); + localparam [W-1:0] R51 = W'('h200000 + 51); + localparam [W-1:0] R52 = W'('h200000 + 52); + localparam [W-1:0] R53 = W'('h200000 + 53); + localparam [W-1:0] R54 = W'('h200000 + 54); + localparam [W-1:0] R55 = W'('h200000 + 55); + localparam [W-1:0] R56 = W'('h200000 + 56); + localparam [W-1:0] R57 = W'('h200000 + 57); + localparam [W-1:0] R58 = W'('h200000 + 58); + localparam [W-1:0] R59 = W'('h200000 + 59); + localparam [W-1:0] R60 = W'('h200000 + 60); + localparam [W-1:0] R61 = W'('h200000 + 61); + localparam [W-1:0] R62 = W'('h200000 + 62); + localparam [W-1:0] R63 = W'('h200000 + 63); + localparam [W-1:0] R64 = W'('h200000 + 64); + localparam [W-1:0] R65 = W'('h200000 + 65); + localparam [W-1:0] R66 = W'('h200000 + 66); + localparam [W-1:0] R67 = W'('h200000 + 67); + localparam [W-1:0] R68 = W'('h200000 + 68); + localparam [W-1:0] R69 = W'('h200000 + 69); + localparam [W-1:0] R70 = W'('h200000 + 70); + localparam [W-1:0] R71 = W'('h200000 + 71); + localparam [W-1:0] R72 = W'('h200000 + 72); + localparam [W-1:0] R73 = W'('h200000 + 73); + localparam [W-1:0] R74 = W'('h200000 + 74); + localparam [W-1:0] R75 = W'('h200000 + 75); + localparam [W-1:0] R76 = W'('h200000 + 76); + localparam [W-1:0] R77 = W'('h200000 + 77); + localparam [W-1:0] R78 = W'('h200000 + 78); + localparam [W-1:0] R79 = W'('h200000 + 79); + localparam [W-1:0] R80 = W'('h200000 + 80); + localparam [W-1:0] R81 = W'('h200000 + 81); + localparam [W-1:0] R82 = W'('h200000 + 82); + localparam [W-1:0] R83 = W'('h200000 + 83); + localparam [W-1:0] R84 = W'('h200000 + 84); + localparam [W-1:0] R85 = W'('h200000 + 85); + localparam [W-1:0] R86 = W'('h200000 + 86); + localparam [W-1:0] R87 = W'('h200000 + 87); + localparam [W-1:0] R88 = W'('h200000 + 88); + localparam [W-1:0] R89 = W'('h200000 + 89); + localparam [W-1:0] R90 = W'('h200000 + 90); + localparam [W-1:0] R91 = W'('h200000 + 91); + localparam [W-1:0] R92 = W'('h200000 + 92); + localparam [W-1:0] R93 = W'('h200000 + 93); + localparam [W-1:0] R94 = W'('h200000 + 94); + localparam [W-1:0] R95 = W'('h200000 + 95); + localparam [W-1:0] R96 = W'('h200000 + 96); + localparam [W-1:0] R97 = W'('h200000 + 97); + localparam [W-1:0] R98 = W'('h200000 + 98); + localparam [W-1:0] R99 = W'('h200000 + 99); + typedef struct packed { + logic r0; + logic r1; + logic r2; + logic r3; + logic r4; + logic r5; + logic r6; + logic r7; + logic r8; + logic r9; + logic r10; + logic r11; + logic r12; + logic r13; + logic r14; + logic r15; + logic r16; + logic r17; + logic r18; + logic r19; + logic r20; + logic r21; + logic r22; + logic r23; + logic r24; + logic r25; + logic r26; + logic r27; + logic r28; + logic r29; + logic r30; + logic r31; + logic r32; + logic r33; + logic r34; + logic r35; + logic r36; + logic r37; + logic r38; + logic r39; + logic r40; + logic r41; + logic r42; + logic r43; + logic r44; + logic r45; + logic r46; + logic r47; + logic r48; + logic r49; + logic r50; + logic r51; + logic r52; + logic r53; + logic r54; + logic r55; + logic r56; + logic r57; + logic r58; + logic r59; + logic r60; + logic r61; + logic r62; + logic r63; + logic r64; + logic r65; + logic r66; + logic r67; + logic r68; + logic r69; + logic r70; + logic r71; + logic r72; + logic r73; + logic r74; + logic r75; + logic r76; + logic r77; + logic r78; + logic r79; + logic r80; + logic r81; + logic r82; + logic r83; + logic r84; + logic r85; + logic r86; + logic r87; + logic r88; + logic r89; + logic r90; + logic r91; + logic r92; + logic r93; + logic r94; + logic r95; + logic r96; + logic r97; + logic r98; + logic r99; + } hit_t; + function automatic hit_t get_hit(input logic [22:0] a); + hit_t hit = '0; + unique case (a) + R0: hit.r0 = 1'b1; + R1: hit.r1 = 1'b1; + R2: hit.r2 = 1'b1; + R3: hit.r3 = 1'b1; + R4: hit.r4 = 1'b1; + R5: hit.r5 = 1'b1; + R6: hit.r6 = 1'b1; + R7: hit.r7 = 1'b1; + R8: hit.r8 = 1'b1; + R9: hit.r9 = 1'b1; + R10: hit.r10 = 1'b1; + R11: hit.r11 = 1'b1; + R12: hit.r12 = 1'b1; + R13: hit.r13 = 1'b1; + R14: hit.r14 = 1'b1; + R15: hit.r15 = 1'b1; + R16: hit.r16 = 1'b1; + R17: hit.r17 = 1'b1; + R18: hit.r18 = 1'b1; + R19: hit.r19 = 1'b1; + R20: hit.r20 = 1'b1; + R21: hit.r21 = 1'b1; + R22: hit.r22 = 1'b1; + R23: hit.r23 = 1'b1; + R24: hit.r24 = 1'b1; + R25: hit.r25 = 1'b1; + R26: hit.r26 = 1'b1; + R27: hit.r27 = 1'b1; + R28: hit.r28 = 1'b1; + R29: hit.r29 = 1'b1; + R30: hit.r30 = 1'b1; + R31: hit.r31 = 1'b1; + R32: hit.r32 = 1'b1; + R33: hit.r33 = 1'b1; + R34: hit.r34 = 1'b1; + R35: hit.r35 = 1'b1; + R36: hit.r36 = 1'b1; + R37: hit.r37 = 1'b1; + R38: hit.r38 = 1'b1; + R39: hit.r39 = 1'b1; + R40: hit.r40 = 1'b1; + R41: hit.r41 = 1'b1; + R42: hit.r42 = 1'b1; + R43: hit.r43 = 1'b1; + R44: hit.r44 = 1'b1; + R45: hit.r45 = 1'b1; + R46: hit.r46 = 1'b1; + R47: hit.r47 = 1'b1; + R48: hit.r48 = 1'b1; + R49: hit.r49 = 1'b1; + R50: hit.r50 = 1'b1; + R51: hit.r51 = 1'b1; + R52: hit.r52 = 1'b1; + R53: hit.r53 = 1'b1; + R54: hit.r54 = 1'b1; + R55: hit.r55 = 1'b1; + R56: hit.r56 = 1'b1; + R57: hit.r57 = 1'b1; + R58: hit.r58 = 1'b1; + R59: hit.r59 = 1'b1; + R60: hit.r60 = 1'b1; + R61: hit.r61 = 1'b1; + R62: hit.r62 = 1'b1; + R63: hit.r63 = 1'b1; + R64: hit.r64 = 1'b1; + R65: hit.r65 = 1'b1; + R66: hit.r66 = 1'b1; + R67: hit.r67 = 1'b1; + R68: hit.r68 = 1'b1; + R69: hit.r69 = 1'b1; + R70: hit.r70 = 1'b1; + R71: hit.r71 = 1'b1; + R72: hit.r72 = 1'b1; + R73: hit.r73 = 1'b1; + R74: hit.r74 = 1'b1; + R75: hit.r75 = 1'b1; + R76: hit.r76 = 1'b1; + R77: hit.r77 = 1'b1; + R78: hit.r78 = 1'b1; + R79: hit.r79 = 1'b1; + R80: hit.r80 = 1'b1; + R81: hit.r81 = 1'b1; + R82: hit.r82 = 1'b1; + R83: hit.r83 = 1'b1; + R84: hit.r84 = 1'b1; + R85: hit.r85 = 1'b1; + R86: hit.r86 = 1'b1; + R87: hit.r87 = 1'b1; + R88: hit.r88 = 1'b1; + R89: hit.r89 = 1'b1; + R90: hit.r90 = 1'b1; + R91: hit.r91 = 1'b1; + R92: hit.r92 = 1'b1; + R93: hit.r93 = 1'b1; + R94: hit.r94 = 1'b1; + R95: hit.r95 = 1'b1; + R96: hit.r96 = 1'b1; + R97: hit.r97 = 1'b1; + R98: hit.r98 = 1'b1; + R99: hit.r99 = 1'b1; + default: hit = '0; + endcase + return hit; + endfunction - initial begin - if (get_hit(R30) !== hit_t'{r30: 1'b1, default: '0}) $stop; - if (get_hit('1) !== '0) $stop; - if (get_hit('0) !== '0) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end + initial begin + if (get_hit(R30) !== hit_t'{r30: 1'b1, default: '0}) $stop; + if (get_hit('1) !== '0) $stop; + if (get_hit('0) !== '0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_if_deep.v b/test_regress/t/t_if_deep.v index 20cdcbaa2..26c8a4641 100644 --- a/test_regress/t/t_if_deep.v +++ b/test_regress/t/t_if_deep.v @@ -5,79 +5,79 @@ // SPDX-License-Identifier: CC0-1.0 module t (/*AUTOARG*/ - // Inputs - clk - ); - input clk; + // Inputs + clk + ); + input clk; - integer cyc = 0; - reg [63:0] crc; - reg [63:0] sum; + integer cyc = 0; + reg [63:0] crc; + reg [63:0] sum; - // Take CRC data and apply to testblock inputs - wire [31:0] in = crc[31:0]; + // Take CRC data and apply to testblock inputs + wire [31:0] in = crc[31:0]; - /*AUTOWIRE*/ - // Beginning of automatic wires (for undeclared instantiated-module outputs) - wire [31:0] out; // From test of Test.v - // End of automatics + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [31:0] out; // From test of Test.v + // End of automatics - Test test (/*AUTOINST*/ - // Outputs - .out (out[31:0]), - // Inputs - .clk (clk), - .in (in[31:0])); + Test test (/*AUTOINST*/ + // Outputs + .out (out[31:0]), + // Inputs + .clk (clk), + .in (in[31:0])); - // Aggregate outputs into a single result vector - wire [63:0] result = {32'h0, out}; + // Aggregate outputs into a single result vector + wire [63:0] result = {32'h0, out}; - // What checksum will we end up with + // What checksum will we end up with `define EXPECTED_SUM 64'h966e272fd829e672 - // Test loop - always @ (posedge clk) begin + // Test loop + always @ (posedge clk) begin `ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); + $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); `endif - cyc <= cyc + 1; - crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; - sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; - if (cyc==0) begin - // Setup - crc <= 64'h5aef0c8d_d70a4497; - end - else if (cyc<10) begin - sum <= 64'h0; - end - else if (cyc<90) begin - end - else if (cyc==99) begin - $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); - if (crc !== 64'hc77bb9b3784ea091) $stop; - if (sum !== `EXPECTED_SUM) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end - end + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc<10) begin + sum <= 64'h0; + end + else if (cyc<90) begin + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end endmodule module Test (/*AUTOARG*/ - // Outputs - out, - // Inputs - clk, in - ); + // Outputs + out, + // Inputs + clk, in + ); - input clk; - input [31:0] in; - output [31:0] out; + input clk; + input [31:0] in; + output [31:0] out; - /*AUTOREG*/ - // Beginning of automatic regs (for this module's undeclared outputs) - reg [31:0] out; - // End of automatics + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [31:0] out; + // End of automatics `ifdef verilator `define dontOptimize $c1("1") @@ -85,57 +85,59 @@ module Test (/*AUTOARG*/ `define dontOptimize 1'b1 `endif - always @(posedge clk) begin - out <= in; - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (in[0]) - out <= ~in; - end + always @(posedge clk) begin + out <= in; + // verilog_format: off + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (in[0]) + out <= ~in; + // verilog_format: on + end endmodule diff --git a/test_regress/t/t_math_concat64.v b/test_regress/t/t_math_concat64.v index e30f45a00..c885567c4 100644 --- a/test_regress/t/t_math_concat64.v +++ b/test_regress/t/t_math_concat64.v @@ -5,127 +5,130 @@ // SPDX-License-Identifier: CC0-1.0 module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); - input clk; - integer cyc; initial cyc=1; + input clk; + integer cyc; + initial cyc = 1; - reg [127:0] i; - wire [127:0] q1; - wire [127:0] q32; - wire [127:0] q64; - wire [63:0] q64_low; + reg [127:0] i; + wire [127:0] q1; + wire [127:0] q32; + wire [127:0] q64; + wire [ 63:0] q64_low; - assign q1 = { - i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], - i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], - i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], - i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], + // verilog_format: off + assign q1 = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], - i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], - i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], - i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], - i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1], + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1], - i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], - i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], - i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], - i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], - i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], - i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], - i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], - i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; - assign q64[127:64] = { - i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], - i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], - i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], - i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], + assign q64[127:64] = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], - i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], - i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], - i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], - i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; - assign q64[63:0] = { - i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], - i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], - i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], - i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; + assign q64[63:0] = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], - i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], - i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], - i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], - i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; - assign q64_low = { - i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], - i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], - i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], - i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + assign q64_low = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], - i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], - i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], - i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], - i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; - assign q32[127:96] = { - i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], - i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], - i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], - i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4]}; - assign q32[95:64] = { - i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], - i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], - i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], - i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; - assign q32[63:32] = { - i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], - i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], - i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], - i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2]}; - assign q32[31:0] = { - i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], - i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], - i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], - i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + assign q32[127:96] = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4]}; + assign q32[95:64] = { + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; + assign q32[63:32] = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2]}; + assign q32[31:0] = { + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + // verilog_format: on - always @ (posedge clk) begin - if (cyc!=0) begin - cyc <= cyc + 1; + always @(posedge clk) begin + if (cyc != 0) begin + cyc <= cyc + 1; `ifdef TEST_VERBOSE - $write("%x %x\n", q1, i); + $write("%x %x\n", q1, i); `endif - if (cyc==1) begin - i <= 128'hed388e646c843d35de489bab2413d770; - end - if (cyc==2) begin - i <= 128'h0e17c88f3d5fe51a982646c8e2bd68c3; - if (q1 != 128'h06f0b17c6551e269e3ab07723b26fb10) $stop; - if (q1 != q32) $stop; - if (q1 != q64) $stop; - if (q1[63:0] != q64_low) $stop; - end - if (cyc==3) begin - i <= 128'he236ddfddddbdad20a48e039c9f395b8; - if (q1 != 128'h8c6f018c8a992c979a3e7859f29ac36d) $stop; - if (q1 != q32) $stop; - if (q1 != q64) $stop; - if (q1[63:0] != q64_low) $stop; - end - if (cyc==4) begin - i <= 128'h45e0eb7642b148537491f3da147e7f26; - if (q1 != 128'hf45fc07e4fa8524cf9571425f17f9ad7) $stop; - if (q1 != q32) $stop; - if (q1 != q64) $stop; - if (q1[63:0] != q64_low) $stop; - end - if (cyc==9) begin - $write("*-* All Finished *-*\n"); - $finish; - end + if (cyc == 1) begin + i <= 128'hed388e646c843d35de489bab2413d770; end - end + if (cyc == 2) begin + i <= 128'h0e17c88f3d5fe51a982646c8e2bd68c3; + if (q1 != 128'h06f0b17c6551e269e3ab07723b26fb10) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc == 3) begin + i <= 128'he236ddfddddbdad20a48e039c9f395b8; + if (q1 != 128'h8c6f018c8a992c979a3e7859f29ac36d) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc == 4) begin + i <= 128'h45e0eb7642b148537491f3da147e7f26; + if (q1 != 128'hf45fc07e4fa8524cf9571425f17f9ad7) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc == 9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end endmodule diff --git a/test_regress/t/t_math_cond_huge.v b/test_regress/t/t_math_cond_huge.v index cff1f7099..4864b982c 100644 --- a/test_regress/t/t_math_cond_huge.v +++ b/test_regress/t/t_math_cond_huge.v @@ -5,412 +5,414 @@ // SPDX-License-Identifier: CC0-1.0 module t (/*AUTOARG*/ - // Inputs - clk - ); - input clk; + // Inputs + clk + ); + input clk; - integer cyc = 0; - reg [63:0] crc; - reg [63:0] sum; + integer cyc = 0; + reg [63:0] crc; + reg [63:0] sum; - // Take CRC data and apply to testblock inputs - wire [7:0] sel = crc[7:0]; - wire [255+3:0] in = {crc[2:0],crc,crc,crc,crc}; + // Take CRC data and apply to testblock inputs + wire [7:0] sel = crc[7:0]; + wire [255+3:0] in = {crc[2:0],crc,crc,crc,crc}; - /*AUTOWIRE*/ - // Beginning of automatic wires (for undeclared instantiated-module outputs) - wire [3:0] out; // From test of Test.v - // End of automatics + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [3:0] out; // From test of Test.v + // End of automatics - /* Test AUTO_TEMPLATE ( - .i\([0-9]+\) (in[\1 +:4]), - ); */ + /* Test AUTO_TEMPLATE ( + .i\([0-9]+\) (in[\1 +:4]), + ); */ - Test test (/*AUTOINST*/ - // Outputs - .out (out[3:0]), - // Inputs - .sel (sel[7:0]), - .i0 (in[0 +:4]), // Templated - .i1 (in[1 +:4]), // Templated - .i2 (in[2 +:4]), // Templated - .i3 (in[3 +:4]), // Templated - .i4 (in[4 +:4]), // Templated - .i5 (in[5 +:4]), // Templated - .i6 (in[6 +:4]), // Templated - .i7 (in[7 +:4]), // Templated - .i8 (in[8 +:4]), // Templated - .i9 (in[9 +:4]), // Templated - .i10 (in[10 +:4]), // Templated - .i11 (in[11 +:4]), // Templated - .i12 (in[12 +:4]), // Templated - .i13 (in[13 +:4]), // Templated - .i14 (in[14 +:4]), // Templated - .i15 (in[15 +:4]), // Templated - .i16 (in[16 +:4]), // Templated - .i17 (in[17 +:4]), // Templated - .i18 (in[18 +:4]), // Templated - .i19 (in[19 +:4]), // Templated - .i20 (in[20 +:4]), // Templated - .i21 (in[21 +:4]), // Templated - .i22 (in[22 +:4]), // Templated - .i23 (in[23 +:4]), // Templated - .i24 (in[24 +:4]), // Templated - .i25 (in[25 +:4]), // Templated - .i26 (in[26 +:4]), // Templated - .i27 (in[27 +:4]), // Templated - .i28 (in[28 +:4]), // Templated - .i29 (in[29 +:4]), // Templated - .i30 (in[30 +:4]), // Templated - .i31 (in[31 +:4]), // Templated - .i32 (in[32 +:4]), // Templated - .i33 (in[33 +:4]), // Templated - .i34 (in[34 +:4]), // Templated - .i35 (in[35 +:4]), // Templated - .i36 (in[36 +:4]), // Templated - .i37 (in[37 +:4]), // Templated - .i38 (in[38 +:4]), // Templated - .i39 (in[39 +:4]), // Templated - .i40 (in[40 +:4]), // Templated - .i41 (in[41 +:4]), // Templated - .i42 (in[42 +:4]), // Templated - .i43 (in[43 +:4]), // Templated - .i44 (in[44 +:4]), // Templated - .i45 (in[45 +:4]), // Templated - .i46 (in[46 +:4]), // Templated - .i47 (in[47 +:4]), // Templated - .i48 (in[48 +:4]), // Templated - .i49 (in[49 +:4]), // Templated - .i50 (in[50 +:4]), // Templated - .i51 (in[51 +:4]), // Templated - .i52 (in[52 +:4]), // Templated - .i53 (in[53 +:4]), // Templated - .i54 (in[54 +:4]), // Templated - .i55 (in[55 +:4]), // Templated - .i56 (in[56 +:4]), // Templated - .i57 (in[57 +:4]), // Templated - .i58 (in[58 +:4]), // Templated - .i59 (in[59 +:4]), // Templated - .i60 (in[60 +:4]), // Templated - .i61 (in[61 +:4]), // Templated - .i62 (in[62 +:4]), // Templated - .i63 (in[63 +:4]), // Templated - .i64 (in[64 +:4]), // Templated - .i65 (in[65 +:4]), // Templated - .i66 (in[66 +:4]), // Templated - .i67 (in[67 +:4]), // Templated - .i68 (in[68 +:4]), // Templated - .i69 (in[69 +:4]), // Templated - .i70 (in[70 +:4]), // Templated - .i71 (in[71 +:4]), // Templated - .i72 (in[72 +:4]), // Templated - .i73 (in[73 +:4]), // Templated - .i74 (in[74 +:4]), // Templated - .i75 (in[75 +:4]), // Templated - .i76 (in[76 +:4]), // Templated - .i77 (in[77 +:4]), // Templated - .i78 (in[78 +:4]), // Templated - .i79 (in[79 +:4]), // Templated - .i80 (in[80 +:4]), // Templated - .i81 (in[81 +:4]), // Templated - .i82 (in[82 +:4]), // Templated - .i83 (in[83 +:4]), // Templated - .i84 (in[84 +:4]), // Templated - .i85 (in[85 +:4]), // Templated - .i86 (in[86 +:4]), // Templated - .i87 (in[87 +:4]), // Templated - .i88 (in[88 +:4]), // Templated - .i89 (in[89 +:4]), // Templated - .i90 (in[90 +:4]), // Templated - .i91 (in[91 +:4]), // Templated - .i92 (in[92 +:4]), // Templated - .i93 (in[93 +:4]), // Templated - .i94 (in[94 +:4]), // Templated - .i95 (in[95 +:4]), // Templated - .i96 (in[96 +:4]), // Templated - .i97 (in[97 +:4]), // Templated - .i98 (in[98 +:4]), // Templated - .i99 (in[99 +:4]), // Templated - .i100 (in[100 +:4]), // Templated - .i101 (in[101 +:4]), // Templated - .i102 (in[102 +:4]), // Templated - .i103 (in[103 +:4]), // Templated - .i104 (in[104 +:4]), // Templated - .i105 (in[105 +:4]), // Templated - .i106 (in[106 +:4]), // Templated - .i107 (in[107 +:4]), // Templated - .i108 (in[108 +:4]), // Templated - .i109 (in[109 +:4]), // Templated - .i110 (in[110 +:4]), // Templated - .i111 (in[111 +:4]), // Templated - .i112 (in[112 +:4]), // Templated - .i113 (in[113 +:4]), // Templated - .i114 (in[114 +:4]), // Templated - .i115 (in[115 +:4]), // Templated - .i116 (in[116 +:4]), // Templated - .i117 (in[117 +:4]), // Templated - .i118 (in[118 +:4]), // Templated - .i119 (in[119 +:4]), // Templated - .i120 (in[120 +:4]), // Templated - .i121 (in[121 +:4]), // Templated - .i122 (in[122 +:4]), // Templated - .i123 (in[123 +:4]), // Templated - .i124 (in[124 +:4]), // Templated - .i125 (in[125 +:4]), // Templated - .i126 (in[126 +:4]), // Templated - .i127 (in[127 +:4]), // Templated - .i128 (in[128 +:4]), // Templated - .i129 (in[129 +:4]), // Templated - .i130 (in[130 +:4]), // Templated - .i131 (in[131 +:4]), // Templated - .i132 (in[132 +:4]), // Templated - .i133 (in[133 +:4]), // Templated - .i134 (in[134 +:4]), // Templated - .i135 (in[135 +:4]), // Templated - .i136 (in[136 +:4]), // Templated - .i137 (in[137 +:4]), // Templated - .i138 (in[138 +:4]), // Templated - .i139 (in[139 +:4]), // Templated - .i140 (in[140 +:4]), // Templated - .i141 (in[141 +:4]), // Templated - .i142 (in[142 +:4]), // Templated - .i143 (in[143 +:4]), // Templated - .i144 (in[144 +:4]), // Templated - .i145 (in[145 +:4]), // Templated - .i146 (in[146 +:4]), // Templated - .i147 (in[147 +:4]), // Templated - .i148 (in[148 +:4]), // Templated - .i149 (in[149 +:4]), // Templated - .i150 (in[150 +:4]), // Templated - .i151 (in[151 +:4]), // Templated - .i152 (in[152 +:4]), // Templated - .i153 (in[153 +:4]), // Templated - .i154 (in[154 +:4]), // Templated - .i155 (in[155 +:4]), // Templated - .i156 (in[156 +:4]), // Templated - .i157 (in[157 +:4]), // Templated - .i158 (in[158 +:4]), // Templated - .i159 (in[159 +:4]), // Templated - .i160 (in[160 +:4]), // Templated - .i161 (in[161 +:4]), // Templated - .i162 (in[162 +:4]), // Templated - .i163 (in[163 +:4]), // Templated - .i164 (in[164 +:4]), // Templated - .i165 (in[165 +:4]), // Templated - .i166 (in[166 +:4]), // Templated - .i167 (in[167 +:4]), // Templated - .i168 (in[168 +:4]), // Templated - .i169 (in[169 +:4]), // Templated - .i170 (in[170 +:4]), // Templated - .i171 (in[171 +:4]), // Templated - .i172 (in[172 +:4]), // Templated - .i173 (in[173 +:4]), // Templated - .i174 (in[174 +:4]), // Templated - .i175 (in[175 +:4]), // Templated - .i176 (in[176 +:4]), // Templated - .i177 (in[177 +:4]), // Templated - .i178 (in[178 +:4]), // Templated - .i179 (in[179 +:4]), // Templated - .i180 (in[180 +:4]), // Templated - .i181 (in[181 +:4]), // Templated - .i182 (in[182 +:4]), // Templated - .i183 (in[183 +:4]), // Templated - .i184 (in[184 +:4]), // Templated - .i185 (in[185 +:4]), // Templated - .i186 (in[186 +:4]), // Templated - .i187 (in[187 +:4]), // Templated - .i188 (in[188 +:4]), // Templated - .i189 (in[189 +:4]), // Templated - .i190 (in[190 +:4]), // Templated - .i191 (in[191 +:4]), // Templated - .i192 (in[192 +:4]), // Templated - .i193 (in[193 +:4]), // Templated - .i194 (in[194 +:4]), // Templated - .i195 (in[195 +:4]), // Templated - .i196 (in[196 +:4]), // Templated - .i197 (in[197 +:4]), // Templated - .i198 (in[198 +:4]), // Templated - .i199 (in[199 +:4]), // Templated - .i200 (in[200 +:4]), // Templated - .i201 (in[201 +:4]), // Templated - .i202 (in[202 +:4]), // Templated - .i203 (in[203 +:4]), // Templated - .i204 (in[204 +:4]), // Templated - .i205 (in[205 +:4]), // Templated - .i206 (in[206 +:4]), // Templated - .i207 (in[207 +:4]), // Templated - .i208 (in[208 +:4]), // Templated - .i209 (in[209 +:4]), // Templated - .i210 (in[210 +:4]), // Templated - .i211 (in[211 +:4]), // Templated - .i212 (in[212 +:4]), // Templated - .i213 (in[213 +:4]), // Templated - .i214 (in[214 +:4]), // Templated - .i215 (in[215 +:4]), // Templated - .i216 (in[216 +:4]), // Templated - .i217 (in[217 +:4]), // Templated - .i218 (in[218 +:4]), // Templated - .i219 (in[219 +:4]), // Templated - .i220 (in[220 +:4]), // Templated - .i221 (in[221 +:4]), // Templated - .i222 (in[222 +:4]), // Templated - .i223 (in[223 +:4]), // Templated - .i224 (in[224 +:4]), // Templated - .i225 (in[225 +:4]), // Templated - .i226 (in[226 +:4]), // Templated - .i227 (in[227 +:4]), // Templated - .i228 (in[228 +:4]), // Templated - .i229 (in[229 +:4]), // Templated - .i230 (in[230 +:4]), // Templated - .i231 (in[231 +:4]), // Templated - .i232 (in[232 +:4]), // Templated - .i233 (in[233 +:4]), // Templated - .i234 (in[234 +:4]), // Templated - .i235 (in[235 +:4]), // Templated - .i236 (in[236 +:4]), // Templated - .i237 (in[237 +:4]), // Templated - .i238 (in[238 +:4]), // Templated - .i239 (in[239 +:4]), // Templated - .i240 (in[240 +:4]), // Templated - .i241 (in[241 +:4]), // Templated - .i242 (in[242 +:4]), // Templated - .i243 (in[243 +:4]), // Templated - .i244 (in[244 +:4]), // Templated - .i245 (in[245 +:4]), // Templated - .i246 (in[246 +:4]), // Templated - .i247 (in[247 +:4]), // Templated - .i248 (in[248 +:4]), // Templated - .i249 (in[249 +:4]), // Templated - .i250 (in[250 +:4]), // Templated - .i251 (in[251 +:4]), // Templated - .i252 (in[252 +:4]), // Templated - .i253 (in[253 +:4]), // Templated - .i254 (in[254 +:4]), // Templated - .i255 (in[255 +:4])); // Templated + Test test (/*AUTOINST*/ + // Outputs + .out (out[3:0]), + // Inputs + .sel (sel[7:0]), + .i0 (in[0 +:4]), // Templated + .i1 (in[1 +:4]), // Templated + .i2 (in[2 +:4]), // Templated + .i3 (in[3 +:4]), // Templated + .i4 (in[4 +:4]), // Templated + .i5 (in[5 +:4]), // Templated + .i6 (in[6 +:4]), // Templated + .i7 (in[7 +:4]), // Templated + .i8 (in[8 +:4]), // Templated + .i9 (in[9 +:4]), // Templated + .i10 (in[10 +:4]), // Templated + .i11 (in[11 +:4]), // Templated + .i12 (in[12 +:4]), // Templated + .i13 (in[13 +:4]), // Templated + .i14 (in[14 +:4]), // Templated + .i15 (in[15 +:4]), // Templated + .i16 (in[16 +:4]), // Templated + .i17 (in[17 +:4]), // Templated + .i18 (in[18 +:4]), // Templated + .i19 (in[19 +:4]), // Templated + .i20 (in[20 +:4]), // Templated + .i21 (in[21 +:4]), // Templated + .i22 (in[22 +:4]), // Templated + .i23 (in[23 +:4]), // Templated + .i24 (in[24 +:4]), // Templated + .i25 (in[25 +:4]), // Templated + .i26 (in[26 +:4]), // Templated + .i27 (in[27 +:4]), // Templated + .i28 (in[28 +:4]), // Templated + .i29 (in[29 +:4]), // Templated + .i30 (in[30 +:4]), // Templated + .i31 (in[31 +:4]), // Templated + .i32 (in[32 +:4]), // Templated + .i33 (in[33 +:4]), // Templated + .i34 (in[34 +:4]), // Templated + .i35 (in[35 +:4]), // Templated + .i36 (in[36 +:4]), // Templated + .i37 (in[37 +:4]), // Templated + .i38 (in[38 +:4]), // Templated + .i39 (in[39 +:4]), // Templated + .i40 (in[40 +:4]), // Templated + .i41 (in[41 +:4]), // Templated + .i42 (in[42 +:4]), // Templated + .i43 (in[43 +:4]), // Templated + .i44 (in[44 +:4]), // Templated + .i45 (in[45 +:4]), // Templated + .i46 (in[46 +:4]), // Templated + .i47 (in[47 +:4]), // Templated + .i48 (in[48 +:4]), // Templated + .i49 (in[49 +:4]), // Templated + .i50 (in[50 +:4]), // Templated + .i51 (in[51 +:4]), // Templated + .i52 (in[52 +:4]), // Templated + .i53 (in[53 +:4]), // Templated + .i54 (in[54 +:4]), // Templated + .i55 (in[55 +:4]), // Templated + .i56 (in[56 +:4]), // Templated + .i57 (in[57 +:4]), // Templated + .i58 (in[58 +:4]), // Templated + .i59 (in[59 +:4]), // Templated + .i60 (in[60 +:4]), // Templated + .i61 (in[61 +:4]), // Templated + .i62 (in[62 +:4]), // Templated + .i63 (in[63 +:4]), // Templated + .i64 (in[64 +:4]), // Templated + .i65 (in[65 +:4]), // Templated + .i66 (in[66 +:4]), // Templated + .i67 (in[67 +:4]), // Templated + .i68 (in[68 +:4]), // Templated + .i69 (in[69 +:4]), // Templated + .i70 (in[70 +:4]), // Templated + .i71 (in[71 +:4]), // Templated + .i72 (in[72 +:4]), // Templated + .i73 (in[73 +:4]), // Templated + .i74 (in[74 +:4]), // Templated + .i75 (in[75 +:4]), // Templated + .i76 (in[76 +:4]), // Templated + .i77 (in[77 +:4]), // Templated + .i78 (in[78 +:4]), // Templated + .i79 (in[79 +:4]), // Templated + .i80 (in[80 +:4]), // Templated + .i81 (in[81 +:4]), // Templated + .i82 (in[82 +:4]), // Templated + .i83 (in[83 +:4]), // Templated + .i84 (in[84 +:4]), // Templated + .i85 (in[85 +:4]), // Templated + .i86 (in[86 +:4]), // Templated + .i87 (in[87 +:4]), // Templated + .i88 (in[88 +:4]), // Templated + .i89 (in[89 +:4]), // Templated + .i90 (in[90 +:4]), // Templated + .i91 (in[91 +:4]), // Templated + .i92 (in[92 +:4]), // Templated + .i93 (in[93 +:4]), // Templated + .i94 (in[94 +:4]), // Templated + .i95 (in[95 +:4]), // Templated + .i96 (in[96 +:4]), // Templated + .i97 (in[97 +:4]), // Templated + .i98 (in[98 +:4]), // Templated + .i99 (in[99 +:4]), // Templated + .i100 (in[100 +:4]), // Templated + .i101 (in[101 +:4]), // Templated + .i102 (in[102 +:4]), // Templated + .i103 (in[103 +:4]), // Templated + .i104 (in[104 +:4]), // Templated + .i105 (in[105 +:4]), // Templated + .i106 (in[106 +:4]), // Templated + .i107 (in[107 +:4]), // Templated + .i108 (in[108 +:4]), // Templated + .i109 (in[109 +:4]), // Templated + .i110 (in[110 +:4]), // Templated + .i111 (in[111 +:4]), // Templated + .i112 (in[112 +:4]), // Templated + .i113 (in[113 +:4]), // Templated + .i114 (in[114 +:4]), // Templated + .i115 (in[115 +:4]), // Templated + .i116 (in[116 +:4]), // Templated + .i117 (in[117 +:4]), // Templated + .i118 (in[118 +:4]), // Templated + .i119 (in[119 +:4]), // Templated + .i120 (in[120 +:4]), // Templated + .i121 (in[121 +:4]), // Templated + .i122 (in[122 +:4]), // Templated + .i123 (in[123 +:4]), // Templated + .i124 (in[124 +:4]), // Templated + .i125 (in[125 +:4]), // Templated + .i126 (in[126 +:4]), // Templated + .i127 (in[127 +:4]), // Templated + .i128 (in[128 +:4]), // Templated + .i129 (in[129 +:4]), // Templated + .i130 (in[130 +:4]), // Templated + .i131 (in[131 +:4]), // Templated + .i132 (in[132 +:4]), // Templated + .i133 (in[133 +:4]), // Templated + .i134 (in[134 +:4]), // Templated + .i135 (in[135 +:4]), // Templated + .i136 (in[136 +:4]), // Templated + .i137 (in[137 +:4]), // Templated + .i138 (in[138 +:4]), // Templated + .i139 (in[139 +:4]), // Templated + .i140 (in[140 +:4]), // Templated + .i141 (in[141 +:4]), // Templated + .i142 (in[142 +:4]), // Templated + .i143 (in[143 +:4]), // Templated + .i144 (in[144 +:4]), // Templated + .i145 (in[145 +:4]), // Templated + .i146 (in[146 +:4]), // Templated + .i147 (in[147 +:4]), // Templated + .i148 (in[148 +:4]), // Templated + .i149 (in[149 +:4]), // Templated + .i150 (in[150 +:4]), // Templated + .i151 (in[151 +:4]), // Templated + .i152 (in[152 +:4]), // Templated + .i153 (in[153 +:4]), // Templated + .i154 (in[154 +:4]), // Templated + .i155 (in[155 +:4]), // Templated + .i156 (in[156 +:4]), // Templated + .i157 (in[157 +:4]), // Templated + .i158 (in[158 +:4]), // Templated + .i159 (in[159 +:4]), // Templated + .i160 (in[160 +:4]), // Templated + .i161 (in[161 +:4]), // Templated + .i162 (in[162 +:4]), // Templated + .i163 (in[163 +:4]), // Templated + .i164 (in[164 +:4]), // Templated + .i165 (in[165 +:4]), // Templated + .i166 (in[166 +:4]), // Templated + .i167 (in[167 +:4]), // Templated + .i168 (in[168 +:4]), // Templated + .i169 (in[169 +:4]), // Templated + .i170 (in[170 +:4]), // Templated + .i171 (in[171 +:4]), // Templated + .i172 (in[172 +:4]), // Templated + .i173 (in[173 +:4]), // Templated + .i174 (in[174 +:4]), // Templated + .i175 (in[175 +:4]), // Templated + .i176 (in[176 +:4]), // Templated + .i177 (in[177 +:4]), // Templated + .i178 (in[178 +:4]), // Templated + .i179 (in[179 +:4]), // Templated + .i180 (in[180 +:4]), // Templated + .i181 (in[181 +:4]), // Templated + .i182 (in[182 +:4]), // Templated + .i183 (in[183 +:4]), // Templated + .i184 (in[184 +:4]), // Templated + .i185 (in[185 +:4]), // Templated + .i186 (in[186 +:4]), // Templated + .i187 (in[187 +:4]), // Templated + .i188 (in[188 +:4]), // Templated + .i189 (in[189 +:4]), // Templated + .i190 (in[190 +:4]), // Templated + .i191 (in[191 +:4]), // Templated + .i192 (in[192 +:4]), // Templated + .i193 (in[193 +:4]), // Templated + .i194 (in[194 +:4]), // Templated + .i195 (in[195 +:4]), // Templated + .i196 (in[196 +:4]), // Templated + .i197 (in[197 +:4]), // Templated + .i198 (in[198 +:4]), // Templated + .i199 (in[199 +:4]), // Templated + .i200 (in[200 +:4]), // Templated + .i201 (in[201 +:4]), // Templated + .i202 (in[202 +:4]), // Templated + .i203 (in[203 +:4]), // Templated + .i204 (in[204 +:4]), // Templated + .i205 (in[205 +:4]), // Templated + .i206 (in[206 +:4]), // Templated + .i207 (in[207 +:4]), // Templated + .i208 (in[208 +:4]), // Templated + .i209 (in[209 +:4]), // Templated + .i210 (in[210 +:4]), // Templated + .i211 (in[211 +:4]), // Templated + .i212 (in[212 +:4]), // Templated + .i213 (in[213 +:4]), // Templated + .i214 (in[214 +:4]), // Templated + .i215 (in[215 +:4]), // Templated + .i216 (in[216 +:4]), // Templated + .i217 (in[217 +:4]), // Templated + .i218 (in[218 +:4]), // Templated + .i219 (in[219 +:4]), // Templated + .i220 (in[220 +:4]), // Templated + .i221 (in[221 +:4]), // Templated + .i222 (in[222 +:4]), // Templated + .i223 (in[223 +:4]), // Templated + .i224 (in[224 +:4]), // Templated + .i225 (in[225 +:4]), // Templated + .i226 (in[226 +:4]), // Templated + .i227 (in[227 +:4]), // Templated + .i228 (in[228 +:4]), // Templated + .i229 (in[229 +:4]), // Templated + .i230 (in[230 +:4]), // Templated + .i231 (in[231 +:4]), // Templated + .i232 (in[232 +:4]), // Templated + .i233 (in[233 +:4]), // Templated + .i234 (in[234 +:4]), // Templated + .i235 (in[235 +:4]), // Templated + .i236 (in[236 +:4]), // Templated + .i237 (in[237 +:4]), // Templated + .i238 (in[238 +:4]), // Templated + .i239 (in[239 +:4]), // Templated + .i240 (in[240 +:4]), // Templated + .i241 (in[241 +:4]), // Templated + .i242 (in[242 +:4]), // Templated + .i243 (in[243 +:4]), // Templated + .i244 (in[244 +:4]), // Templated + .i245 (in[245 +:4]), // Templated + .i246 (in[246 +:4]), // Templated + .i247 (in[247 +:4]), // Templated + .i248 (in[248 +:4]), // Templated + .i249 (in[249 +:4]), // Templated + .i250 (in[250 +:4]), // Templated + .i251 (in[251 +:4]), // Templated + .i252 (in[252 +:4]), // Templated + .i253 (in[253 +:4]), // Templated + .i254 (in[254 +:4]), // Templated + .i255 (in[255 +:4])); // Templated - // Aggregate outputs into a single result vector - wire [63:0] result = {60'h0, out}; + // Aggregate outputs into a single result vector + wire [63:0] result = {60'h0, out}; - // What checksum will we end up with + // What checksum will we end up with `define EXPECTED_SUM 64'h36f3051d15caf07a - // Test loop - always @ (posedge clk) begin + // Test loop + always @ (posedge clk) begin `ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); + $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); `endif - cyc <= cyc + 1; - crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; - sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; - if (cyc==0) begin - // Setup - crc <= 64'h5aef0c8d_d70a4497; - end - else if (cyc<10) begin - sum <= 64'h0; - end - else if (cyc<90) begin - end - else if (cyc==99) begin - $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); - if (crc !== 64'hc77bb9b3784ea091) $stop; - if (sum !== `EXPECTED_SUM) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end - end + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc<10) begin + sum <= 64'h0; + end + else if (cyc<90) begin + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end endmodule module Test ( output wire [3:0] out, - input [7:0] sel, + input [7:0] sel, - input [3:0] i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, - i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, - i34, i35, i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, - i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65, i66, i67, - i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80, i81, i82, i83, i84, - i85, i86, i87, i88, i89, i90, i91, i92, i93, i94, i95, i96, i97, i98, i99, i100, i101, - i102, i103, i104, i105, i106, i107, i108, i109, i110, i111, i112, i113, i114, i115, - i116, i117, i118, i119, i120, i121, i122, i123, i124, i125, i126, i127, i128, i129, - i130, i131, i132, i133, i134, i135, i136, i137, i138, i139, i140, i141, i142, i143, - i144, i145, i146, i147, i148, i149, i150, i151, i152, i153, i154, i155, i156, i157, - i158, i159, i160, i161, i162, i163, i164, i165, i166, i167, i168, i169, i170, i171, - i172, i173, i174, i175, i176, i177, i178, i179, i180, i181, i182, i183, i184, i185, - i186, i187, i188, i189, i190, i191, i192, i193, i194, i195, i196, i197, i198, i199, - i200, i201, i202, i203, i204, i205, i206, i207, i208, i209, i210, i211, i212, i213, - i214, i215, i216, i217, i218, i219, i220, i221, i222, i223, i224, i225, i226, i227, - i228, i229, i230, i231, i232, i233, i234, i235, i236, i237, i238, i239, i240, i241, - i242, i243, i244, i245, i246, i247, i248, i249, i250, i251, i252, i253, i254, i255 - ); + // verilog_format: off + input [3:0] i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, + i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, + i34, i35, i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, + i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65, i66, i67, + i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80, i81, i82, i83, i84, + i85, i86, i87, i88, i89, i90, i91, i92, i93, i94, i95, i96, i97, i98, i99, i100, i101, + i102, i103, i104, i105, i106, i107, i108, i109, i110, i111, i112, i113, i114, i115, + i116, i117, i118, i119, i120, i121, i122, i123, i124, i125, i126, i127, i128, i129, + i130, i131, i132, i133, i134, i135, i136, i137, i138, i139, i140, i141, i142, i143, + i144, i145, i146, i147, i148, i149, i150, i151, i152, i153, i154, i155, i156, i157, + i158, i159, i160, i161, i162, i163, i164, i165, i166, i167, i168, i169, i170, i171, + i172, i173, i174, i175, i176, i177, i178, i179, i180, i181, i182, i183, i184, i185, + i186, i187, i188, i189, i190, i191, i192, i193, i194, i195, i196, i197, i198, i199, + i200, i201, i202, i203, i204, i205, i206, i207, i208, i209, i210, i211, i212, i213, + i214, i215, i216, i217, i218, i219, i220, i221, i222, i223, i224, i225, i226, i227, + i228, i229, i230, i231, i232, i233, i234, i235, i236, i237, i238, i239, i240, i241, + i242, i243, i244, i245, i246, i247, i248, i249, i250, i251, i252, i253, i254, i255 + // verilog_format: on + ); - assign out - = (sel==8'h00) ? i0 : (sel==8'h01) ? i1 : (sel==8'h02) ? i2 : (sel==8'h03) ? i3 - : (sel==8'h04) ? i4 : (sel==8'h05) ? i5 : (sel==8'h06) ? i6 : (sel==8'h07) ? i7 - : (sel==8'h08) ? i8 : (sel==8'h09) ? i9 : (sel==8'h0a) ? i10 : (sel==8'h0b) ? i11 - : (sel==8'h0c) ? i12 : (sel==8'h0d) ? i13 : (sel==8'h0e) ? i14 : (sel==8'h0f) ? i15 - : (sel==8'h10) ? i16 : (sel==8'h11) ? i17 : (sel==8'h12) ? i18 : (sel==8'h13) ? i19 - : (sel==8'h14) ? i20 : (sel==8'h15) ? i21 : (sel==8'h16) ? i22 : (sel==8'h17) ? i23 - : (sel==8'h18) ? i24 : (sel==8'h19) ? i25 : (sel==8'h1a) ? i26 : (sel==8'h1b) ? i27 - : (sel==8'h1c) ? i28 : (sel==8'h1d) ? i29 : (sel==8'h1e) ? i30 : (sel==8'h1f) ? i31 - : (sel==8'h20) ? i32 : (sel==8'h21) ? i33 : (sel==8'h22) ? i34 : (sel==8'h23) ? i35 - : (sel==8'h24) ? i36 : (sel==8'h25) ? i37 : (sel==8'h26) ? i38 : (sel==8'h27) ? i39 - : (sel==8'h28) ? i40 : (sel==8'h29) ? i41 : (sel==8'h2a) ? i42 : (sel==8'h2b) ? i43 - : (sel==8'h2c) ? i44 : (sel==8'h2d) ? i45 : (sel==8'h2e) ? i46 : (sel==8'h2f) ? i47 - : (sel==8'h30) ? i48 : (sel==8'h31) ? i49 : (sel==8'h32) ? i50 : (sel==8'h33) ? i51 - : (sel==8'h34) ? i52 : (sel==8'h35) ? i53 : (sel==8'h36) ? i54 : (sel==8'h37) ? i55 - : (sel==8'h38) ? i56 : (sel==8'h39) ? i57 : (sel==8'h3a) ? i58 : (sel==8'h3b) ? i59 - : (sel==8'h3c) ? i60 : (sel==8'h3d) ? i61 : (sel==8'h3e) ? i62 : (sel==8'h3f) ? i63 - : (sel==8'h40) ? i64 : (sel==8'h41) ? i65 : (sel==8'h42) ? i66 : (sel==8'h43) ? i67 - : (sel==8'h44) ? i68 : (sel==8'h45) ? i69 : (sel==8'h46) ? i70 : (sel==8'h47) ? i71 - : (sel==8'h48) ? i72 : (sel==8'h49) ? i73 : (sel==8'h4a) ? i74 : (sel==8'h4b) ? i75 - : (sel==8'h4c) ? i76 : (sel==8'h4d) ? i77 : (sel==8'h4e) ? i78 : (sel==8'h4f) ? i79 - : (sel==8'h50) ? i80 : (sel==8'h51) ? i81 : (sel==8'h52) ? i82 : (sel==8'h53) ? i83 - : (sel==8'h54) ? i84 : (sel==8'h55) ? i85 : (sel==8'h56) ? i86 : (sel==8'h57) ? i87 - : (sel==8'h58) ? i88 : (sel==8'h59) ? i89 : (sel==8'h5a) ? i90 : (sel==8'h5b) ? i91 - : (sel==8'h5c) ? i92 : (sel==8'h5d) ? i93 : (sel==8'h5e) ? i94 : (sel==8'h5f) ? i95 - : (sel==8'h60) ? i96 : (sel==8'h61) ? i97 : (sel==8'h62) ? i98 : (sel==8'h63) ? i99 - : (sel==8'h64) ? i100 : (sel==8'h65) ? i101 : (sel==8'h66) ? i102 : (sel==8'h67) ? i103 - : (sel==8'h68) ? i104 : (sel==8'h69) ? i105 : (sel==8'h6a) ? i106 : (sel==8'h6b) ? i107 - : (sel==8'h6c) ? i108 : (sel==8'h6d) ? i109 : (sel==8'h6e) ? i110 : (sel==8'h6f) ? i111 - : (sel==8'h70) ? i112 : (sel==8'h71) ? i113 : (sel==8'h72) ? i114 : (sel==8'h73) ? i115 - : (sel==8'h74) ? i116 : (sel==8'h75) ? i117 : (sel==8'h76) ? i118 : (sel==8'h77) ? i119 - : (sel==8'h78) ? i120 : (sel==8'h79) ? i121 : (sel==8'h7a) ? i122 : (sel==8'h7b) ? i123 - : (sel==8'h7c) ? i124 : (sel==8'h7d) ? i125 : (sel==8'h7e) ? i126 : (sel==8'h7f) ? i127 - : (sel==8'h80) ? i128 : (sel==8'h81) ? i129 : (sel==8'h82) ? i130 : (sel==8'h83) ? i131 - : (sel==8'h84) ? i132 : (sel==8'h85) ? i133 : (sel==8'h86) ? i134 : (sel==8'h87) ? i135 - : (sel==8'h88) ? i136 : (sel==8'h89) ? i137 : (sel==8'h8a) ? i138 : (sel==8'h8b) ? i139 - : (sel==8'h8c) ? i140 : (sel==8'h8d) ? i141 : (sel==8'h8e) ? i142 : (sel==8'h8f) ? i143 - : (sel==8'h90) ? i144 : (sel==8'h91) ? i145 : (sel==8'h92) ? i146 : (sel==8'h93) ? i147 - : (sel==8'h94) ? i148 : (sel==8'h95) ? i149 : (sel==8'h96) ? i150 : (sel==8'h98) ? i151 - : (sel==8'h99) ? i152 : (sel==8'h9a) ? i153 : (sel==8'h9b) ? i154 : (sel==8'h9c) ? i155 - : (sel==8'h9d) ? i156 : (sel==8'h9e) ? i157 : (sel==8'h9f) ? i158 : (sel==8'ha0) ? i159 - : (sel==8'ha1) ? i160 : (sel==8'ha2) ? i161 : (sel==8'ha3) ? i162 : (sel==8'ha4) ? i163 - : (sel==8'ha5) ? i164 : (sel==8'ha6) ? i165 : (sel==8'ha7) ? i166 : (sel==8'ha8) ? i167 - : (sel==8'ha9) ? i168 : (sel==8'haa) ? i169 : (sel==8'hab) ? i170 : (sel==8'hac) ? i171 - : (sel==8'had) ? i172 : (sel==8'hae) ? i173 : (sel==8'haf) ? i174 : (sel==8'hb0) ? i175 - : (sel==8'hb1) ? i176 : (sel==8'hb2) ? i177 : (sel==8'hb3) ? i178 : (sel==8'hb4) ? i179 - : (sel==8'hb5) ? i180 : (sel==8'hb6) ? i181 : (sel==8'hb7) ? i182 : (sel==8'hb8) ? i183 - : (sel==8'hb9) ? i184 : (sel==8'hba) ? i185 : (sel==8'hbb) ? i186 : (sel==8'hbc) ? i187 - : (sel==8'hbd) ? i188 : (sel==8'hbe) ? i189 : (sel==8'hbf) ? i190 : (sel==8'hc0) ? i191 - : (sel==8'hc1) ? i192 : (sel==8'hc2) ? i193 : (sel==8'hc3) ? i194 : (sel==8'hc4) ? i195 - : (sel==8'hc5) ? i196 : (sel==8'hc6) ? i197 : (sel==8'hc7) ? i198 : (sel==8'hc8) ? i199 - : (sel==8'hc9) ? i200 : (sel==8'hca) ? i201 : (sel==8'hcb) ? i202 : (sel==8'hcc) ? i203 - : (sel==8'hcd) ? i204 : (sel==8'hce) ? i205 : (sel==8'hcf) ? i206 : (sel==8'hd0) ? i207 - : (sel==8'hd1) ? i208 : (sel==8'hd2) ? i209 : (sel==8'hd3) ? i210 : (sel==8'hd4) ? i211 - : (sel==8'hd5) ? i212 : (sel==8'hd6) ? i213 : (sel==8'hd7) ? i214 : (sel==8'hd8) ? i215 - : (sel==8'hd9) ? i216 : (sel==8'hda) ? i217 : (sel==8'hdb) ? i218 : (sel==8'hdc) ? i219 - : (sel==8'hdd) ? i220 : (sel==8'hde) ? i221 : (sel==8'hdf) ? i222 : (sel==8'he0) ? i223 - : (sel==8'he1) ? i224 : (sel==8'he2) ? i225 : (sel==8'he3) ? i226 : (sel==8'he4) ? i227 - : (sel==8'he5) ? i228 : (sel==8'he6) ? i229 : (sel==8'he7) ? i230 : (sel==8'he8) ? i231 - : (sel==8'he9) ? i232 : (sel==8'hea) ? i233 : (sel==8'heb) ? i234 : (sel==8'hec) ? i235 - : (sel==8'hed) ? i236 : (sel==8'hee) ? i237 : (sel==8'hef) ? i238 : (sel==8'hf0) ? i239 - : (sel==8'hf1) ? i240 : (sel==8'hf2) ? i241 : (sel==8'hf3) ? i242 : (sel==8'hf4) ? i243 - : (sel==8'hf5) ? i244 : (sel==8'hf6) ? i245 : (sel==8'hf7) ? i246 : (sel==8'hf8) ? i247 - : (sel==8'hf9) ? i248 : (sel==8'hfa) ? i249 : (sel==8'hfb) ? i250 : (sel==8'hfc) ? i251 - : (sel==8'hfd) ? i252 : (sel==8'hfe) ? i253 : (sel==8'hff) ? i254 : i255; + assign out + = (sel==8'h00) ? i0 : (sel==8'h01) ? i1 : (sel==8'h02) ? i2 : (sel==8'h03) ? i3 + : (sel==8'h04) ? i4 : (sel==8'h05) ? i5 : (sel==8'h06) ? i6 : (sel==8'h07) ? i7 + : (sel==8'h08) ? i8 : (sel==8'h09) ? i9 : (sel==8'h0a) ? i10 : (sel==8'h0b) ? i11 + : (sel==8'h0c) ? i12 : (sel==8'h0d) ? i13 : (sel==8'h0e) ? i14 : (sel==8'h0f) ? i15 + : (sel==8'h10) ? i16 : (sel==8'h11) ? i17 : (sel==8'h12) ? i18 : (sel==8'h13) ? i19 + : (sel==8'h14) ? i20 : (sel==8'h15) ? i21 : (sel==8'h16) ? i22 : (sel==8'h17) ? i23 + : (sel==8'h18) ? i24 : (sel==8'h19) ? i25 : (sel==8'h1a) ? i26 : (sel==8'h1b) ? i27 + : (sel==8'h1c) ? i28 : (sel==8'h1d) ? i29 : (sel==8'h1e) ? i30 : (sel==8'h1f) ? i31 + : (sel==8'h20) ? i32 : (sel==8'h21) ? i33 : (sel==8'h22) ? i34 : (sel==8'h23) ? i35 + : (sel==8'h24) ? i36 : (sel==8'h25) ? i37 : (sel==8'h26) ? i38 : (sel==8'h27) ? i39 + : (sel==8'h28) ? i40 : (sel==8'h29) ? i41 : (sel==8'h2a) ? i42 : (sel==8'h2b) ? i43 + : (sel==8'h2c) ? i44 : (sel==8'h2d) ? i45 : (sel==8'h2e) ? i46 : (sel==8'h2f) ? i47 + : (sel==8'h30) ? i48 : (sel==8'h31) ? i49 : (sel==8'h32) ? i50 : (sel==8'h33) ? i51 + : (sel==8'h34) ? i52 : (sel==8'h35) ? i53 : (sel==8'h36) ? i54 : (sel==8'h37) ? i55 + : (sel==8'h38) ? i56 : (sel==8'h39) ? i57 : (sel==8'h3a) ? i58 : (sel==8'h3b) ? i59 + : (sel==8'h3c) ? i60 : (sel==8'h3d) ? i61 : (sel==8'h3e) ? i62 : (sel==8'h3f) ? i63 + : (sel==8'h40) ? i64 : (sel==8'h41) ? i65 : (sel==8'h42) ? i66 : (sel==8'h43) ? i67 + : (sel==8'h44) ? i68 : (sel==8'h45) ? i69 : (sel==8'h46) ? i70 : (sel==8'h47) ? i71 + : (sel==8'h48) ? i72 : (sel==8'h49) ? i73 : (sel==8'h4a) ? i74 : (sel==8'h4b) ? i75 + : (sel==8'h4c) ? i76 : (sel==8'h4d) ? i77 : (sel==8'h4e) ? i78 : (sel==8'h4f) ? i79 + : (sel==8'h50) ? i80 : (sel==8'h51) ? i81 : (sel==8'h52) ? i82 : (sel==8'h53) ? i83 + : (sel==8'h54) ? i84 : (sel==8'h55) ? i85 : (sel==8'h56) ? i86 : (sel==8'h57) ? i87 + : (sel==8'h58) ? i88 : (sel==8'h59) ? i89 : (sel==8'h5a) ? i90 : (sel==8'h5b) ? i91 + : (sel==8'h5c) ? i92 : (sel==8'h5d) ? i93 : (sel==8'h5e) ? i94 : (sel==8'h5f) ? i95 + : (sel==8'h60) ? i96 : (sel==8'h61) ? i97 : (sel==8'h62) ? i98 : (sel==8'h63) ? i99 + : (sel==8'h64) ? i100 : (sel==8'h65) ? i101 : (sel==8'h66) ? i102 : (sel==8'h67) ? i103 + : (sel==8'h68) ? i104 : (sel==8'h69) ? i105 : (sel==8'h6a) ? i106 : (sel==8'h6b) ? i107 + : (sel==8'h6c) ? i108 : (sel==8'h6d) ? i109 : (sel==8'h6e) ? i110 : (sel==8'h6f) ? i111 + : (sel==8'h70) ? i112 : (sel==8'h71) ? i113 : (sel==8'h72) ? i114 : (sel==8'h73) ? i115 + : (sel==8'h74) ? i116 : (sel==8'h75) ? i117 : (sel==8'h76) ? i118 : (sel==8'h77) ? i119 + : (sel==8'h78) ? i120 : (sel==8'h79) ? i121 : (sel==8'h7a) ? i122 : (sel==8'h7b) ? i123 + : (sel==8'h7c) ? i124 : (sel==8'h7d) ? i125 : (sel==8'h7e) ? i126 : (sel==8'h7f) ? i127 + : (sel==8'h80) ? i128 : (sel==8'h81) ? i129 : (sel==8'h82) ? i130 : (sel==8'h83) ? i131 + : (sel==8'h84) ? i132 : (sel==8'h85) ? i133 : (sel==8'h86) ? i134 : (sel==8'h87) ? i135 + : (sel==8'h88) ? i136 : (sel==8'h89) ? i137 : (sel==8'h8a) ? i138 : (sel==8'h8b) ? i139 + : (sel==8'h8c) ? i140 : (sel==8'h8d) ? i141 : (sel==8'h8e) ? i142 : (sel==8'h8f) ? i143 + : (sel==8'h90) ? i144 : (sel==8'h91) ? i145 : (sel==8'h92) ? i146 : (sel==8'h93) ? i147 + : (sel==8'h94) ? i148 : (sel==8'h95) ? i149 : (sel==8'h96) ? i150 : (sel==8'h98) ? i151 + : (sel==8'h99) ? i152 : (sel==8'h9a) ? i153 : (sel==8'h9b) ? i154 : (sel==8'h9c) ? i155 + : (sel==8'h9d) ? i156 : (sel==8'h9e) ? i157 : (sel==8'h9f) ? i158 : (sel==8'ha0) ? i159 + : (sel==8'ha1) ? i160 : (sel==8'ha2) ? i161 : (sel==8'ha3) ? i162 : (sel==8'ha4) ? i163 + : (sel==8'ha5) ? i164 : (sel==8'ha6) ? i165 : (sel==8'ha7) ? i166 : (sel==8'ha8) ? i167 + : (sel==8'ha9) ? i168 : (sel==8'haa) ? i169 : (sel==8'hab) ? i170 : (sel==8'hac) ? i171 + : (sel==8'had) ? i172 : (sel==8'hae) ? i173 : (sel==8'haf) ? i174 : (sel==8'hb0) ? i175 + : (sel==8'hb1) ? i176 : (sel==8'hb2) ? i177 : (sel==8'hb3) ? i178 : (sel==8'hb4) ? i179 + : (sel==8'hb5) ? i180 : (sel==8'hb6) ? i181 : (sel==8'hb7) ? i182 : (sel==8'hb8) ? i183 + : (sel==8'hb9) ? i184 : (sel==8'hba) ? i185 : (sel==8'hbb) ? i186 : (sel==8'hbc) ? i187 + : (sel==8'hbd) ? i188 : (sel==8'hbe) ? i189 : (sel==8'hbf) ? i190 : (sel==8'hc0) ? i191 + : (sel==8'hc1) ? i192 : (sel==8'hc2) ? i193 : (sel==8'hc3) ? i194 : (sel==8'hc4) ? i195 + : (sel==8'hc5) ? i196 : (sel==8'hc6) ? i197 : (sel==8'hc7) ? i198 : (sel==8'hc8) ? i199 + : (sel==8'hc9) ? i200 : (sel==8'hca) ? i201 : (sel==8'hcb) ? i202 : (sel==8'hcc) ? i203 + : (sel==8'hcd) ? i204 : (sel==8'hce) ? i205 : (sel==8'hcf) ? i206 : (sel==8'hd0) ? i207 + : (sel==8'hd1) ? i208 : (sel==8'hd2) ? i209 : (sel==8'hd3) ? i210 : (sel==8'hd4) ? i211 + : (sel==8'hd5) ? i212 : (sel==8'hd6) ? i213 : (sel==8'hd7) ? i214 : (sel==8'hd8) ? i215 + : (sel==8'hd9) ? i216 : (sel==8'hda) ? i217 : (sel==8'hdb) ? i218 : (sel==8'hdc) ? i219 + : (sel==8'hdd) ? i220 : (sel==8'hde) ? i221 : (sel==8'hdf) ? i222 : (sel==8'he0) ? i223 + : (sel==8'he1) ? i224 : (sel==8'he2) ? i225 : (sel==8'he3) ? i226 : (sel==8'he4) ? i227 + : (sel==8'he5) ? i228 : (sel==8'he6) ? i229 : (sel==8'he7) ? i230 : (sel==8'he8) ? i231 + : (sel==8'he9) ? i232 : (sel==8'hea) ? i233 : (sel==8'heb) ? i234 : (sel==8'hec) ? i235 + : (sel==8'hed) ? i236 : (sel==8'hee) ? i237 : (sel==8'hef) ? i238 : (sel==8'hf0) ? i239 + : (sel==8'hf1) ? i240 : (sel==8'hf2) ? i241 : (sel==8'hf3) ? i242 : (sel==8'hf4) ? i243 + : (sel==8'hf5) ? i244 : (sel==8'hf6) ? i245 : (sel==8'hf7) ? i246 : (sel==8'hf8) ? i247 + : (sel==8'hf9) ? i248 : (sel==8'hfa) ? i249 : (sel==8'hfb) ? i250 : (sel==8'hfc) ? i251 + : (sel==8'hfd) ? i252 : (sel==8'hfe) ? i253 : (sel==8'hff) ? i254 : i255; endmodule diff --git a/test_regress/t/t_opt_localize_deep.v b/test_regress/t/t_opt_localize_deep.v index 1c8267fc7..9d99620c7 100644 --- a/test_regress/t/t_opt_localize_deep.v +++ b/test_regress/t/t_opt_localize_deep.v @@ -11,73 +11,75 @@ `endif module t (/*AUTOARG*/ - // Inputs - clk - ); - input clk; + // Inputs + clk + ); + input clk; - int cyc = 0; - int x = 0; + int cyc = 0; + int x = 0; - always @(posedge clk) begin - cyc <= cyc + 1; - x = 32'hcafe1234; + always @(posedge clk) begin + cyc <= cyc + 1; + x = 32'hcafe1234; - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) - x = cyc; + // verilog_format: off + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) if (`dontOptimize) + x = cyc; + // verilog_format: on - $write("[%0t] cyc=%0d x=%x\n", $time, cyc, x); - if (x !== cyc) $stop; - if (cyc == 99) begin - $write("*-* All Finished *-*\n"); - $finish; - end - end + $write("[%0t] cyc=%0d x=%x\n", $time, cyc, x); + if (x !== cyc) $stop; + if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end endmodule From 87050670b4d65e3070b18a83e32dd26489d31efb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 17 Jul 2025 18:17:49 -0400 Subject: [PATCH 036/252] Fix structure select causing 'Wide Op' error (#6191). --- Changes | 1 + src/V3AstNodes.cpp | 3 ++ test_regress/t/t_unpacked_wide_unknown.py | 18 ++++++++++ test_regress/t/t_unpacked_wide_unknown.v | 44 +++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100755 test_regress/t/t_unpacked_wide_unknown.py create mode 100644 test_regress/t/t_unpacked_wide_unknown.v diff --git a/Changes b/Changes index 44758dd21..76f32e894 100644 --- a/Changes +++ b/Changes @@ -32,6 +32,7 @@ Verilator 5.039 devel * Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.] * Fix `--coverage-expr` null pointer dereference (#6181). [Igor Zaworski, Antmicro Ltd.] * Fix conflicting function/class name linking error (#6182). [Igor Zaworski, Antmicro Ltd.] +* Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] Verilator 5.038 2025-07-08 diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 3307eedfc..c4c460645 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1110,6 +1110,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { } else if (overMembers && VN_IS(nodep, MemberSel)) { nodep = VN_AS(nodep, MemberSel)->fromp(); continue; + } else if (overMembers && VN_IS(nodep, StructSel)) { + nodep = VN_AS(nodep, StructSel)->fromp(); + continue; } // AstNodePreSel stashes the associated variable under an ATTROF // of VAttrType::VAR_BASE so it isn't constified diff --git a/test_regress/t/t_unpacked_wide_unknown.py b/test_regress/t/t_unpacked_wide_unknown.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_unpacked_wide_unknown.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_unpacked_wide_unknown.v b/test_regress/t/t_unpacked_wide_unknown.v new file mode 100644 index 000000000..208f99481 --- /dev/null +++ b/test_regress/t/t_unpacked_wide_unknown.v @@ -0,0 +1,44 @@ +// 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 + +typedef struct packed { + logic [149:0] hdr; + logic [1:0] vc; +} packet_t; + +module t; + + logic clk; + + typedef struct {packet_t [1:0] pkt_i;} dut_if_t; + + dut_if_t dut[2]; + + initial begin + clk = 0; + forever #(0.5) clk = ~clk; + end + + task automatic send_req_packets(int module_id, int channel); + packet_t packet = '0; + dut[module_id].pkt_i[channel] = packet; + @(posedge clk); // If you comment out this line. It will build. + endtask + + initial begin + for (int m = 0; m < 2; m++) begin + for (int i = 0; i < 2; i++) begin + automatic int mod = m; + automatic int ch = i; + send_req_packets(mod, ch); + end + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From c1506deef9048ceca890971cefc64f98ed8e72c8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 17 Jul 2025 20:20:43 -0400 Subject: [PATCH 037/252] Add enum base type checking per IEEE. --- Changes | 1 + src/V3Width.cpp | 25 ++++++++++++++--- test_regress/t/t_enum_base.py | 18 +++++++++++++ test_regress/t/t_enum_base.v | 18 +++++++++++++ test_regress/t/t_enum_base_bad.out | 30 ++++++++++++++++++--- test_regress/t/t_enum_base_bad.v | 43 +++++++++++++++++++++++------- 6 files changed, 119 insertions(+), 16 deletions(-) create mode 100755 test_regress/t/t_enum_base.py create mode 100644 test_regress/t/t_enum_base.v diff --git a/Changes b/Changes index 76f32e894..12d4e47f5 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. +* Add enum base type checking per IEEE. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 7153c441a..9790d17e8 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2483,10 +2483,11 @@ class WidthVisitor final : public VNVisitor { UINFO(5, " ENUMDTYPE " << nodep); nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); nodep->dtypep(nodep); - AstBasicDType* basicp = nodep->dtypep()->skipRefp()->basicp(); - if (!basicp || !basicp->keyword().isIntNumeric()) { + AstNodeDType* basicp = nodep->dtypep()->skipRefp()->basicp(); + if (!dtypeIsIntAtomOrVecRecurse(nodep->subDTypep())) { nodep->v3error( - "Enum type must be an integer atom or vector type (IEEE 1800-2023 6.19)"); + "Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not " + << nodep->subDTypep()->prettyDTypeNameQ()); basicp = nodep->findSigned32DType()->basicp(); nodep->refDTypep(basicp); } @@ -8309,6 +8310,24 @@ class WidthVisitor final : public VNVisitor { if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp()); return false; } + bool dtypeIsIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) { + nodep = nodep->skipRefToEnump(); + if (AstBasicDType* const dtp = VN_CAST(nodep, BasicDType)) { + if (ranged && (!dtp->isBitLogic() || dtp->isRanged())) { + UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); + return false; // Packed when already packed + } + if (dtp->keyword().isIntNumeric()) return true; + } else if (AstPackArrayDType* const dtp = VN_CAST(nodep, PackArrayDType)) { + if (ranged) { + UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); + return false; // Packed when already packed + } + return dtypeIsIntAtomOrVecRecurse(nodep->subDTypep(), true); + } + UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); + return false; + } //---------------------------------------------------------------------- // METHODS - special type detection diff --git a/test_regress/t/t_enum_base.py b/test_regress/t/t_enum_base.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_enum_base.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_enum_base.v b/test_regress/t/t_enum_base.v new file mode 100644 index 000000000..74a811edf --- /dev/null +++ b/test_regress/t/t_enum_base.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + + typedef int int_t; + typedef enum int_t {INTT_VAL = 1} intt_e; + intt_e intte; + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_enum_base_bad.out b/test_regress/t/t_enum_base_bad.out index d46208e46..f57b5e80c 100644 --- a/test_regress/t/t_enum_base_bad.out +++ b/test_regress/t/t_enum_base_bad.out @@ -1,6 +1,30 @@ -%Error: t/t_enum_base_bad.v:13:12: Enum type must be an integer atom or vector type (IEEE 1800-2023 6.19) +%Error: t/t_enum_base_bad.v:11:11: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'struct{}t.s_t' : ... note: In instance 't' - 13 | typedef enum s_t { - | ^~~~ + 11 | typedef enum s_t { + | ^~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_enum_base_bad.v:16:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'int[1:0]' + : ... note: In instance 't' + 16 | enum int_t [1:0] { + | ^~~~ +%Error: t/t_enum_base_bad.v:21:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'bit[1:0][1:0]' + : ... note: In instance 't' + 21 | enum d2_t { + | ^~~~ +%Error: t/t_enum_base_bad.v:25:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'logic[1:0][1:0]' + : ... note: In instance 't' + 25 | enum logic [1:0][1:0] { + | ^~~~ +%Error: t/t_enum_base_bad.v:30:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'struct{}t.str_t' + : ... note: In instance 't' + 30 | enum str_t { + | ^~~~ +%Error: t/t_enum_base_bad.v:35:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'enum{}t.enum_t' + : ... note: In instance 't' + 35 | enum enum_t { + | ^~~~ +%Error: t/t_enum_base_bad.v:40:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'logic$[1:0]' + : ... note: In instance 't' + 40 | enum array2_t { + | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_enum_base_bad.v b/test_regress/t/t_enum_base_bad.v index 2647b47e8..309bae927 100644 --- a/test_regress/t/t_enum_base_bad.v +++ b/test_regress/t/t_enum_base_bad.v @@ -4,20 +4,43 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -module t(/*AUTOARG*/); +module t; - typedef struct { - int a; - } s_t; + typedef struct {int a;} s_t; - typedef enum s_t { - EN_ZERO } bad_t; + typedef enum s_t { // BAD + EN_ZERO + } bad_t; - typedef int int_t; + typedef int int_t; + enum int_t [1:0] { // BAD enum type + INTRANGE_VAL + } intrange_e; - typedef enum int_t { EN_ONE = 1 } ok1_t; + typedef bit [1:0][1:0] d2_t; + enum d2_t { // BAD enum type + TD2_VAL + } td2_e; - s_t s; - int_t i; + enum logic [1:0][1:0] { // BAD enum type + D2_VAL + } d2_e; + + typedef struct packed {int x;} str_t; + enum str_t { // BAD enum type + STR_VAL + } str_e; + + typedef enum {ENUM_VAL} enum_t; + enum enum_t { // BAD enum type + ENUMT_VAL + } enumt_val; + + typedef logic array2_t[1:0]; + enum array2_t { // BAD enum type + ARRAY2_VAL + } array2_e; + + initial $stop; endmodule From e527ff49a317c8528907283f4aab38895bd6116d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 17 Jul 2025 20:41:46 -0400 Subject: [PATCH 038/252] Report `--stats` final results after build phase --- src/Verilator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 2af5b973a..f712412b8 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -649,7 +649,6 @@ static void process() { // Final statistics if (v3Global.opt.stats()) V3Stats::statsStage("emit"); - reportStatsIfEnabled(); } static void verilate(const string& argString) { @@ -769,6 +768,7 @@ static void verilate(const string& argString) { V3Os::filesystemFlushBuildDir(v3Global.opt.makeDir()); if (v3Global.opt.hierTop()) V3Os::filesystemFlushBuildDir(v3Global.opt.hierTopDataDir()); + if (v3Global.opt.stats()) V3Stats::statsStage("wrote"); // Final writing shouldn't throw warnings, but... V3Error::abortIfWarnings(); @@ -867,6 +867,7 @@ int main(int argc, char** argv) { execBuildJob(); } + reportStatsIfEnabled(); V3DiagSarif::output(true); // Explicitly release resources From 7a6775ca84b73a7aa20bfcd20e98ad74d087ad63 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 17 Jul 2025 22:15:28 -0400 Subject: [PATCH 039/252] Internals: Rename memPeakUsageBytes --- include/verilated.cpp | 2 +- include/verilatedos.h | 2 +- include/verilatedos_c.h | 5 +++-- src/V3StatsReport.cpp | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index f0a7f0630..74ceae09d 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3061,7 +3061,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE { = vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s"); VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(), simtime.c_str(), walltime, simtimePerf.c_str()); - const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0; + const double modelMB = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime, threadsInModels(), modelMB); } diff --git a/include/verilatedos.h b/include/verilatedos.h index 6e47e85db..085bb2544 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -637,7 +637,7 @@ extern std::string getenvStr(const std::string& envvar, extern uint16_t getcpu() VL_MT_SAFE; /// Return memory usage in bytes, or 0 if unknown -extern uint64_t memUsageBytes() VL_MT_SAFE; +extern uint64_t memPeakUsageBytes() VL_MT_SAFE; // Internal: Record CPU time, starting point on construction, and current delta from that class DeltaCpuTime final { diff --git a/include/verilatedos_c.h b/include/verilatedos_c.h index 24edda4c0..6ecdf7dba 100644 --- a/include/verilatedos_c.h +++ b/include/verilatedos_c.h @@ -102,9 +102,9 @@ uint16_t getcpu() VL_MT_SAFE { } //========================================================================= -// VlOs::memUsageBytes implementation +// VlOs::memPeakUsageBytes implementation -uint64_t memUsageBytes() VL_MT_SAFE { +uint64_t memPeakUsageBytes() VL_MT_SAFE { #if defined(_WIN32) || defined(__MINGW32__) const HANDLE process = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; @@ -124,6 +124,7 @@ uint64_t memUsageBytes() VL_MT_SAFE { &size, &resident, &share, &text, &lib, &data, &dt); fclose(fp); if (VL_UNCOVERABLE(7 != items)) return 0; + // Return the vm size, not the current active set size (/proc/self/status VmRSS + VmSwap) return (text + data) * getpagesize(); #endif } diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 0998fa489..2c5dadb80 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -215,7 +215,7 @@ void V3Stats::statsStage(const string& name) { V3Stats::addStatPerf("Stage, Elapsed time (sec), " + digitName, wallTimeDelta); V3Stats::addStatPerf("Stage, Elapsed time (sec), TOTAL", wallTimeDelta); - const double memory = VlOs::memUsageBytes() / 1024.0 / 1024.0; + const double memory = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; V3Stats::addStatPerf("Stage, Memory (MB), " + digitName, memory); } @@ -266,7 +266,7 @@ void V3Stats::summaryReport() { << ", cvt=" << walltimeCvt << ", bld=" << walltimeBuild << "); cpu " << cputime << " s on " << std::max(v3Global.opt.verilateJobs(), v3Global.opt.buildJobs()) << " threads"; - const double memory = VlOs::memUsageBytes() / 1024.0 / 1024.0; + const double memory = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; if (VL_UNCOVERABLE(memory != 0.0)) std::cout << "; alloced " << memory << " MB"; std::cout << "\n"; } From 28808f38bbcf659f1936958eed9a0860896801ff Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 18 Jul 2025 08:37:00 +0200 Subject: [PATCH 040/252] Dump function to which AstAddrOfCFunc points (#6188) --- src/V3AstNodeExpr.h | 1 + src/V3AstNodes.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 8be95ea1f..b4c2f9a81 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -561,6 +561,7 @@ public: public: ASTGEN_MEMBERS_AstAddrOfCFunc; + void dump(std::ostream& str) const override; string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c4c460645..92915d5eb 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -189,6 +189,12 @@ void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Numb } } +void AstAddrOfCFunc::dump(std::ostream& str) const { + this->AstNodeExpr::dump(str); + str << " -> "; + funcp()->dump(str); +} + void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int wantwidthmin, AstRange* rangep) { // wantwidth=0 means figure it out, but if a widthmin is >=0 From a21ecb2ab949b584c321064e78a62bc6a02d0928 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 18 Jul 2025 13:04:47 +0200 Subject: [PATCH 041/252] Add support for randomize..with on objects of aliased types (#6195) --- src/V3Randomize.cpp | 16 ++++++++-------- test_regress/t/t_randomize_method_with.v | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index de73e9523..7c905f697 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -2198,14 +2198,14 @@ class RandomizeVisitor final : public VNVisitor { AstClass* classp = nullptr; if (AstMethodCall* const callp = VN_CAST(nodep, MethodCall)) { - UASSERT_OBJ(callp->fromp()->dtypep(), callp->fromp(), "Object dtype is not linked"); - AstClassRefDType* const classrefdtypep - = VN_CAST(callp->fromp()->dtypep(), ClassRefDType); - if (!classrefdtypep) { - nodep->v3warn(E_UNSUPPORTED, - "Inline constraints are not supported for this node type"); - return; - } + const AstNodeDType* const fromDTypep = callp->fromp()->dtypep(); + UASSERT_OBJ(fromDTypep, callp->fromp(), "Object dtype is not linked"); + const AstClassRefDType* const classrefdtypep + = VN_CAST(fromDTypep->skipRefp(), ClassRefDType); + UASSERT_OBJ(classrefdtypep, callp->fromp(), + "Randomize called on expression of non-class type " + << fromDTypep->skipRefp()->prettyDTypeNameQ() + << " (it should be detected earlier)"); classp = classrefdtypep->classp(); UASSERT_OBJ(classp, classrefdtypep, "Class type is unlinked to its ref type"); } else { diff --git a/test_regress/t/t_randomize_method_with.v b/test_regress/t/t_randomize_method_with.v index 29c8bb2b9..f1dad680a 100644 --- a/test_regress/t/t_randomize_method_with.v +++ b/test_regress/t/t_randomize_method_with.v @@ -109,6 +109,8 @@ module mwith(); int c = 30; Foo foo = new(c); Baz baz = new; + typedef Baz baz_t; + baz_t baz1 = new; Baz2 baz2 = new; Bar bar = new; Cls2 cls2 = new; @@ -149,6 +151,7 @@ module mwith(); if (foo.randomize() with { a > return_2(); } != 1) $stop; // Check randomization of class with no constraints if (baz.randomize() with { v inside {[2:10]}; } != 1) $stop; + if (baz1.randomize() with { v inside {[2:10]}; } != 1) $stop; // Check randomization with captured non-static variable from different AstNodeModule if (!bar.test_capture_of_callers_derived_var(foo)) $stop; // Check randomization with non-captured non-static variable from different AstNodeModule From 9b99d9697fd631d46b7bdaf0713a4b9d82348138 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Fri, 18 Jul 2025 15:07:31 +0200 Subject: [PATCH 042/252] Fix virtual interface member propagation (#6175) (#6184) --- src/V3Sched.cpp | 34 ++++- src/V3Sched.h | 2 +- .../t/t_virtual_interface_member_trigger.v | 58 ++++++-- ...interface_member_trigger_realistic_case.py | 18 +++ ..._interface_member_trigger_realistic_case.v | 138 ++++++++++++++++++ 5 files changed, 230 insertions(+), 20 deletions(-) create mode 100755 test_regress/t/t_virtual_interface_member_trigger_realistic_case.py create mode 100755 test_regress/t/t_virtual_interface_member_trigger_realistic_case.v diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 633d8fbf3..8e16c3479 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -104,6 +104,17 @@ void invertAndMergeSenTreeMap( for (const auto& pair : senTreeMap) result.emplace(pair.second, pair.first); } +AstSenTree* findTriggeredIface(const AstVarScope* vscp, + const VirtIfaceTriggers::IfaceSensMap& vifTrigged, + const VirtIfaceTriggers::IfaceMemberSensMap& vifMemberTriggered) { + const auto ifaceIt = vifTrigged.find(vscp->varp()->sensIfacep()); + if (ifaceIt != vifTrigged.end()) return ifaceIt->second; + for (const auto& memberIt : vifMemberTriggered) { + if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) { return memberIt.second; } + } + return nullptr; +} + //============================================================================ // Code generation utility functions @@ -955,8 +966,10 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp, AstSenTree* const dpiExportTriggered = dpiExportTriggerVscp ? createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex) : nullptr; - const auto& vifTriggered + const auto& vifTriggeredIco = virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp); + const auto& vifMemberTriggeredIco + = virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp); // Create and Order the body function AstCFunc* const icoFuncp @@ -968,8 +981,9 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp, } if (varp->isWrittenByDpi()) out.push_back(dpiExportTriggered); if (vscp->varp()->sensIfacep()) { - const auto it = vifTriggered.find(vscp->varp()->sensIfacep()); - if (it != vifTriggered.end()) out.push_back(it->second); + AstSenTree* ifaceTriggered = findTriggeredIface( + vscp, vifTriggeredIco, vifMemberTriggeredIco); + out.push_back(ifaceTriggered); } }); splitCheck(icoFuncp); @@ -1371,6 +1385,8 @@ void schedule(AstNetlist* netlistp) { const auto& vifTriggeredAct = virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp); + const auto& vifMemberTriggeredAct + = virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp); AstCFunc* const actFuncp = V3Order::order( netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct, @@ -1379,8 +1395,9 @@ void schedule(AstNetlist* netlistp) { if (it != actTimingDomains.end()) out = it->second; if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredAct); if (vscp->varp()->sensIfacep()) { - const auto sit = vifTriggeredAct.find(vscp->varp()->sensIfacep()); - if (sit != vifTriggeredAct.end()) out.push_back(sit->second); + AstSenTree* ifaceTriggered + = findTriggeredIface(vscp, vifTriggeredAct, vifMemberTriggeredAct); + out.push_back(ifaceTriggered); } }); splitCheck(actFuncp); @@ -1408,6 +1425,8 @@ void schedule(AstNetlist* netlistp) { : nullptr; const auto& vifTriggered = virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trigVscp); + const auto& vifMemberTriggered + = virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, trigVscp); const auto& timingDomains = timingKit.remapDomains(trigMap); AstCFunc* const funcp = V3Order::order( @@ -1417,8 +1436,9 @@ void schedule(AstNetlist* netlistp) { if (it != timingDomains.end()) out = it->second; if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggered); if (vscp->varp()->sensIfacep()) { - const auto sit = vifTriggered.find(vscp->varp()->sensIfacep()); - if (sit != vifTriggered.end()) out.push_back(sit->second); + AstSenTree* ifaceTriggered + = findTriggeredIface(vscp, vifTriggered, vifMemberTriggered); + out.push_back(ifaceTriggered); } }); diff --git a/src/V3Sched.h b/src/V3Sched.h index 5d197e5ca..03ff99a18 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -168,6 +168,7 @@ class VirtIfaceTriggers final { } }; +public: using IfaceMemberTrigger = std::pair; using IfaceMemberTriggerVec = std::vector; using IfaceMemberSensMap = std::map; @@ -179,7 +180,6 @@ class VirtIfaceTriggers final { IfaceMemberTriggerVec m_memberTriggers; IfaceTriggerVec m_ifaceTriggers; -public: void addMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp, AstVarScope* triggerVscp) { m_memberTriggers.emplace_back(IfaceMember(ifacep, memberVarp), triggerVscp); diff --git a/test_regress/t/t_virtual_interface_member_trigger.v b/test_regress/t/t_virtual_interface_member_trigger.v index a6bf92d7f..593e78baa 100755 --- a/test_regress/t/t_virtual_interface_member_trigger.v +++ b/test_regress/t/t_virtual_interface_member_trigger.v @@ -10,6 +10,7 @@ interface INTF; logic x; logic y; logic z; + logic [7:0] data; endinterface class Dummy; @@ -17,28 +18,61 @@ class Dummy; function new(virtual INTF vif); this.vif = vif; endfunction + task write_data(logic [7:0] d); + vif.data = d; + endtask endclass module t_virtual_interface_member_trigger(); - logic s1, src_val; - logic s2; - - INTF vintf(); - - assign vintf.x = s1; - assign vintf.y = src_val; - assign vintf.z = !vintf.y; - assign s2 = vintf.z; + // === Part 1: logic trigger false loop test === + logic s1, s2, src_val; + INTF intf_loop(); + virtual INTF vif_loop; + assign intf_loop.x = s1; + assign intf_loop.y = src_val; + assign intf_loop.z = !intf_loop.y; + assign s2 = intf_loop.z; assign s1 = s2; - Dummy d; + // === Part 2: data transfer chain test === + logic [7:0] data; + INTF intf_read(); + INTF intf_write(); + assign intf_read.data = data; + assign data = intf_write.data; + virtual INTF vif_read, vif_write; + + Dummy cl_1, cl_2; initial begin - d = new(vintf); + + // Test 1: no false loop with member-level trigger + #1ns; + vif_loop = intf_loop; + cl_1 = new(vif_loop); #1ns; src_val = 0; #1ns; - if (!(d.vif.x == 1 && d.vif.y == 0 && d.vif.z == 1 && s1 == 1 && s2 == 1)) $stop; + if (!(cl_1.vif.x == 1 && cl_1.vif.y == 0 && cl_1.vif.z == 1 && s1 == 1 && s2 == 1)) $stop; + + // Test 2: write from module + #1ns; + vif_read = intf_read; + vif_write = intf_write; + #1ns; + vif_write.data = 8'hA5; + #1ns; + if (vif_read.data !== 8'hA5) $stop; + + // Test 3: write from class + #1ns; + cl_2 = new(vif_write); + #1ns; + cl_2.write_data(8'hB7); + #1ns; + if (vif_read.data !== 8'hB7) $stop; + + #5ns; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_virtual_interface_member_trigger_realistic_case.py b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.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_virtual_interface_member_trigger_realistic_case.v b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v new file mode 100755 index 000000000..2674d7a0a --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v @@ -0,0 +1,138 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps + +interface INTF(); + logic clk; + logic [7:0] data; + logic valid; + logic ready; +endinterface + +time TA = 5ns; + +class intf_driver; + virtual INTF intf; + function new(virtual INTF intf); + this.intf = intf; + endfunction + + task cycle_start(); + #TA; + endtask + + task cycle_end(); + @(posedge intf.clk); + endtask + + task init_master(); + intf.data = '0; + intf.valid = 0; + endtask + + task init_slave(); + intf.ready = 0; + endtask + + task recv_data(output logic [7:0] data); + intf.ready <= #TA 1; + cycle_start(); + while (!(intf.valid && intf.ready)) begin cycle_end(); cycle_start(); end + cycle_end(); + data = intf.data; + intf.ready <= #TA 0; + endtask + + task send_data(input logic [7:0] data); + intf.data <= #TA data; + intf.valid <= #TA 1; + cycle_start(); + while (!(intf.valid && intf.ready)) begin cycle_end(); cycle_start(); end + cycle_end(); + intf.valid <= #TA 0; + endtask +endclass + +module t_virtual_interface_member_trigger_realistic_case(); + logic clk; + logic [7:0] data; + logic valid; + logic ready; + logic [7:0] recv_data; + + INTF read_intf(); + assign read_intf.clk = clk; + assign read_intf.data = data; + assign read_intf.valid = valid; + assign ready = read_intf.ready; + + INTF write_intf(); + assign write_intf.clk = clk; + assign data = write_intf.data; + assign valid = write_intf.valid; + assign write_intf.ready = ready; + + intf_driver driver_master; + intf_driver driver_slave; + + virtual INTF vif_read = read_intf; + virtual INTF vif_write = write_intf; + + initial begin + repeat(1000) begin + clk = '1; + #10ns; + clk = '0; + #10ns; + end + end + + initial begin + driver_master = new(vif_write); + driver_slave = new(vif_read); + + driver_master.init_master(); + driver_slave.init_slave(); + fork + begin + #35ns; + driver_master.send_data(8'h42); + // $display("[%0d]: Write data: %02x", $time, write_intf.data); + #10ns; + driver_master.send_data(8'h43); + // $display("[%0d]: Write data: %02x", $time, write_intf.data); + #10ns; + driver_master.send_data(8'h44); + // $display("[%0d]: Write data: %02x", $time, write_intf.data); + end + begin + #10ns; + driver_slave.recv_data(recv_data); + // $display("[%0d]: Got data: %02x", $time, recv_data); + if (recv_data !== 8'h42) $stop; + #5ns; + driver_slave.recv_data(recv_data); + // $display("[%0d]: Got data: %02x", $time, recv_data); + if (recv_data !== 8'h43) $stop; + #15ns; + driver_slave.recv_data(recv_data); + // $display("[%0d]: Got data: %02x", $time, recv_data); + if (recv_data !== 8'h44) $stop; + end + join + + $write("*-* All Finished *-*\n"); + $finish; + end + + // Dump waveforms + // initial begin + // $dumpfile("t_virtual_interface_member_trigger.vcd"); + // $dumpvars(0, t_virtual_interface_member_trigger); + // end + +endmodule From 7d43a935bd4404d04e56a87350d152197ba942bb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 18 Jul 2025 19:32:34 -0400 Subject: [PATCH 043/252] Add SPECIFYIGN warning for specify constructs that were previously silently ignored. --- Changes | 1 + docs/guide/warnings.rst | 16 +++++++++-- src/V3Error.h | 13 ++++----- src/V3Options.cpp | 2 ++ src/verilog.l | 1 + src/verilog.y | 30 ++++++++++++++++----- test_regress/t/t_gate_basic.py | 2 +- test_regress/t/t_gate_basic_specify_bad.out | 22 +++++++++++++++ test_regress/t/t_gate_basic_specify_bad.py | 17 ++++++++++++ test_regress/t/t_gate_basic_timing.py | 3 ++- test_regress/t/t_gate_delay_unsup.py | 4 ++- test_regress/t/t_sdf_annotate_unsup.out | 15 +++++++++++ test_regress/t/t_sdf_annotate_unsup.py | 16 +++++++++++ test_regress/t/t_sdf_annotate_unsup.v | 20 ++++++++++++++ 14 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 test_regress/t/t_gate_basic_specify_bad.out create mode 100755 test_regress/t/t_gate_basic_specify_bad.py create mode 100644 test_regress/t/t_sdf_annotate_unsup.out create mode 100755 test_regress/t/t_sdf_annotate_unsup.py create mode 100644 test_regress/t/t_sdf_annotate_unsup.v diff --git a/Changes b/Changes index 12d4e47f5..2d506b97f 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. +* Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base type checking per IEEE. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index a7143fe3b..9e6d534be 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -458,7 +458,7 @@ List Of Warnings Warns that Verilator does not support certain forms of :code:`constraint`, :code:`constraint_mode`, or :code:`rand_mode`, and - the construct was are ignored. + the construct was ignored. Ignoring this warning may make Verilator randomize() simulations differ from other simulators. @@ -486,7 +486,7 @@ List Of Warnings Warns that Verilator does not support certain forms of :code:`covergroup`, :code:`coverpoint`, and coverage options, and the - construct was are ignored. + construct was ignored. Disabling the :option:`UNSUPPORTED` error also disables this warning. @@ -1779,6 +1779,18 @@ List Of Warnings simulators. +.. option:: SPECIFYIGN + + Warns that Verilator does not support certain constructs in + :code:`specify` blocks, nor :code:`$sdf_annotate`, and the construct was + ignored. + + Disabling the :option:`UNSUPPORTED` error also disables this warning. + + Ignoring this warning may make Verilator ignore lint checking on the + construct, and get different results from other simulators. + + .. option:: SPLITVAR Warns that a variable with a :option:`/*verilator&32;split_var*/` diff --git a/src/V3Error.h b/src/V3Error.h index b8ca71866..6706e4891 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -146,6 +146,7 @@ public: SELRANGE, // Selection index out of range SHORTREAL, // Shortreal not supported SIDEEFFECT, // Sideeffect ignored + SPECIFYIGN, // Specify construct ignored SPLITVAR, // Cannot split the variable STATICVAR, // Static variable declared in a loop with a declaration assignment STMTDLY, // Delayed statement @@ -212,12 +213,12 @@ public: "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", - "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR", "STATICVAR", - "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", - "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", - "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", - "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", - "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; + "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", "SPLITVAR", + "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", + "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", + "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", + "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", + "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; return names[m_e]; } // Warnings that default to off diff --git a/src/V3Options.cpp b/src/V3Options.cpp index c4643c115..1b64307ce 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1803,8 +1803,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-Wwarn-UNSUPPORTED", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, false); FileLine::globalWarnOff(V3ErrorCode::COVERIGN, false); + FileLine::globalWarnOff(V3ErrorCode::SPECIFYIGN, false); V3Error::pretendError(V3ErrorCode::E_UNSUPPORTED, false); V3Error::pretendError(V3ErrorCode::COVERIGN, false); + V3Error::pretendError(V3ErrorCode::SPECIFYIGN, false); }); DECL_OPTION("-Wwarn-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, false); diff --git a/src/verilog.l b/src/verilog.l index 754368e1c..dc1e3a323 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -270,6 +270,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$rewind" { FL; return yD_REWIND; } "$rtoi" { FL; return yD_RTOI; } "$sampled" { FL; return yD_SAMPLED; } + "$sdf_annotate" { FL; return yD_SDF_ANNOTATE; } "$setup" { FL; return yaTIMINGSPEC; } "$setuphold" { FL; return yD_SETUPHOLD; } "$sformat" { FL; return yD_SFORMAT; } diff --git a/src/verilog.y b/src/verilog.y index b25b207f7..0b57eeab1 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -98,6 +98,7 @@ public: bool m_tracingParse = true; // Tracing disable for parser bool m_inImplements = false; // Is inside class implements list bool m_insideProperty = false; // Is inside property declaration + bool m_specifyignWarned = false; // Issued a SPECIFYIGN warning bool m_typedPropertyPort = false; // Typed property port occurred on port lists bool m_modportImpExpActive = false; // Standalone ID is a tf_identifier instead of port_identifier @@ -926,6 +927,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_ROSE_GCLK "$rose_gclk" %token yD_RTOI "$rtoi" %token yD_SAMPLED "$sampled" +%token yD_SDF_ANNOTATE "$sdf_annotate" %token yD_SETUPHOLD "$setuphold" %token yD_SFORMAT "$sformat" %token yD_SFORMATF "$sformatf" @@ -4284,6 +4286,7 @@ system_t_call: // IEEE: system_tf_call (as task) | yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl{$1, VDumpCtlType::ON}; DEL($3); } // | yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt{$1, $3}); } + | yD_SDF_ANNOTATE '(' exprEListE ')' { $$ = nullptr; $1->v3warn(SPECIFYIGN, "Ignoring unsupported: $sdf_annotate"); } | yD_STACKTRACE parenE { $$ = new AstStackTraceT{$1}; } | yD_SYSTEM '(' expr ')' { $$ = new AstSystemT{$1, $3}; } // @@ -5353,6 +5356,11 @@ exprList: | exprList ',' expr { $$ = $1->addNext($3); } ; +exprEListE: // expression list with empty commas allowed + exprE { $$ = $1; } + | exprEListE ',' exprE { $$ = addNextNull($1, $3); } + ; + exprDispList: // exprList for within $display expr { $$ = $1; } | exprDispList ',' expr { $$ = $1->addNext($3); } @@ -5825,8 +5833,12 @@ tablelVal: // Specify specify_block: // ==IEEE: specify_block - ySPECIFY specify_itemList yENDSPECIFY { $$ = $2; } - | ySPECIFY yENDSPECIFY { $$ = nullptr; } + specifyFront specify_itemList yENDSPECIFY { $$ = $2; } + | specifyFront yENDSPECIFY { $$ = nullptr; } + ; + +specifyFront: // IEEE: specify_block front + ySPECIFY { GRAMMARP->m_specifyignWarned = false; } ; specify_itemList: // IEEE: { specify_item } @@ -5837,7 +5849,13 @@ specify_itemList: // IEEE: { specify_item } specify_item: // ==IEEE: specify_item specparam_declaration { $$ = $1; } | system_timing_check { $$ = $1; } - | junkToSemiList ';' { $$ = nullptr; } + | junkToSemiList ';' + { $$ = nullptr; + if (!GRAMMARP->m_specifyignWarned) { + GRAMMARP->m_specifyignWarned = true; + $1->v3warn(SPECIFYIGN, "Ignoring unsupported: specify block construct"); + } + } ; specparam_declaration: // ==IEEE: specparam_declaration @@ -5926,9 +5944,9 @@ idAnyE: | idAny { $$ = $1; } ; -junkToSemiList: - junkToSemi { } /* ignored */ - | junkToSemiList junkToSemi { } /* ignored */ +junkToSemiList: + junkToSemi { $$ = CRELINE(); } + | junkToSemiList junkToSemi { $$ = CRELINE(); } ; junkToSemi: diff --git a/test_regress/t/t_gate_basic.py b/test_regress/t/t_gate_basic.py index e5667d0d0..c98cbe4f8 100755 --- a/test_regress/t/t_gate_basic.py +++ b/test_regress/t/t_gate_basic.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--no-timing"]) +test.compile(verilator_flags2=["--no-timing", "-Wno-SPECIFYIGN"]) test.execute() diff --git a/test_regress/t/t_gate_basic_specify_bad.out b/test_regress/t/t_gate_basic_specify_bad.out new file mode 100644 index 000000000..8b041a215 --- /dev/null +++ b/test_regress/t/t_gate_basic_specify_bad.out @@ -0,0 +1,22 @@ +%Warning-SPECIFYIGN: t/t_gate_basic.v:50:27: Ignoring unsupported: specify block construct + 50 | (nt0 *> nt0) = (0, 0); + | ^ + ... For warning description see https://verilator.org/warn/SPECIFYIGN?v=latest + ... Use "/* verilator lint_off SPECIFYIGN */" and lint_on around source to disable this message. +%Warning-SPECIFYIGN: t/t_gate_basic.v:60:32: Ignoring unsupported: specify block construct + 60 | (A1 *> Q) = (a$A1$Y, a$A1$Y); + | ^ +%Error-NEEDTIMINGOPT: t/t_gate_basic.v:24:10: Use --timing or --no-timing to specify how timing controls should be handled + : ... note: In instance 't' + 24 | not #(0.108) NT0 (nt0, a[0]); + | ^ + ... For error description see https://verilator.org/warn/NEEDTIMINGOPT?v=latest +%Error-NEEDTIMINGOPT: t/t_gate_basic.v:25:10: Use --timing or --no-timing to specify how timing controls should be handled + : ... note: In instance 't' + 25 | and #1 AN0 (an0, a[0], b[0]); + | ^ +%Error-NEEDTIMINGOPT: t/t_gate_basic.v:26:10: Use --timing or --no-timing to specify how timing controls should be handled + : ... note: In instance 't' + 26 | nand #(2,3) ND0 (nd0, a[0], b[0], b[1]); + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_gate_basic_specify_bad.py b/test_regress/t/t_gate_basic_specify_bad.py new file mode 100755 index 000000000..07db30dae --- /dev/null +++ b/test_regress/t/t_gate_basic_specify_bad.py @@ -0,0 +1,17 @@ +#!/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.top_filename = 't/t_gate_basic.v' + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_gate_basic_timing.py b/test_regress/t/t_gate_basic_timing.py index cfcba0b42..12c73db41 100755 --- a/test_regress/t/t_gate_basic_timing.py +++ b/test_regress/t/t_gate_basic_timing.py @@ -13,7 +13,8 @@ test.scenarios('simulator') test.top_filename = "t/t_gate_basic.v" test.main_time_multiplier = 10e-7 / 10e-9 -test.compile(timing_loop=True, verilator_flags2=["--timing --timescale 10ns/1ns -Wno-RISEFALLDLY"]) +test.compile(timing_loop=True, + verilator_flags2=["--timing --timescale 10ns/1ns -Wno-RISEFALLDLY -Wno-SPECIFYIGN"]) test.execute() diff --git a/test_regress/t/t_gate_delay_unsup.py b/test_regress/t/t_gate_delay_unsup.py index 3e0229f9a..9591d0ead 100755 --- a/test_regress/t/t_gate_delay_unsup.py +++ b/test_regress/t/t_gate_delay_unsup.py @@ -12,7 +12,9 @@ import vltest_bootstrap test.scenarios('linter') test.top_filename = "t/t_gate_basic.v" -test.lint(verilator_flags2=["--lint-only -Wall -Wno-DECLFILENAME -Wno-UNUSED --timing"], +test.lint(verilator_flags2=[ + "--lint-only --timing -Wall", "-Wno-DECLFILENAME -Wno-SPECIFYIGN -Wno-UNUSED" +], fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_sdf_annotate_unsup.out b/test_regress/t/t_sdf_annotate_unsup.out new file mode 100644 index 000000000..016202286 --- /dev/null +++ b/test_regress/t/t_sdf_annotate_unsup.out @@ -0,0 +1,15 @@ +%Warning-SPECIFYIGN: t/t_sdf_annotate_unsup.v:10:5: Ignoring unsupported: $sdf_annotate + 10 | $sdf_annotate("file.sdf"); + | ^~~~~~~~~~~~~ + ... For warning description see https://verilator.org/warn/SPECIFYIGN?v=latest + ... Use "/* verilator lint_off SPECIFYIGN */" and lint_on around source to disable this message. +%Warning-SPECIFYIGN: t/t_sdf_annotate_unsup.v:11:5: Ignoring unsupported: $sdf_annotate + 11 | $sdf_annotate("file.sdf",); + | ^~~~~~~~~~~~~ +%Warning-SPECIFYIGN: t/t_sdf_annotate_unsup.v:12:5: Ignoring unsupported: $sdf_annotate + 12 | $sdf_annotate("file.sdf", t); + | ^~~~~~~~~~~~~ +%Warning-SPECIFYIGN: t/t_sdf_annotate_unsup.v:14:5: Ignoring unsupported: $sdf_annotate + 14 | $sdf_annotate("file.sdf", t, "config_file", "log_file", "mtm_spec", "scale_factors", + | ^~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_sdf_annotate_unsup.py b/test_regress/t/t_sdf_annotate_unsup.py new file mode 100755 index 000000000..272fc1280 --- /dev/null +++ b/test_regress/t/t_sdf_annotate_unsup.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('linter') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_sdf_annotate_unsup.v b/test_regress/t/t_sdf_annotate_unsup.v new file mode 100644 index 000000000..dcad1c56c --- /dev/null +++ b/test_regress/t/t_sdf_annotate_unsup.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog 'alias' +// +// 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 + $sdf_annotate("file.sdf"); + $sdf_annotate("file.sdf",); + $sdf_annotate("file.sdf", t); + // TArguments are all optional, so test more exhaustively + $sdf_annotate("file.sdf", t, "config_file", "log_file", "mtm_spec", "scale_factors", + "scale_type"); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 9d146eae16499ea656de969ec26315521e5cd0e2 Mon Sep 17 00:00:00 2001 From: Ibrahim Burak Yorulmaz Date: Sun, 20 Jul 2025 02:22:02 +0200 Subject: [PATCH 044/252] Fix VPI signal range order (#6189) (#6200) --- docs/CONTRIBUTORS | 1 + src/V3EmitCSyms.cpp | 4 ++-- test_regress/t/t_vpi_var.cpp | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f82a13199..dceab099e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -84,6 +84,7 @@ Huang Rui Huanghuang Zhou HungMingWu HyungKi Jeong +Ibrahim Burak Yorulmaz Igor Zaworski Ilya Barkov Iru Cai diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index a07b431c6..c81020b64 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -964,9 +964,9 @@ void EmitCSyms::emitSymImp() { } else { if (basicp->isRanged()) { bounds += " ,"; - bounds += cvtToStr(basicp->hi()); + bounds += cvtToStr(basicp->left()); bounds += ","; - bounds += cvtToStr(basicp->lo()); + bounds += cvtToStr(basicp->right()); pdim++; } break; // AstBasicDType - nothing below, 1 diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 2cb16563f..3a04f6831 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -360,6 +360,24 @@ int _mon_check_var() { p = vpi_get_str(vpiType, vh10); CHECK_RESULT_CSTR(p, "vpiConstant"); } + TestVpiHandle vh6 = vpi_handle_by_index(vh5, 2); + CHECK_RESULT_NZ(vh6); + { + TestVpiHandle vh10 = vpi_handle(vpiLeftRange, vh6); + CHECK_RESULT_NZ(vh10); + vpi_get_value(vh10, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 0); + p = vpi_get_str(vpiType, vh10); + CHECK_RESULT_CSTR(p, "vpiConstant"); + } + { + TestVpiHandle vh10 = vpi_handle(vpiRightRange, vh6); + CHECK_RESULT_NZ(vh10); + vpi_get_value(vh10, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 61); + p = vpi_get_str(vpiType, vh10); + CHECK_RESULT_CSTR(p, "vpiConstant"); + } // C++ keyword collision { From 641e0e567272b47c3153922bef78dc379c6f5fe1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 19 Jul 2025 21:12:10 -0400 Subject: [PATCH 045/252] Internals: Fix assertion if bad display --- src/V3EmitCFunc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index ebec542ab..8031e4f5b 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -247,14 +247,14 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const AstNode* argp = nullptr; if (!ignore) { argp = *elistp; - // Prep for next parameter - *elistp = (*elistp)->nextp(); if (VL_UNCOVERABLE(!argp)) { // expectDisplay() checks this first, so internal error if found here dispp->v3error( "Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE } + // Prep for next parameter + *elistp = (*elistp)->nextp(); if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) { dispp->v3warn(E_UNSUPPORTED, "Unsupported: Exceeded limit of " + cvtToStr(VL_VALUE_STRING_MAX_WIDTH) From f535a73ea761368ece6da54e7be65811368786b6 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 19 Jul 2025 21:12:23 -0400 Subject: [PATCH 046/252] Commentary: Changes update --- Changes | 4 +++- docs/spelling.txt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 2d506b97f..c7b45459e 100644 --- a/Changes +++ b/Changes @@ -20,7 +20,7 @@ Verilator 5.039 devel * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] -* Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] +* Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). @@ -32,8 +32,10 @@ Verilator 5.039 devel * Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173) (#6176). [Geza Lore] * Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.]] * Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.] +* Fix virtual interface member propagation (#6175) (#6184). [Yilou Wang] * Fix `--coverage-expr` null pointer dereference (#6181). [Igor Zaworski, Antmicro Ltd.] * Fix conflicting function/class name linking error (#6182). [Igor Zaworski, Antmicro Ltd.] +* Fix VPI signal range order (#6189) (#6200). [Ibrahim Burak Yorulmaz] * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] diff --git a/docs/spelling.txt b/docs/spelling.txt index 118898847..055e931ab 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -314,6 +314,7 @@ Noack Nodine Ober Oleg +Oler Olof Olofsson Oron From b8b9478938ff1a713842c41acbedc712bfd9ae47 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 19 Jul 2025 22:37:07 -0400 Subject: [PATCH 047/252] Improve enum base type checking error message. --- src/V3Width.cpp | 32 +++++++++++++-------------- test_regress/t/t_enum_base_bad.out | 35 ++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 9790d17e8..def288069 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2484,10 +2484,14 @@ class WidthVisitor final : public VNVisitor { nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); nodep->dtypep(nodep); AstNodeDType* basicp = nodep->dtypep()->skipRefp()->basicp(); - if (!dtypeIsIntAtomOrVecRecurse(nodep->subDTypep())) { + AstNodeDType* const badDtp = dtypeNotIntAtomOrVecRecurse(nodep->subDTypep()); + if (badDtp) { nodep->v3error( - "Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not " - << nodep->subDTypep()->prettyDTypeNameQ()); + "Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19)\n" + << nodep->warnContextPrimary() << '\n' + << badDtp->warnOther() << "... Location of failing data type " + << badDtp->prettyDTypeNameQ() << '\n' + << badDtp->warnContextSecondary()); basicp = nodep->findSigned32DType()->basicp(); nodep->refDTypep(basicp); } @@ -8310,23 +8314,19 @@ class WidthVisitor final : public VNVisitor { if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp()); return false; } - bool dtypeIsIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) { + AstNodeDType* dtypeNotIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) { + // If node is _not_ integer or atomic, return node that makes it fail nodep = nodep->skipRefToEnump(); if (AstBasicDType* const dtp = VN_CAST(nodep, BasicDType)) { - if (ranged && (!dtp->isBitLogic() || dtp->isRanged())) { - UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); - return false; // Packed when already packed - } - if (dtp->keyword().isIntNumeric()) return true; + if (ranged && (!dtp->isBitLogic() || dtp->isRanged())) + return dtp; // Packed when already packed + if (dtp->keyword().isIntNumeric()) return nullptr; + return dtp; } else if (AstPackArrayDType* const dtp = VN_CAST(nodep, PackArrayDType)) { - if (ranged) { - UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); - return false; // Packed when already packed - } - return dtypeIsIntAtomOrVecRecurse(nodep->subDTypep(), true); + if (ranged) return dtp; // Packed when already packed + return dtypeNotIntAtomOrVecRecurse(nodep->subDTypep(), true); } - UINFO(9, "dtypeIsIntAtomOrVecRecurse false at " << nodep); - return false; + return nodep; } //---------------------------------------------------------------------- diff --git a/test_regress/t/t_enum_base_bad.out b/test_regress/t/t_enum_base_bad.out index f57b5e80c..1dff9944a 100644 --- a/test_regress/t/t_enum_base_bad.out +++ b/test_regress/t/t_enum_base_bad.out @@ -1,30 +1,51 @@ -%Error: t/t_enum_base_bad.v:11:11: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'struct{}t.s_t' +%Error: t/t_enum_base_bad.v:11:11: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 11 | typedef enum s_t { | ^~~~ + t/t_enum_base_bad.v:9:11: ... Location of failing data type 'struct{}t.s_t' + 9 | typedef struct {int a;} s_t; + | ^~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_enum_base_bad.v:16:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'int[1:0]' +%Error: t/t_enum_base_bad.v:16:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 16 | enum int_t [1:0] { | ^~~~ -%Error: t/t_enum_base_bad.v:21:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'bit[1:0][1:0]' + t/t_enum_base_bad.v:15:11: ... Location of failing data type 'int' + 15 | typedef int int_t; + | ^~~ +%Error: t/t_enum_base_bad.v:21:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 21 | enum d2_t { | ^~~~ -%Error: t/t_enum_base_bad.v:25:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'logic[1:0][1:0]' + t/t_enum_base_bad.v:20:11: ... Location of failing data type 'bit[1:0]' + 20 | typedef bit [1:0][1:0] d2_t; + | ^~~ +%Error: t/t_enum_base_bad.v:25:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 25 | enum logic [1:0][1:0] { | ^~~~ -%Error: t/t_enum_base_bad.v:30:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'struct{}t.str_t' + t/t_enum_base_bad.v:25:8: ... Location of failing data type 'logic[1:0]' + 25 | enum logic [1:0][1:0] { + | ^~~~~ +%Error: t/t_enum_base_bad.v:30:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 30 | enum str_t { | ^~~~ -%Error: t/t_enum_base_bad.v:35:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'enum{}t.enum_t' + t/t_enum_base_bad.v:29:11: ... Location of failing data type 'struct{}t.str_t' + 29 | typedef struct packed {int x;} str_t; + | ^~~~~~ +%Error: t/t_enum_base_bad.v:35:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 35 | enum enum_t { | ^~~~ -%Error: t/t_enum_base_bad.v:40:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19), not 'logic$[1:0]' + t/t_enum_base_bad.v:34:11: ... Location of failing data type 'enum{}t.enum_t' + 34 | typedef enum {ENUM_VAL} enum_t; + | ^~~~ +%Error: t/t_enum_base_bad.v:40:3: Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19) : ... note: In instance 't' 40 | enum array2_t { | ^~~~ + t/t_enum_base_bad.v:39:25: ... Location of failing data type 'logic$[1:0]' + 39 | typedef logic array2_t[1:0]; + | ^ %Error: Exiting due to From 078bb21a89fea853d408db573f8039220ffc1ffd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 20 Jul 2025 07:21:30 -0400 Subject: [PATCH 048/252] Add wire data type checking per IEEE. --- Changes | 2 +- docs/gen/ex_WIDTHEXPAND_1_faulty.rst | 6 +-- docs/gen/ex_WIDTHEXPAND_1_fixed.rst | 2 +- docs/gen/ex_WIDTHEXPAND_1_msg.rst | 2 +- src/V3Width.cpp | 31 ++++++++++++++ test_regress/t/t_extract_static_const.v | 8 ++-- .../t/t_extract_static_const_no_merge.py | 2 +- test_regress/t/t_flag_prefix.v | 2 +- .../t/t_lint_widthexpand_docs_bad.out | 6 +-- test_regress/t/t_lint_widthexpand_docs_bad.v | 16 ++++---- test_regress/t/t_net_dtype_bad.out | 37 +++++++++++++++++ test_regress/t/t_net_dtype_bad.py | 16 ++++++++ test_regress/t/t_net_dtype_bad.v | 40 +++++++++++++++++++ test_regress/t/t_scheduling_many_clocks.v | 3 +- test_regress/t/t_struct_init.v | 16 ++++---- test_regress/t/t_trace_dumpvars_dyn.v | 6 ++- test_regress/t/t_trace_dumpvars_dyn_fst_0.out | 18 ++++----- test_regress/t/t_trace_dumpvars_dyn_fst_1.out | 12 +++--- test_regress/t/t_trace_scope_vlt.v | 6 ++- test_regress/t/t_wire_types.v | 25 ------------ 20 files changed, 181 insertions(+), 75 deletions(-) create mode 100644 test_regress/t/t_net_dtype_bad.out create mode 100755 test_regress/t/t_net_dtype_bad.py create mode 100644 test_regress/t/t_net_dtype_bad.v diff --git a/Changes b/Changes index c7b45459e..ae6acf5d3 100644 --- a/Changes +++ b/Changes @@ -16,7 +16,7 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. -* Add enum base type checking per IEEE. +* Add enum base data type, and wire data type checking per IEEE. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] diff --git a/docs/gen/ex_WIDTHEXPAND_1_faulty.rst b/docs/gen/ex_WIDTHEXPAND_1_faulty.rst index 08c1f0ba2..5dcab8d80 100644 --- a/docs/gen/ex_WIDTHEXPAND_1_faulty.rst +++ b/docs/gen/ex_WIDTHEXPAND_1_faulty.rst @@ -3,6 +3,6 @@ :linenos: :emphasize-lines: 3 - int array[5]; - bit [1:0] rd_addr; - wire int rd_value = array[rd_addr]; //<--- Warning + logic [31:0] array[5]; + bit [1:0] rd_addr; + wire [31:0] rd_value = array[rd_addr]; //<--- Warning diff --git a/docs/gen/ex_WIDTHEXPAND_1_fixed.rst b/docs/gen/ex_WIDTHEXPAND_1_fixed.rst index b40b8b2a6..616e5c1d0 100644 --- a/docs/gen/ex_WIDTHEXPAND_1_fixed.rst +++ b/docs/gen/ex_WIDTHEXPAND_1_fixed.rst @@ -2,4 +2,4 @@ .. code-block:: sv :emphasize-lines: 1 - wire int rd_value = array[{1'b0, rd_addr}]; //<--- Fixed + wire [31:0] rd_value = array[{1'b0, rd_addr}]; //<--- Fixed diff --git a/docs/gen/ex_WIDTHEXPAND_1_msg.rst b/docs/gen/ex_WIDTHEXPAND_1_msg.rst index 927476284..57806096f 100644 --- a/docs/gen/ex_WIDTHEXPAND_1_msg.rst +++ b/docs/gen/ex_WIDTHEXPAND_1_msg.rst @@ -1,4 +1,4 @@ .. comment: generated by t_lint_widthexpand_docs_bad .. code-block:: - %Warning-WIDTHEXPAND: example.v:3:29 Bit extraction of array[4:0] requires 3 bit index, not 2 bits. + %Warning-WIDTHEXPAND: example.v:3:31 Bit extraction of array[4:0] requires 3 bit index, not 2 bits. diff --git a/src/V3Width.cpp b/src/V3Width.cpp index def288069..f938a6a5c 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2422,6 +2422,18 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetLogicSized(1, bdtypep->numeric()); VL_DANGLING(bdtypep); } + if (nodep->isNet()) { + AstNodeDType* const badDtp = dtypeNot4StateIntegralRecurse(nodep->dtypep()); + if (badDtp) + nodep->v3error( + "Net " << nodep->prettyNameQ() + << " data type must be 4-state integral or array/union/struct of such" + << " (IEEE 1800-2023 6.7.1)\n" + << nodep->warnContextPrimary() << '\n' + << badDtp->warnOther() << "... Location of failing data type " + << badDtp->prettyDTypeNameQ() << '\n' + << badDtp->warnContextSecondary()); + } if (nodep->valuep() && !didchk) { // if (debug()) nodep->dumpTree("- final: "); // AstPattern requires assignments to pass datatype on PRELIM @@ -8328,6 +8340,25 @@ class WidthVisitor final : public VNVisitor { } return nodep; } + AstNodeDType* dtypeNot4StateIntegralRecurse(AstNodeDType* nodep) { + // If node is _not_ inet valid data type, 4-state integral packed or union, return node + // that makes it fail + nodep = nodep->skipRefp(); + if (AstBasicDType* const dtp = VN_CAST(nodep, BasicDType)) { + if (!dtp->keyword().isFourstate()) return dtp; + return nullptr; + } else if (AstNodeArrayDType* const dtp = VN_CAST(nodep, NodeArrayDType)) { + return dtypeNot4StateIntegralRecurse(nodep->subDTypep()); + } else if (AstNodeUOrStructDType* const dtp = VN_CAST(nodep, NodeUOrStructDType)) { + for (AstMemberDType* itemp = dtp->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + AstNodeDType* const badDtp = dtypeNot4StateIntegralRecurse(itemp->dtypep()); + if (badDtp) return badDtp; + } + return nullptr; + } + return nodep; + } //---------------------------------------------------------------------- // METHODS - special type detection diff --git a/test_regress/t/t_extract_static_const.v b/test_regress/t/t_extract_static_const.v index 94a1c528b..2eb35d791 100644 --- a/test_regress/t/t_extract_static_const.v +++ b/test_regress/t/t_extract_static_const.v @@ -6,7 +6,8 @@ module t (/*AUTOARG*/); - wire bit [255:0] C = {32'h1111_1111, + bit [255:0] C; + initial C = {32'h1111_1111, 32'h2222_2222, 32'h3333_3333, 32'h4444_4444, @@ -16,7 +17,8 @@ module t (/*AUTOARG*/); 32'h8888_8888}; // Same values as above, but with different type - wire logic [255:0] D = {32'h1111_1111, + logic [255:0] D; + initial D = {32'h1111_1111, 32'h2222_2222, 32'h3333_3333, 32'h4444_4444, @@ -28,7 +30,7 @@ module t (/*AUTOARG*/); int i; initial begin - // Note: Base index via $c to prevent optimizatoin by Verilator + // Note: Base index via $c to prevent optimization by Verilator i = $c(0*32); $display("0x%8x", C[i+:32]); i = $c(1*32); $display("0x%8x", D[i+:32]); i = $c(2*32); $display("0x%8x", C[i+:32]); diff --git a/test_regress/t/t_extract_static_const_no_merge.py b/test_regress/t/t_extract_static_const_no_merge.py index 8c3835a5d..354d90d19 100755 --- a/test_regress/t/t_extract_static_const_no_merge.py +++ b/test_regress/t/t_extract_static_const_no_merge.py @@ -19,6 +19,6 @@ test.execute(expect_filename=test.golden_filename) if test.vlt_all: test.file_grep(test.stats, r'Optimizations, Prelim extracted value to ConstPool\s+(\d+)', 8) - test.file_grep(test.stats, r'ConstPool, Constants emitted\s+(\d+)', 2) + test.file_grep(test.stats, r'ConstPool, Constants emitted\s+(\d+)', 1) test.passes() diff --git a/test_regress/t/t_flag_prefix.v b/test_regress/t/t_flag_prefix.v index 1ee503c93..d350086b8 100644 --- a/test_regress/t/t_flag_prefix.v +++ b/test_regress/t/t_flag_prefix.v @@ -13,7 +13,7 @@ module sub; /* verilator no_inline_module */ // Goes into const pool which is separate file - wire bit [255:0] C = {32'h1111_1111, + wire logic [255:0] C = {32'h1111_1111, 32'h2222_2222, 32'h3333_3333, 32'h4444_4444, diff --git a/test_regress/t/t_lint_widthexpand_docs_bad.out b/test_regress/t/t_lint_widthexpand_docs_bad.out index 8af03b587..686a662d9 100644 --- a/test_regress/t/t_lint_widthexpand_docs_bad.out +++ b/test_regress/t/t_lint_widthexpand_docs_bad.out @@ -1,7 +1,7 @@ -%Warning-WIDTHEXPAND: t/t_lint_widthexpand_docs_bad.v:10:29: Bit extraction of array[4:0] requires 3 bit index, not 2 bits. +%Warning-WIDTHEXPAND: t/t_lint_widthexpand_docs_bad.v:10:31: Bit extraction of array[4:0] requires 3 bit index, not 2 bits. : ... note: In instance 't' - 10 | wire int rd_value = array[rd_addr]; - | ^ + 10 | wire [31:0] rd_value = array[rd_addr]; + | ^ ... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=latest ... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message. %Error: Exiting due to diff --git a/test_regress/t/t_lint_widthexpand_docs_bad.v b/test_regress/t/t_lint_widthexpand_docs_bad.v index 1a99073a0..896a6d389 100644 --- a/test_regress/t/t_lint_widthexpand_docs_bad.v +++ b/test_regress/t/t_lint_widthexpand_docs_bad.v @@ -5,15 +5,15 @@ // SPDX-License-Identifier: CC0-1.0 module t; - int array[5]; - bit [1:0] rd_addr; - wire int rd_value = array[rd_addr]; //<--- Warning + logic [31:0] array[5]; + bit [1:0] rd_addr; + wire [31:0] rd_value = array[rd_addr]; //<--- Warning - ok ok(); + ok ok (); endmodule module ok; - int array[5]; - bit [1:0] rd_addr; - wire int rd_value = array[{1'b0, rd_addr}]; //<--- Fixed -endmodule; + logic [31:0] array[5]; + bit [1:0] rd_addr; + wire [31:0] rd_value = array[{1'b0, rd_addr}]; //<--- Fixed +endmodule diff --git a/test_regress/t/t_net_dtype_bad.out b/test_regress/t/t_net_dtype_bad.out new file mode 100644 index 000000000..2bf44915c --- /dev/null +++ b/test_regress/t/t_net_dtype_bad.out @@ -0,0 +1,37 @@ +%Error: t/t_net_dtype_bad.v:25:15: Net 'bad_real' data type must be 4-state integral or array/union/struct of such (IEEE 1800-2023 6.7.1) + : ... note: In instance 't' + 25 | wire real_t bad_real; + | ^~~~~~~~ + t/t_net_dtype_bad.v:11:11: ... Location of failing data type 'real' + 11 | typedef real real_t; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_net_dtype_bad.v:27:12: Net 'bad_class' data type must be 4-state integral or array/union/struct of such (IEEE 1800-2023 6.7.1) + : ... note: In instance 't' + 27 | wire Cls bad_class; + | ^~~~~~~~~ + t/t_net_dtype_bad.v:27:8: ... Location of failing data type 'class{}Cls' + 27 | wire Cls bad_class; + | ^~~ +%Error: t/t_net_dtype_bad.v:29:15: Net 'bad_string' data type must be 4-state integral or array/union/struct of such (IEEE 1800-2023 6.7.1) + : ... note: In instance 't' + 29 | wire string bad_string; + | ^~~~~~~~~~ + t/t_net_dtype_bad.v:29:8: ... Location of failing data type 'string' + 29 | wire string bad_string; + | ^~~~~~ +%Error: t/t_net_dtype_bad.v:31:12: Net 'bad_bit' data type must be 4-state integral or array/union/struct of such (IEEE 1800-2023 6.7.1) + : ... note: In instance 't' + 31 | wire bit bad_bit; + | ^~~~~~~ + t/t_net_dtype_bad.v:31:8: ... Location of failing data type 'bit' + 31 | wire bit bad_bit; + | ^~~ +%Error: t/t_net_dtype_bad.v:33:14: Net 'bad_struct' data type must be 4-state integral or array/union/struct of such (IEEE 1800-2023 6.7.1) + : ... note: In instance 't' + 33 | wire bad_t bad_struct; + | ^~~~~~~~~~ + t/t_net_dtype_bad.v:14:5: ... Location of failing data type 'bit' + 14 | bit m_bit; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_net_dtype_bad.py b/test_regress/t/t_net_dtype_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_net_dtype_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_net_dtype_bad.v b/test_regress/t/t_net_dtype_bad.v new file mode 100644 index 000000000..f5f3383ea --- /dev/null +++ b/test_regress/t/t_net_dtype_bad.v @@ -0,0 +1,40 @@ +// 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 + +class Cls; +endclass + +module t; + typedef real real_t; + + typedef struct packed { + bit m_bit; + } bad_t; + + typedef struct { + logic m_bit; + } ok_unpk_t; + + typedef struct packed { + logic m_bit; + } ok_t; + + wire real_t bad_real; // <--- Error - bad net type + + wire Cls bad_class; // <--- Error - bad net type + + wire string bad_string; // <--- Error - bad net type + + wire bit bad_bit; // <--- Error - bad net type + + wire bad_t bad_struct; // <--- Error - bad net type + + wire ok_unpk_t ok_unpk_struct; + + wire ok_t ok_struct; // Ok + + initial $stop; +endmodule diff --git a/test_regress/t/t_scheduling_many_clocks.v b/test_regress/t/t_scheduling_many_clocks.v index 04aeccb45..b403efb4e 100644 --- a/test_regress/t/t_scheduling_many_clocks.v +++ b/test_regress/t/t_scheduling_many_clocks.v @@ -45,7 +45,8 @@ module t(/*AUTOARG*/ int cnt = 0; always @(posedge gclk[n]) cnt <= cnt + 1; - wire int cnt_plus_one = cnt + 1; + int cnt_plus_one; + always_comb cnt_plus_one = cnt + 1; final begin `checkh(cnt_plus_one, ITERATIONS + 1); diff --git a/test_regress/t/t_struct_init.v b/test_regress/t/t_struct_init.v index 79f2dd373..c9825d019 100644 --- a/test_regress/t/t_struct_init.v +++ b/test_regress/t/t_struct_init.v @@ -10,10 +10,10 @@ module t; //typedef struct pack2; // Forward declaration typedef struct packed { // [3:0] - bit b3; - bit b2; - bit b1; - bit b0; + logic b3; + logic b2; + logic b1; + logic b0; } b4_t; typedef struct packed { // [3:0] @@ -28,17 +28,17 @@ module t; } q4_t; typedef struct packed { // [5:0] - bit msb; + logic msb; q4_t four; - bit lsb; + logic lsb; } pack2_t; typedef union packed { // [5:0] pack2_t pack2; bit [6:1] pvec; // Vector not allowed in packed structure, per spec: - // bit vec[6]; - // bit vec2d[2][3]; + // logic vec[6]; + // logic vec2d[2][3]; } pack3_t; const b4_t b4_const_a = '{1'b1, 1'b0, 1'b0, 1'b1}; diff --git a/test_regress/t/t_trace_dumpvars_dyn.v b/test_regress/t/t_trace_dumpvars_dyn.v index dfeaa16dd..4c9705348 100644 --- a/test_regress/t/t_trace_dumpvars_dyn.v +++ b/test_regress/t/t_trace_dumpvars_dyn.v @@ -28,7 +28,8 @@ endmodule module sub1 #(parameter int ADD) (input int cyc); - wire int value = cyc + ADD; + int value; + always_comb value = cyc + ADD; sub2 #(ADD + 1) sub2a(.*); sub2 #(ADD + 2) sub2b(.*); @@ -38,5 +39,6 @@ endmodule module sub2 #(parameter int ADD) (input int cyc); - wire int value = cyc + ADD; + int value; + always_comb value = cyc + ADD; endmodule diff --git a/test_regress/t/t_trace_dumpvars_dyn_fst_0.out b/test_regress/t/t_trace_dumpvars_dyn_fst_0.out index e3d5eaaf3..ed5398dd3 100644 --- a/test_regress/t/t_trace_dumpvars_dyn_fst_0.out +++ b/test_regress/t/t_trace_dumpvars_dyn_fst_0.out @@ -1,5 +1,5 @@ $date - Sat Mar 5 14:06:13 2022 + Sat Jul 19 22:57:16 2025 $end $version @@ -16,41 +16,41 @@ $var int 32 " cyc [31:0] $end $scope module sub1a $end $var parameter 32 # ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 $ value [31:0] $end +$var int 32 $ value [31:0] $end $scope module sub2a $end $var parameter 32 % ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 & value [31:0] $end +$var int 32 & value [31:0] $end $upscope $end $scope module sub2b $end $var parameter 32 ' ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 ( value [31:0] $end +$var int 32 ( value [31:0] $end $upscope $end $scope module sub2c $end $var parameter 32 ) ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 * value [31:0] $end +$var int 32 * value [31:0] $end $upscope $end $upscope $end $scope module sub1b $end $var parameter 32 + ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 , value [31:0] $end +$var int 32 , value [31:0] $end $scope module sub2a $end $var parameter 32 - ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 . value [31:0] $end +$var int 32 . value [31:0] $end $upscope $end $scope module sub2b $end $var parameter 32 / ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 0 value [31:0] $end +$var int 32 0 value [31:0] $end $upscope $end $scope module sub2c $end $var parameter 32 1 ADD [31:0] $end $var wire 32 " cyc [31:0] $end -$var wire 32 2 value [31:0] $end +$var int 32 2 value [31:0] $end $upscope $end $upscope $end $upscope $end diff --git a/test_regress/t/t_trace_dumpvars_dyn_fst_1.out b/test_regress/t/t_trace_dumpvars_dyn_fst_1.out index 68b1899ee..6426ca007 100644 --- a/test_regress/t/t_trace_dumpvars_dyn_fst_1.out +++ b/test_regress/t/t_trace_dumpvars_dyn_fst_1.out @@ -1,5 +1,5 @@ $date - Sat Apr 5 13:56:24 2025 + Sat Jul 19 22:57:23 2025 $end $version @@ -14,7 +14,7 @@ $var int 32 ! cyc [31:0] $end $scope module sub1a $end $var parameter 32 " ADD [31:0] $end $var wire 32 ! cyc [31:0] $end -$var wire 32 # value [31:0] $end +$var int 32 # value [31:0] $end $scope module sub2a $end $upscope $end $scope module sub2b $end @@ -25,21 +25,21 @@ $upscope $end $scope module sub1b $end $var parameter 32 $ ADD [31:0] $end $var wire 32 ! cyc [31:0] $end -$var wire 32 % value [31:0] $end +$var int 32 % value [31:0] $end $scope module sub2a $end $var parameter 32 & ADD [31:0] $end $var wire 32 ! cyc [31:0] $end -$var wire 32 ' value [31:0] $end +$var int 32 ' value [31:0] $end $upscope $end $scope module sub2b $end $var parameter 32 ( ADD [31:0] $end $var wire 32 ! cyc [31:0] $end -$var wire 32 ) value [31:0] $end +$var int 32 ) value [31:0] $end $upscope $end $scope module sub2c $end $var parameter 32 * ADD [31:0] $end $var wire 32 ! cyc [31:0] $end -$var wire 32 + value [31:0] $end +$var int 32 + value [31:0] $end $upscope $end $upscope $end $upscope $end diff --git a/test_regress/t/t_trace_scope_vlt.v b/test_regress/t/t_trace_scope_vlt.v index dfeaa16dd..4c9705348 100644 --- a/test_regress/t/t_trace_scope_vlt.v +++ b/test_regress/t/t_trace_scope_vlt.v @@ -28,7 +28,8 @@ endmodule module sub1 #(parameter int ADD) (input int cyc); - wire int value = cyc + ADD; + int value; + always_comb value = cyc + ADD; sub2 #(ADD + 1) sub2a(.*); sub2 #(ADD + 2) sub2b(.*); @@ -38,5 +39,6 @@ endmodule module sub2 #(parameter int ADD) (input int cyc); - wire int value = cyc + ADD; + int value; + always_comb value = cyc + ADD; endmodule diff --git a/test_regress/t/t_wire_types.v b/test_regress/t/t_wire_types.v index a1389327e..d99864d9b 100644 --- a/test_regress/t/t_wire_types.v +++ b/test_regress/t/t_wire_types.v @@ -15,48 +15,23 @@ module t (/*AUTOARG*/ input clk; // IEEE: integer_atom_type - wire byte w_byte; - wire shortint w_shortint; - wire int w_int; - wire longint w_longint; wire integer w_integer; // IEEE: integer_atom_type - wire bit w_bit; wire logic w_logic; - wire bit [1:0] w_bit2; wire logic [1:0] w_logic2; - // IEEE: non_integer_type - //UNSUP shortreal w_shortreal; - wire real w_real; - - assign w_byte = 8'h12; - assign w_shortint = 16'h1234; - assign w_int = -123456; - assign w_longint = -1234567; assign w_integer = -123456; - assign w_bit = 1'b1; assign w_logic = 1'b1; - assign w_bit2 = 2'b10; assign w_logic2 = 2'b10; - assign w_real = 3.14; - always @ (posedge clk) begin - `checkh(w_byte, 8'h12); - `checkh(w_shortint, 16'h1234); - `checkh(w_int, -123456); - `checkh(w_longint, -1234567); `checkh(w_integer, -123456); - `checkh(w_bit, 1'b1); `checkh(w_logic, 1'b1); - `checkh(w_bit2, 2'b10); `checkh(w_logic2, 2'b10); - `checkr(w_real, 3.14); $write("*-* All Finished *-*\n"); $finish; end From a50ea2a1a6af4474ac99b50c078bb95de9e76a14 Mon Sep 17 00:00:00 2001 From: Max Wipfli Date: Sun, 20 Jul 2025 15:56:34 +0200 Subject: [PATCH 049/252] Optimize 2 ** X to 1 << X if base is signed (#6203) --- docs/CONTRIBUTORS | 1 + src/V3Const.cpp | 9 +++++++-- test_regress/t/t_math_pow.v | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index dceab099e..bbe5f8c56 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -158,6 +158,7 @@ Martin Schmidt Martin Stadler Mateusz Gancarz Matthew Ballance +Max Wipfli Michael Bikovitsky Michael Killough Michal Czyz diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 690e70db2..31a8688a1 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1267,8 +1267,12 @@ class ConstVisitor final : public VNVisitor { && nodep->lhsp()->isPure()); } bool operandIsTwo(const AstNode* nodep) { - return (VN_IS(nodep, Const) && !VN_AS(nodep, Const)->num().isFourState() - && nodep->width() <= VL_QUADSIZE && VN_AS(nodep, Const)->toUQuad() == 2); + const AstConst* const constp = VN_CAST(nodep, Const); + if (!constp) return false; // not constant + if (constp->num().isFourState()) return false; // four-state + if (nodep->width() > VL_QUADSIZE) return false; // too wide + if (nodep->isSigned() && constp->num().isNegative()) return false; // signed and negative + return constp->toUQuad() == 2; } bool operandIsTwostate(const AstNode* nodep) { return (VN_IS(nodep, Const) && !VN_AS(nodep, Const)->num().isFourState()); @@ -3550,6 +3554,7 @@ class ConstVisitor final : public VNVisitor { TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1) TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y)) TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b) // Trinary ops diff --git a/test_regress/t/t_math_pow.v b/test_regress/t/t_math_pow.v index bf7236e89..286fe0546 100644 --- a/test_regress/t/t_math_pow.v +++ b/test_regress/t/t_math_pow.v @@ -37,7 +37,8 @@ module t (/*AUTOARG*/ wire signed [66:0] bsw = b[66:0]; // verilator lint_off WIDTH - wire [66:0] shifted = 2 ** b[20:0]; + wire [66:0] shifted = 32'd2 ** b[20:0]; + wire [66:0] shifted_signed = 32'sd2 ** b[20:0]; wire [15:0] uiii = aui ** bui; wire [15:0] uiiq = aui ** buq; @@ -358,5 +359,6 @@ module t (/*AUTOARG*/ 32'd09: `checkh(shifted, 67'h0000000000000000); default: ; endcase + `checkh(shifted_signed, shifted); end endmodule From d419c49921a6345954244a089bf9e013e9dc5db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Mon, 21 Jul 2025 13:28:01 +0200 Subject: [PATCH 050/252] Internals: Fix unused variable, no functional change (#6204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bartłomiej Chmiel --- src/V3Width.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f938a6a5c..20903fede 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -8348,7 +8348,7 @@ class WidthVisitor final : public VNVisitor { if (!dtp->keyword().isFourstate()) return dtp; return nullptr; } else if (AstNodeArrayDType* const dtp = VN_CAST(nodep, NodeArrayDType)) { - return dtypeNot4StateIntegralRecurse(nodep->subDTypep()); + return dtypeNot4StateIntegralRecurse(dtp->subDTypep()); } else if (AstNodeUOrStructDType* const dtp = VN_CAST(nodep, NodeUOrStructDType)) { for (AstMemberDType* itemp = dtp->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { From 98b8d43a4ae52dba9d42422e61c76c52be5d5c0d Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Mon, 21 Jul 2025 13:30:10 +0200 Subject: [PATCH 051/252] Fix parameter-dependent type linking (#6170) --- src/V3LinkDot.cpp | 7 ++++--- src/V3Param.cpp | 2 +- test_regress/t/t_class_param_subtype2.py | 18 ++++++++++++++++ test_regress/t/t_class_param_subtype2.v | 26 ++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_class_param_subtype2.py create mode 100644 test_regress/t/t_class_param_subtype2.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index a654141b8..425bb1f40 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3589,9 +3589,6 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(9, indent() << m_ds.ascii()); VL_RESTORER(m_usedPins); m_usedPins.clear(); - UASSERT_OBJ(m_statep->forPrimary() || VN_IS(nodep->classOrPackageNodep(), ParamTypeDType) - || nodep->classOrPackageSkipp(), - nodep, "ClassRef has unlinked class"); UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep, "class reference parameter not removed by V3Param"); { @@ -3602,6 +3599,10 @@ class LinkDotResolveVisitor final : public VNVisitor { m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE, false, ":: reference"); } + UASSERT_OBJ(m_statep->forPrimary() + || VN_IS(nodep->classOrPackageNodep(), ParamTypeDType) + || nodep->classOrPackageSkipp(), + nodep, "ClassRef has unlinked class"); // ClassRef's have pins, so track if (nodep->classOrPackageSkipp()) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index d3ceb7a24..09b527a65 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1116,7 +1116,7 @@ class ParamVisitor final : public VNVisitor { } else { cellp->v3fatalSrc("Expected module parameterization"); } - UASSERT_OBJ(srcModp, cellp, "Unlinked class ref"); + if (!srcModp) continue; // Update path string someInstanceName = modp->someInstanceName(); diff --git a/test_regress/t/t_class_param_subtype2.py b/test_regress/t/t_class_param_subtype2.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_param_subtype2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_param_subtype2.v b/test_regress/t/t_class_param_subtype2.v new file mode 100644 index 000000000..5bb85b6e3 --- /dev/null +++ b/test_regress/t/t_class_param_subtype2.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Class1 #(type T); + static function int get(); + return T::Helper::getter(); + endfunction +endclass + +class Class2; + typedef Class2 Helper; + static function int getter(); + return 13; + endfunction +endclass + +module t; + initial begin + if (Class1#(Class2)::get() != 13) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From a8dca71ed05a66614093de96cfa62bf93faaeab3 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 21 Jul 2025 17:33:12 +0100 Subject: [PATCH 052/252] Support more complex combinational assignments in DFG. (#6205) Previously DFG was limited to having a Sel, or an ArraySel potentially under a Concat on the LHS of combinational assignments. Other forms or combinations were not representable in the graph. This adds support for arbitrary combinations of the above by combining DfgSplicePacked and DfgSpliceArray vertices introduced in #6176. In particular, Sel(ArraySel(VarRef,_),_) enables a lot more code to be represented in DFG. --- src/V3Dfg.cpp | 26 +- src/V3DfgAstToDfg.cpp | 665 ++++++++++++------- src/V3DfgDfgToAst.cpp | 103 ++- src/V3DfgPeephole.cpp | 13 +- src/V3DfgRegularize.cpp | 2 +- src/V3DfgVertices.h | 31 +- test_regress/t/t_dfg_break_cycles.py | 3 + test_regress/t/t_dfg_multidriver_dfg_bad.out | 98 ++- test_regress/t/t_dfg_multidriver_dfg_bad.v | 28 +- test_regress/t/t_dfg_peephole.cpp | 10 +- test_regress/t/t_dfg_peephole.py | 9 +- test_regress/t/t_dfg_peephole.v | 24 +- test_regress/t/t_inst_array_partial.v | 2 + 13 files changed, 658 insertions(+), 356 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 613b29e7d..04679edd8 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -246,8 +246,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { AstNode* const nodep = varVtxp->nodep(); AstVar* const varp = varVtxp->varp(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << "\nW" << varVtxp->width() << " / F" - << varVtxp->fanout() << '"'; + os << " [label=\"" << nodep->name() << "\n"; + varVtxp->dtypep()->dumpSmall(os); + os << " / F" << varVtxp->fanout() << '"'; if (varp->direction() == VDirection::INPUT) { os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green @@ -273,9 +274,10 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { if (const DfgVarArray* const arrVtxp = vtx.cast()) { AstNode* const nodep = arrVtxp->nodep(); AstVar* const varp = arrVtxp->varp(); - const int elements = VN_AS(arrVtxp->dtypep(), UnpackArrayDType)->elementsConst(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << "[" << elements << "]\""; + os << " [label=\"" << nodep->name() << "\n"; + arrVtxp->dtypep()->dumpSmall(os); + os << " / F" << arrVtxp->fanout() << '"'; if (varp->direction() == VDirection::INPUT) { os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green } else if (varp->direction() == VDirection::OUTPUT) { @@ -318,8 +320,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { const uint32_t lsb = selVtxp->lsb(); const uint32_t msb = lsb + selVtxp->width() - 1; os << toDotId(vtx); - os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\nW" << vtx.width() << " / F" - << vtx.fanout() << '"'; + os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\n"; + vtx.dtypep()->dumpSmall(os); + os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { os << ", shape=doublecircle"; } else { @@ -332,12 +335,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { if (vtx.is()) { os << toDotId(vtx); os << " [label=\"" << vtx.typeName() << "\n"; - if (const DfgSpliceArray* const sp = vtx.cast()) { - const int elements = VN_AS(sp->dtypep(), UnpackArrayDType)->elementsConst(); - os << "_[" << elements << "]"; - } else { - os << "W" << vtx.width(); - } + vtx.dtypep()->dumpSmall(os); os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { os << ", shape=doubleoctagon"; @@ -349,7 +347,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } os << toDotId(vtx); - os << " [label=\"" << vtx.typeName() << "\nW" << vtx.width() << " / F" << vtx.fanout() << '"'; + os << " [label=\"" << vtx.typeName() << "\n"; + vtx.dtypep()->dumpSmall(os); + os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { os << ", shape=doublecircle"; } else { diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index dc121cf00..8691b1a53 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -28,9 +28,12 @@ #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT +#include "V3Const.h" #include "V3Dfg.h" #include "V3DfgPasses.h" +#include + VL_DEFINE_DEBUG_FUNCTIONS; namespace { @@ -42,6 +45,17 @@ T_Vertex* makeVertex(const T_Node* nodep, DfgGraph& dfg) { return new T_Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; } +template <> +DfgArraySel* makeVertex(const AstArraySel* nodep, DfgGraph& dfg) { + // Some earlier passes create malformed ArraySels, just bail on those... + // See t_bitsel_wire_array_bad + if (VN_IS(nodep->fromp(), Const)) return nullptr; + AstUnpackArrayDType* const fromDtypep + = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType); + if (!fromDtypep) return nullptr; + return new DfgArraySel{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; +} + //====================================================================== // Currently unhandled nodes // LCOV_EXCL_START @@ -77,17 +91,6 @@ class AstToDfgVisitor final : public VNVisitor { const VNUser1InUse m_user1InUse; // TYPES - // Represents a driver during canonicalization - struct Driver final { - FileLine* m_fileline; - DfgVertex* m_vtxp; - uint32_t m_lsb; - Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) - : m_fileline{flp} - , m_vtxp{vtxp} - , m_lsb{lsb} {} - }; - using RootType = std::conditional_t; using VariableType = std::conditional_t; @@ -183,93 +186,144 @@ class AstToDfgVisitor final : public VNVisitor { return m_foundUnhandled; } - DfgVertexSplice* convertLValue(AstNode* nodep) { - FileLine* const flp = nodep->fileline(); - + std::pair convertLValue(AstNode* nodep) { if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { m_foundUnhandled = false; visit(vrefp); - if (m_foundUnhandled) return nullptr; + if (m_foundUnhandled) return {nullptr, 0}; + + // Get the variable vertex DfgVertexVar* const vtxp = getVertex(vrefp)->template as(); - // Ensure driving splice vertex exists + // Ensure the Splice driver exists for this variable if (!vtxp->srcp()) { - if (VN_IS(vtxp->dtypep(), UnpackArrayDType)) { - vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, vtxp->dtypep()}); + FileLine* const flp = vtxp->fileline(); + AstNodeDType* const dtypep = vtxp->dtypep(); + if (vtxp->is()) { + vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, dtypep}); + } else if (vtxp->is()) { + vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, dtypep}); } else { - vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, vtxp->dtypep()}); + nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE } } - return vtxp->srcp()->as(); + // Return the Splice driver + return {vtxp->srcp()->as(), 0}; + } + + if (AstSel* selp = VN_CAST(nodep, Sel)) { + // Only handle constant selects + const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); + if (!lsbp) { + ++m_ctx.m_nonRepLhs; + return {nullptr, 0}; + } + uint32_t lsb = lsbp->toUInt(); + + // Convert the 'fromp' sub-expression + const auto pair = convertLValue(selp->fromp()); + if (!pair.first) return {nullptr, 0}; + DfgSplicePacked* const splicep = pair.first->template as(); + // Adjust index. + lsb += pair.second; + + // AstSel doesn't change type kind (array vs packed), so we can use + // the existing splice driver with adjusted lsb + return {splicep, lsb}; + } + + if (AstArraySel* const aselp = VN_CAST(nodep, ArraySel)) { + // Only handle constant selects + const AstConst* const indexp = VN_CAST(aselp->bitp(), Const); + if (!indexp) { + ++m_ctx.m_nonRepLhs; + return {nullptr, 0}; + } + uint32_t index = indexp->toUInt(); + + // Convert the 'fromp' sub-expression + const auto pair = convertLValue(aselp->fromp()); + if (!pair.first) return {nullptr, 0}; + DfgSpliceArray* const splicep = pair.first->template as(); + // Adjust index. Note pair.second is always 0, but we might handle array slices later.. + index += pair.second; + + // Ensure the Splice driver exists for this element + if (!splicep->driverAt(index)) { + FileLine* const flp = nodep->fileline(); + AstNodeDType* const dtypep = DfgVertex::dtypeFor(nodep); + if (VN_IS(dtypep, BasicDType)) { + splicep->addDriver(flp, index, new DfgSplicePacked{*m_dfgp, flp, dtypep}); + } else if (VN_IS(dtypep, UnpackArrayDType)) { + splicep->addDriver(flp, index, new DfgSpliceArray{*m_dfgp, flp, dtypep}); + } else { + nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE + } + } + + // Return the splice driver + return {splicep->driverAt(index)->as(), 0}; } ++m_ctx.m_nonRepLhs; - return nullptr; + return {nullptr, 0}; } // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. - bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) { + bool convertAssignment(FileLine* flp, AstNode* lhsp, DfgVertex* vtxp) { + // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) + lhsp = V3Const::constifyExpensiveEdit(lhsp); + // Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part - if (AstConcat* const concatp = VN_CAST(nodep, Concat)) { - AstNode* const lhsp = concatp->lhsp(); - AstNode* const rhsp = concatp->rhsp(); + if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { + AstNode* const cLhsp = concatp->lhsp(); + AstNode* const cRhsp = concatp->rhsp(); { // Convet LHS of concat - FileLine* const lFlp = lhsp->fileline(); - DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)}; + FileLine* const lFlp = cLhsp->fileline(); + DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(cLhsp)}; lVtxp->fromp(vtxp); - lVtxp->lsb(rhsp->width()); - if (!convertAssignment(flp, lhsp, lVtxp)) return false; + lVtxp->lsb(cRhsp->width()); + if (!convertAssignment(flp, cLhsp, lVtxp)) return false; } { // Convert RHS of concat - FileLine* const rFlp = rhsp->fileline(); - DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)}; + FileLine* const rFlp = cRhsp->fileline(); + DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(cRhsp)}; rVtxp->fromp(vtxp); rVtxp->lsb(0); - return convertAssignment(flp, rhsp, rVtxp); + return convertAssignment(flp, cRhsp, rVtxp); } } - if (AstSel* const selp = VN_CAST(nodep, Sel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); - if (!vrefp || !lsbp) { - ++m_ctx.m_nonRepLhs; - return false; - } - if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { - splicep->template as()->addDriver(flp, lsbp->toUInt(), vtxp); - return true; - } - } else if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const idxp = VN_CAST(selp->bitp(), Const); - if (!vrefp || !idxp) { - ++m_ctx.m_nonRepLhs; - return false; - } - if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { - splicep->template as()->addDriver(flp, idxp->toUInt(), vtxp); - return true; - } - } else if (VN_IS(nodep, VarRef)) { - if (DfgVertexSplice* const splicep = convertLValue(nodep)) { - splicep->template as()->addDriver(flp, 0, vtxp); - return true; - } + // Construct LHS + const auto pair = convertLValue(lhsp); + if (!pair.first) return false; + + // If successful connect the driver + if (DfgSplicePacked* const sPackedp = pair.first->template cast()) { + sPackedp->addDriver(flp, pair.second, vtxp); + } else if (DfgSpliceArray* const sArrayp = pair.first->template cast()) { + sArrayp->addDriver(flp, pair.second, vtxp); } else { - ++m_ctx.m_nonRepLhs; + lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE } - return false; + + return true; } bool convertEquation(AstNode* nodep, FileLine* flp, AstNode* lhsp, AstNode* rhsp) { UASSERT_OBJ(m_uncommittedVertices.empty(), nodep, "Should not nest"); - // Currently cannot handle direct assignments between unpacked types. These arise e.g. - // when passing an unpacked array through a module port. - if (!DfgVertex::isSupportedPackedDType(lhsp->dtypep()) - || !DfgVertex::isSupportedPackedDType(rhsp->dtypep())) { + // Check data types are compatible. + if (!DfgVertex::isSupportedDType(lhsp->dtypep()) + || !DfgVertex::isSupportedDType(rhsp->dtypep())) { + markReferenced(nodep); + ++m_ctx.m_nonRepDType; + return false; + } + + // For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c) + if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) { markReferenced(nodep); ++m_ctx.m_nonRepDType; return false; @@ -312,180 +366,310 @@ class AstToDfgVisitor final : public VNVisitor { return true; } - // Sometime assignment ranges are coalesced by V3Const, - // so we unpack concatenations for better error reporting. - void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp, - std::vector& drivers) const { - if (DfgConcat* const concatp = vtxp->cast()) { - DfgVertex* const rhsp = concatp->rhsp(); - auto const rhs_width = rhsp->width(); - addDriver(rhsp->fileline(), lsb, rhsp, drivers); - DfgVertex* const lhsp = concatp->lhsp(); - addDriver(lhsp->fileline(), lsb + rhs_width, lhsp, drivers); - concatp->unlinkDelete(*m_dfgp); - } else { - drivers.emplace_back(flp, lsb, vtxp); + // Prune vertices potentially unused due to resolving multiple drivers. + // Having multiple drivers is an error and is hence assumed to be rare, + // so performance is not very important, set will suffice. + void removeUnused(std::set& prune) { + while (!prune.empty()) { + // Pop last vertex + const auto it = prune.begin(); + DfgVertex* const vtxp = *it; + prune.erase(it); + // If used (or a variable), then done + if (vtxp->hasSinks() || vtxp->is()) continue; + // If unused, then add sources to work list and delete + vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); }); + vtxp->unlinkDelete(*m_dfgp); } } - // Canonicalize packed variables - void canonicalizePacked() { - for (DfgVarPacked* const varp : m_varPackedps) { - // Delete variables with no sinks nor sources (this can happen due to reverting - // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && !varp->srcp()) { - VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); + // Normalize packed driver - return the normalized vertex and location for 'splicep' + std::pair // + normalizePacked(DfgVertexVar* varp, const std::string& sub, DfgSplicePacked* const splicep) { + // Represents a driver of 'splicep' + struct Driver final { + FileLine* m_fileline; + DfgVertex* m_vtxp; + uint32_t m_lsb; + Driver() = delete; + Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) + : m_fileline{flp} + , m_vtxp{vtxp} + , m_lsb{lsb} {} + }; + + // The drivers of 'splicep' + std::vector drivers; + drivers.reserve(splicep->arity()); + + // Sometime assignment ranges are coalesced by V3Const, + // so we unpack concatenations for better error reporting. + const std::function gather + = [&](FileLine* flp, uint32_t lsb, DfgVertex* vtxp) -> void { + if (DfgConcat* const concatp = vtxp->cast()) { + DfgVertex* const rhsp = concatp->rhsp(); + auto const rhs_width = rhsp->width(); + gather(rhsp->fileline(), lsb, rhsp); + DfgVertex* const lhsp = concatp->lhsp(); + gather(lhsp->fileline(), lsb + rhs_width, lhsp); + concatp->unlinkDelete(*m_dfgp); + } else { + drivers.emplace_back(flp, lsb, vtxp); + } + }; + + // Gather and unlink all drivers + splicep->forEachSourceEdge([&](DfgEdge& edge, size_t idx) { + DfgVertex* const driverp = edge.sourcep(); + UASSERT(driverp, "Should not have created undriven sources"); + UASSERT_OBJ(!driverp->is(), splicep, "Should not be DfgVertexSplice"); + gather(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp); + edge.unlinkSource(); + }); + + const auto cmp = [](const Driver& a, const Driver& b) { + if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb; + return a.m_fileline->operatorCompare(*b.m_fileline) < 0; + }; + + // Sort drivers by LSB + std::stable_sort(drivers.begin(), drivers.end(), cmp); + + // Vertices that might have become unused due to multiple driver resolution. Having + // multiple drivers is an error and is hence assumed to be rare, so performance is + // not very important, set will suffice. + std::set prune; + + // Fix multiply driven ranges + for (auto it = drivers.begin(); it != drivers.end();) { + Driver& a = *it++; + const uint32_t aWidth = a.m_vtxp->width(); + const uint32_t aEnd = a.m_lsb + aWidth; + while (it != drivers.end()) { + Driver& b = *it; + // If no overlap, then nothing to do + if (b.m_lsb >= aEnd) break; + + const uint32_t bWidth = b.m_vtxp->width(); + const uint32_t bEnd = b.m_lsb + bWidth; + const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; + + if (a.m_fileline->operatorCompare(*b.m_fileline) != 0 + && !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress + ) { + AstNode* const vp = varp->varScopep() + ? static_cast(varp->varScopep()) + : static_cast(varp->varp()); + + vp->v3warn( // + MULTIDRIVEN, + "Bits [" // + << overlapEnd << ":" << b.m_lsb << "] of signal '" << vp->prettyName() + << sub << "' have multiple combinational drivers\n" + << a.m_fileline->warnOther() << "... Location of first driver\n" + << a.m_fileline->warnContextPrimary() << '\n' + << b.m_fileline->warnOther() << "... Location of other driver\n" + << b.m_fileline->warnContextSecondary() << vp->warnOther() + << "... Only the first driver will be respected"); + } + + // If the first driver completely covers the range of the second driver, + // we can just delete the second driver completely, otherwise adjust the + // second driver to apply from the end of the range of the first driver. + if (aEnd >= bEnd) { + prune.emplace(b.m_vtxp); + it = drivers.erase(it); + } else { + const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd); + DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep}; + selp->fromp(b.m_vtxp); + selp->lsb(aEnd - b.m_lsb); + b.m_lsb = aEnd; + b.m_vtxp = selp; + std::stable_sort(it, drivers.end(), cmp); + } + } + } + + // Coalesce adjacent ranges + for (size_t i = 0, j = 1; j < drivers.size(); ++j) { + Driver& a = drivers[i]; + Driver& b = drivers[j]; + + // Coalesce adjacent range + const uint32_t aWidth = a.m_vtxp->width(); + const uint32_t bWidth = b.m_vtxp->width(); + if (a.m_lsb + aWidth == b.m_lsb) { + const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth); + DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep}; + concatp->rhsp(a.m_vtxp); + concatp->lhsp(b.m_vtxp); + a.m_vtxp = concatp; + b.m_vtxp = nullptr; // Mark as moved + ++m_ctx.m_coalescedAssignments; continue; } - // Nothing to do for un-driven (input) variables - if (!varp->srcp()) continue; + ++i; - DfgSplicePacked* const splicep = varp->srcp()->as(); - - // Gather (and unlink) all drivers - std::vector drivers; - drivers.reserve(splicep->arity()); - splicep->forEachSourceEdge([this, splicep, &drivers](DfgEdge& edge, size_t idx) { - DfgVertex* const driverp = edge.sourcep(); - UASSERT(driverp, "Should not have created undriven sources"); - addDriver(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp, drivers); - edge.unlinkSource(); - }); - - const auto cmp = [](const Driver& a, const Driver& b) { - if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb; - return a.m_fileline->operatorCompare(*b.m_fileline) < 0; - }; - - // Sort drivers by LSB - std::stable_sort(drivers.begin(), drivers.end(), cmp); - - // Vertices that might have become unused due to multiple driver resolution. Having - // multiple drivers is an error and is hence assumed to be rare, so performance is - // not very important, set will suffice. - std::set prune; - - // Fix multiply driven ranges - for (auto it = drivers.begin(); it != drivers.end();) { - Driver& a = *it++; - const uint32_t aWidth = a.m_vtxp->width(); - const uint32_t aEnd = a.m_lsb + aWidth; - while (it != drivers.end()) { - Driver& b = *it; - // If no overlap, then nothing to do - if (b.m_lsb >= aEnd) break; - - const uint32_t bWidth = b.m_vtxp->width(); - const uint32_t bEnd = b.m_lsb + bWidth; - const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - - if (a.m_fileline->operatorCompare(*b.m_fileline) != 0 - && !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress - ) { - AstNode* const vp = varp->varScopep() - ? static_cast(varp->varScopep()) - : static_cast(varp->varp()); - vp->v3warn( // - MULTIDRIVEN, - "Bits [" // - << overlapEnd << ":" << b.m_lsb << "] of signal " - << vp->prettyNameQ() << " have multiple combinational drivers\n" - << a.m_fileline->warnOther() << "... Location of first driver\n" - << a.m_fileline->warnContextPrimary() << '\n' - << b.m_fileline->warnOther() << "... Location of other driver\n" - << b.m_fileline->warnContextSecondary() << vp->warnOther() - << "... Only the first driver will be respected"); - } - - // If the first driver completely covers the range of the second driver, - // we can just delete the second driver completely, otherwise adjust the - // second driver to apply from the end of the range of the first driver. - if (aEnd >= bEnd) { - prune.emplace(b.m_vtxp); - it = drivers.erase(it); - } else { - const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd); - DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep}; - selp->fromp(b.m_vtxp); - selp->lsb(aEnd - b.m_lsb); - b.m_lsb = aEnd; - b.m_vtxp = selp; - std::stable_sort(it, drivers.end(), cmp); - } - } - } - - // Coalesce adjacent ranges - for (size_t i = 0, j = 1; j < drivers.size(); ++j) { - Driver& a = drivers[i]; - Driver& b = drivers[j]; - - // Coalesce adjacent range - const uint32_t aWidth = a.m_vtxp->width(); - const uint32_t bWidth = b.m_vtxp->width(); - if (a.m_lsb + aWidth == b.m_lsb) { - const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth); - DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep}; - concatp->rhsp(a.m_vtxp); - concatp->lhsp(b.m_vtxp); - a.m_vtxp = concatp; - b.m_vtxp = nullptr; // Mark as moved - ++m_ctx.m_coalescedAssignments; - continue; - } - - ++i; - - // Compact non-adjacent ranges within the vector - if (j != i) { - Driver& c = drivers[i]; - UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved"); - c = b; - b.m_vtxp = nullptr; // Mark as moved - } - } - - // Reinsert drivers in order - splicep->resetSources(); - for (const Driver& driver : drivers) { - if (!driver.m_vtxp) break; // Stop at end of compacted list - splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); - } - - // Prune vertices potentially unused due to resolving multiple drivers. - while (!prune.empty()) { - // Pop last vertex - const auto it = prune.begin(); - DfgVertex* const vtxp = *it; - prune.erase(it); - // If used (or a variable), then done - if (vtxp->hasSinks() || vtxp->is()) continue; - // If unused, then add sources to work list and delete - vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); }); - vtxp->unlinkDelete(*m_dfgp); - } - - // If the whole variable is driven, remove the splice node - if (splicep->arity() == 1 // - && splicep->driverLsb(0) == 0 // - && splicep->source(0)->width() == varp->width()) { - varp->srcp(splicep->source(0)); - varp->driverFileLine(splicep->driverFileLine(0)); - splicep->unlinkDelete(*m_dfgp); + // Compact non-adjacent ranges within the vector + if (j != i) { + Driver& c = drivers[i]; + UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved"); + c = b; + b.m_vtxp = nullptr; // Mark as moved } } + + // Reinsert drivers in order + splicep->resetSources(); + for (const Driver& driver : drivers) { + if (!driver.m_vtxp) break; // Stop at end of compacted list + splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); + } + + removeUnused(prune); + + // If the whole variable is driven whole, we can just use that driver + if (splicep->arity() == 1 // + && splicep->driverLsb(0) == 0 // + && splicep->source(0)->width() == splicep->width()) { + const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0)); + VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep); + return result; + } + return {splicep, splicep->fileline()}; } - // Canonicalize array variables - void canonicalizeArray() { - for (DfgVarArray* const varp : m_varArrayps) { - // Delete variables with no sinks nor sources (this can happen due to reverting - // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && !varp->srcp()) { - VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); + // Normalize array driver - return the normalized vertex and location for 'splicep' + std::pair // + normalizeArray(DfgVertexVar* varp, const std::string& sub, DfgSpliceArray* const splicep) { + // Represents a driver of 'splicep' + struct Driver final { + FileLine* m_fileline; + DfgVertex* m_vtxp; + uint32_t m_idx; + Driver() = delete; + Driver(FileLine* flp, uint32_t idx, DfgVertex* vtxp) + : m_fileline{flp} + , m_vtxp{vtxp} + , m_idx{idx} {} + }; + + // The drivers of 'splicep' + std::vector drivers; + drivers.reserve(splicep->arity()); + + // Normalize, gather, and unlink all drivers + splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { + DfgVertex* const driverp = edge.sourcep(); + UASSERT(driverp, "Should not have created undriven sources"); + const uint32_t idx = splicep->driverIndex(i); + if (DfgSplicePacked* const spp = driverp->cast()) { + const auto pair + = normalizePacked(varp, sub + "[" + std::to_string(idx) + "]", spp); + drivers.emplace_back(pair.second, idx, pair.first); + } else if (DfgSpliceArray* const sap = driverp->cast()) { + const auto pair = normalizeArray(varp, sub + "[" + std::to_string(idx) + "]", sap); + drivers.emplace_back(pair.second, idx, pair.first); + } else if (driverp->is()) { + driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); + } else { + drivers.emplace_back(splicep->driverFileLine(i), idx, driverp); + } + edge.unlinkSource(); + }); + + const auto cmp = [](const Driver& a, const Driver& b) { + if (a.m_idx != b.m_idx) return a.m_idx < b.m_idx; + return a.m_fileline->operatorCompare(*b.m_fileline) < 0; + }; + + // Sort drivers by index + std::stable_sort(drivers.begin(), drivers.end(), cmp); + + // Vertices that become unused due to multiple driver resolution + std::set prune; + + // Fix multiply driven ranges + for (auto it = drivers.begin(); it != drivers.end();) { + Driver& a = *it++; + AstUnpackArrayDType* aArrayDTypep = VN_CAST(a.m_vtxp->dtypep(), UnpackArrayDType); + const uint32_t aElements = aArrayDTypep ? aArrayDTypep->elementsConst() : 1; + const uint32_t aEnd = a.m_idx + aElements; + while (it != drivers.end()) { + Driver& b = *it; + // If no overlap, then nothing to do + if (b.m_idx >= aEnd) break; + + AstUnpackArrayDType* bArrayDTypep = VN_CAST(b.m_vtxp->dtypep(), UnpackArrayDType); + const uint32_t bElements = bArrayDTypep ? bArrayDTypep->elementsConst() : 1; + const uint32_t bEnd = b.m_idx + bElements; + const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; + + if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) { + AstNode* const vp = varp->varScopep() + ? static_cast(varp->varScopep()) + : static_cast(varp->varp()); + + vp->v3warn( // + MULTIDRIVEN, + "Elements [" // + << overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName() + << sub << "' have multiple combinational drivers\n" + << a.m_fileline->warnOther() << "... Location of first driver\n" + << a.m_fileline->warnContextPrimary() << '\n' + << b.m_fileline->warnOther() << "... Location of other driver\n" + << b.m_fileline->warnContextSecondary() << vp->warnOther() + << "... Only the first driver will be respected"); + } + + // If the first driver completely covers the range of the second driver, + // we can just delete the second driver completely, otherwise adjust the + // second driver to apply from the end of the range of the first driver. + if (aEnd >= bEnd) { + prune.emplace(b.m_vtxp); + it = drivers.erase(it); + } else { + const auto distance = std::distance(drivers.begin(), it); + DfgVertex* const bVtxp = b.m_vtxp; + FileLine* const flp = b.m_vtxp->fileline(); + AstNodeDType* const elemDtypep = DfgVertex::dtypeFor( + VN_AS(splicep->dtypep(), UnpackArrayDType)->subDTypep()); + // Remove this driver + it = drivers.erase(it); + // Add missing items element-wise + for (uint32_t i = aEnd; i < bEnd; ++i) { + DfgArraySel* const aselp = new DfgArraySel{*m_dfgp, flp, elemDtypep}; + aselp->fromp(bVtxp); + aselp->bitp(new DfgConst{*m_dfgp, flp, 32, i}); + drivers.emplace_back(flp, i, aselp); + } + it = drivers.begin(); + std::advance(it, distance); + std::stable_sort(it, drivers.end(), cmp); + } } } + + // Reinsert drivers in order + splicep->resetSources(); + for (const Driver& driver : drivers) { + if (!driver.m_vtxp) break; // Stop at end of compacted list + splicep->addDriver(driver.m_fileline, driver.m_idx, driver.m_vtxp); + } + + removeUnused(prune); + + // If the whole variable is driven whole, we can just use that driver + if (splicep->arity() == 1 // + && splicep->driverIndex(0) == 0 // + && splicep->source(0)->dtypep()->isSame(splicep->dtypep())) { + const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0)); + VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep); + return result; + } + return {splicep, splicep->fileline()}; } // VISITORS @@ -707,9 +891,32 @@ class AstToDfgVisitor final : public VNVisitor { iterate(&root); UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain"); - // Canonicalize variables - canonicalizePacked(); - canonicalizeArray(); + if (dumpDfgLevel() >= 8) m_dfgp->dumpDotFilePrefixed(ctx.prefix() + "ast2dfg"); + + // Normalize variable drivers (remove multiple drivers, remove unnecessary splice vertices) + for (DfgVertexVar* const varp : m_dfgp->varVertices().unlinkable()) { + // Delete variables with no sinks nor sources (this can happen due to reverting + // uncommitted vertices, which does not remove variables) + if (!varp->hasSinks() && !varp->srcp()) { + VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); + continue; + } + + // Nothing to do for un-driven (input) variables + if (!varp->srcp()) continue; + + // The driver of a variable must always be a splice vertex, normalize it + std::pair normalizedDriver; + if (DfgSpliceArray* const sArrayp = varp->srcp()->cast()) { + normalizedDriver = normalizeArray(varp, "", sArrayp); + } else if (DfgSplicePacked* const sPackedp = varp->srcp()->cast()) { + normalizedDriver = normalizePacked(varp, "", sPackedp); + } else { + varp->v3fatalSrc("Unhandled DfgSplicePacked sub-type"); // LCOV_EXCL_LINE + } + varp->srcp(normalizedDriver.first); + varp->driverFileLine(normalizedDriver.second); + } } public: diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 7c7ac3d88..520f42622 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -188,65 +188,57 @@ class DfgToAstVisitor final : DfgVisitor { return resultp; } - void addResultEquation(const DfgVertexVar* vtxp, FileLine* flp, AstNodeExpr* lhsp, - AstNodeExpr* rhsp) { - AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp}; - if VL_CONSTEXPR_CXX17 (T_Scoped) { - // Add it to the scope holding the target variable - getCombActive(vtxp->varScopep()->scopep())->addStmtsp(assignp); - } else { - // Add it to the parend module of the DfgGraph - m_modp->addStmtsp(assignp); + void convertDriver(AstScope* scopep, FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) { + if (!driverp->is()) { + // Base case: assign vertex to current lhs + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp); + AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp}; + lhsp->foreach([flp](AstNode* nodep) { nodep->fileline(flp); }); + if VL_CONSTEXPR_CXX17 (T_Scoped) { + // Add it to the scope holding the target variable + getCombActive(scopep)->addStmtsp(assignp); + } else { + // Add it to the parend module of the DfgGraph + m_modp->addStmtsp(assignp); + } + ++m_ctx.m_resultEquations; + return; } - ++m_ctx.m_resultEquations; - } - void convertPackedDriver(const DfgVarPacked* dfgVarp) { - if (DfgSplicePacked* const splicep = dfgVarp->srcp()->cast()) { - // Variable is driven partially. Render each driver as a separate assignment. - splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { - UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); - // Render the rhs expression - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); - // Create select LValue - FileLine* const flp = splicep->driverFileLine(idx); - AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const lsbp = new AstConst{flp, splicep->driverLsb(idx)}; + if (DfgSplicePacked* const sPackedp = driverp->cast()) { + // Partial assignment of packed value + sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + UASSERT_OBJ(edge.sourcep(), sPackedp, "Should have removed undriven sources"); + // Create Sel + FileLine* const dflp = sPackedp->driverFileLine(i); + AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLsb(i)}; const int width = static_cast(edge.sourcep()->width()); - AstSel* const lhsp = new AstSel{flp, refp, lsbp, width}; - // Add assignment of the value to the selected bits - addResultEquation(dfgVarp, flp, lhsp, rhsp); + AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width}; + // Convert source + convertDriver(scopep, dflp, nLhsp, edge.sourcep()); + // Delete Sel if not consumed + if (!nLhsp->backp()) VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); }); return; } - // Whole variable is driven. Render driver and assign directly to whole variable. - FileLine* const flp - = dfgVarp->driverFileLine() ? dfgVarp->driverFileLine() : dfgVarp->fileline(); - AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->srcp()); - addResultEquation(dfgVarp, flp, lhsp, rhsp); - } - - void convertArrayDiver(const DfgVarArray* dfgVarp) { - if (DfgSpliceArray* const splicep = dfgVarp->srcp()->cast()) { - // Variable is driven partially. Assign from parts of the canonical var. - splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { - UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); - // Render the rhs expression - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); - // Create select LValue - FileLine* const flp = splicep->driverFileLine(idx); - AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const idxp = new AstConst{flp, splicep->driverIndex(idx)}; - AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp}; - // Add assignment of the value to the selected bits - addResultEquation(dfgVarp, flp, lhsp, rhsp); + if (DfgSpliceArray* const sArrayp = driverp->cast()) { + // Partial assignment of array variable + sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + UASSERT_OBJ(edge.sourcep(), sArrayp, "Should have removed undriven sources"); + // Create ArraySel + FileLine* const dflp = sArrayp->driverFileLine(i); + AstConst* const idxp = new AstConst{dflp, sArrayp->driverIndex(i)}; + AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp}; + // Convert source + convertDriver(scopep, dflp, nLhsp, edge.sourcep()); + // Delete ArraySel if not consumed + if (!nLhsp->backp()) VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); }); return; } - UASSERT_OBJ(false, dfgVarp, "Should not have wholly driven arrays in Dfg"); + driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE } // VISITORS @@ -294,14 +286,13 @@ class DfgToAstVisitor final : DfgVisitor { // If there is no driver (this vertex is an input to the graph), then nothing to do. if (!vtx.srcp()) continue; - // Render packed variable assignments - if (const DfgVarPacked* const dfgVarp = vtx.cast()) { - convertPackedDriver(dfgVarp); - continue; - } - - // Render array variable assignments - convertArrayDiver(vtx.as()); + // Render variable assignments + FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline(); + AstScope* const scopep = T_Scoped ? vtx.varScopep()->scopep() : nullptr; + AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE}; + convertDriver(scopep, flp, lhsp, vtx.srcp()); + // convetDriver clones and might not use up the original lhsp + if (!lhsp->backp()) VL_DO_DANGLING(lhsp->deleteTree(), lhsp); } } diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 0e4f27967..b91e34071 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1202,13 +1202,14 @@ class V3DfgPeephole final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { if (DfgConst* const idxp = vtxp->bitp()->cast()) { if (DfgVarArray* const varp = vtxp->fromp()->cast()) { - if (varp->srcp()) { + if (varp->srcp() && !varp->varp()->isForced() && !varp->varp()->isSc()) { if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { - const size_t idx = idxp->toSizeT(); - if (DfgVertex* const driverp = splicep->driverAt(idx)) { - APPLYING(INLINE_ARRAYSEL) { - replace(vtxp, driverp); - return; + if (DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT())) { + if (!driverp->is()) { + APPLYING(INLINE_ARRAYSEL) { + replace(vtxp, driverp); + return; + } } } } diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 0bfa62a08..d8de75898 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -50,7 +50,7 @@ class DfgRegularize final { if (vtx.is()) { const bool hasNonVarSink = vtx.findSink([](const DfgVertex& snk) { // - return !snk.is(); + return !snk.is() && !snk.is(); }); return hasNonVarSink; } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 50dd9a070..11a04e5a1 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -193,10 +193,6 @@ class DfgVarArray final : public DfgVertexVar { friend class DfgVertex; friend class DfgVisitor; - using DriverData = std::pair; - - std::vector m_driverData; // Additional data associate with each driver - public: DfgVarArray(DfgGraph& dfg, AstVar* varp) : DfgVertexVar{dfg, dfgType(), varp} { @@ -229,7 +225,14 @@ class DfgSpliceArray final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; - using DriverData = std::pair; + struct DriverData final { + FileLine* m_flp; // Location of this driver + uint32_t m_index; // Array index driven by this driver (or low index of range) + DriverData() = delete; + DriverData(FileLine* flp, uint32_t index) + : m_flp{flp} + , m_index{index} {} + }; std::vector m_driverData; // Additional data associated with each driver @@ -253,8 +256,8 @@ public: DfgVertexVariadic::resetSources(); } - FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } - uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; } + FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; } + uint32_t driverIndex(size_t i) const { return m_driverData.at(i).m_index; } DfgVertex* driverAt(size_t idx) const { const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge&, size_t i) { // @@ -271,8 +274,14 @@ class DfgSplicePacked final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; - using DriverData = std::pair; - + struct DriverData final { + FileLine* m_flp; // Location of this driver + uint32_t m_lsb; // LSB of range driven by this driver + DriverData() = delete; + DriverData(FileLine* flp, uint32_t lsb) + : m_flp{flp} + , m_lsb{lsb} {} + }; std::vector m_driverData; // Additional data associated with each driver bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; @@ -295,8 +304,8 @@ public: DfgVertexVariadic::resetSources(); } - FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } - uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; } + FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; } + uint32_t driverLsb(size_t i) const { return m_driverData.at(i).m_lsb; } const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); } }; diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index 19b96d307..bd94a1b10 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -112,6 +112,9 @@ if coveredLines != expectedLines: for n in sorted(coveredLines - expectedLines): test.error_keep_going(f"V3DfgBreakCycles.cpp line {n} covered but not expected") +test.file_grep_not(test.obj_dir + "/obj_opt/Vopt__stats.txt", + r'DFG.*non-representable.*\s[1-9]\d*$') + # Execute test to check equivalence test.execute(executable=test.obj_dir + "/obj_opt/Vopt") diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.out b/test_regress/t/t_dfg_multidriver_dfg_bad.out index 62fea591c..9020d13ac 100644 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.out +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.out @@ -1,39 +1,75 @@ -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [3:1] of signal 'a' have multiple combinational drivers - : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:14:19: ... Location of first driver - 14 | assign a[3:0] = i[3:0]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:15:19: ... Location of other driver - 15 | assign a[4:1] = ~i[4:1]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected - ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest - ... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message. -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [3:3] of signal 'a' have multiple combinational drivers - : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:14:19: ... Location of first driver - 14 | assign a[3:0] = i[3:0]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:16:17: ... Location of other driver - 16 | assign a[3] = ~i[3]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [7:6] of signal 'a' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:1] of signal 'a' have multiple combinational drivers : ... note: In instance 't' t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver - 17 | assign a[8:5] = i[8:5]; + 17 | assign a[3:0] = i[3:0]; | ^ t/t_dfg_multidriver_dfg_bad.v:18:19: ... Location of other driver - 18 | assign a[7:6] = ~i[7:6]; + 18 | assign a[4:1] = ~i[4:1]; | ^ - t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [9:9] of signal 'a' have multiple combinational drivers + t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected + ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest + ... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message. +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:3] of signal 'a' have multiple combinational drivers : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of first driver - 19 | assign a[9] = i[9]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of other driver - 20 | assign a[9] = ~i[9]; + t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver + 17 | assign a[3:0] = i[3:0]; | ^ - t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected + t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of other driver + 19 | assign a[3] = ~i[3]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [7:6] of signal 'a' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of first driver + 20 | assign a[8:5] = i[8:5]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:21:19: ... Location of other driver + 21 | assign a[7:6] = ~i[7:6]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [9:9] of signal 'a' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:22:17: ... Location of first driver + 22 | assign a[9] = i[9]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:23:19: ... Location of other driver + 23 | assign a[9] = ~i[9]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:18: Elements [3:0] of signal 'u' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:27:14: ... Location of first driver + 27 | assign u = j; + | ^ + t/t_dfg_multidriver_dfg_bad.v:28:14: ... Location of other driver + 28 | assign u = k; + | ^ + t/t_dfg_multidriver_dfg_bad.v:26:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:18: Elements [1:1] of signal 'v' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:31:14: ... Location of first driver + 31 | assign v = j; + | ^ + t/t_dfg_multidriver_dfg_bad.v:32:17: ... Location of other driver + 32 | assign v[1] = i; + | ^ + t/t_dfg_multidriver_dfg_bad.v:30:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:18: Elements [0:0] of signal 'w' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:35:17: ... Location of first driver + 35 | assign w[0] = i; + | ^ + t/t_dfg_multidriver_dfg_bad.v:36:14: ... Location of other driver + 36 | assign w = j; + | ^ + t/t_dfg_multidriver_dfg_bad.v:34:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:18: Bits [3:2] of signal 'x[3]' have multiple combinational drivers + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:39:17: ... Location of first driver + 39 | assign x[3] = i; + | ^ + t/t_dfg_multidriver_dfg_bad.v:40:22: ... Location of other driver + 40 | assign x[3][3:2] = ~i[1:0]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:38:18: ... Only the first driver will be respected %Error: Exiting due to diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.v b/test_regress/t/t_dfg_multidriver_dfg_bad.v index 724b33539..84659c923 100644 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.v +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.v @@ -7,7 +7,10 @@ `default_nettype none module t( - input wire [10:0] i, + input wire [10:0] i, + input wire [10:0] j [4], + input wire [10:0] k [4], + output wire [10:0] o ); logic [10:0] a; @@ -19,5 +22,26 @@ module t( assign a[9] = i[9]; assign a[9] = ~i[9]; assign a[10] = i[10]; - assign o = a; + + logic [10:0] u [4]; + assign u = j; + assign u = k; + + logic [10:0] v [4]; + assign v = j; + assign v[1] = i; + + logic [10:0] w [4]; + assign w[0] = i; + assign w = j; + + logic [10:0] x [4]; + assign x[3] = i; + assign x[3][3:2] = ~i[1:0]; + // No warning for w[2]! + assign x[2][3:2] = ~i[1:0]; + assign x[2][1:0] = ~i[1:0]; + + assign o = a ^ u[3] ^ v[3] ^ w[3] ^ x[3]; + endmodule diff --git a/test_regress/t/t_dfg_peephole.cpp b/test_regress/t/t_dfg_peephole.cpp index 0664397e1..deb7da096 100644 --- a/test_regress/t/t_dfg_peephole.cpp +++ b/test_regress/t/t_dfg_peephole.cpp @@ -29,8 +29,10 @@ int main(int, char**) { uint64_t rand_a = 0x5aef0c8dd70a4497; uint64_t rand_b = 0xf0c0a8dd75ae4497; - uint64_t srand_a = 0x00fa8dcc7ae4957; - uint64_t srand_b = 0x0fa8dc7ae3c9574; + uint64_t srand_a = 0x000fa8dcc7ae4957; + uint64_t srand_b = 0x00fa8dc7ae3c9574; + uint64_t arand_a = 0x758c168d16c93a0f; + uint64_t arand_b = 0xbe01de017d87355d; for (size_t n = 0; n < 200000; ++n) { // Update rngs @@ -38,12 +40,16 @@ int main(int, char**) { rngUpdate(rand_b); rngUpdate(srand_a); rngUpdate(srand_b); + rngUpdate(arand_a); + rngUpdate(arand_b); // Assign inputs ref.rand_a = opt.rand_a = rand_a; ref.rand_b = opt.rand_b = rand_b; ref.srand_a = opt.srand_a = srand_a; ref.srand_b = opt.srand_b = srand_b; + ref.arand_a = opt.arand_a = arand_a; + ref.arand_b = opt.arand_b = arand_b; // Evaluate both models ref.eval(); diff --git a/test_regress/t/t_dfg_peephole.py b/test_regress/t/t_dfg_peephole.py index 0466a34e1..034ba591c 100755 --- a/test_regress/t/t_dfg_peephole.py +++ b/test_regress/t/t_dfg_peephole.py @@ -88,9 +88,6 @@ test.compile(verilator_flags2=[ "../../t/" + test.name + ".cpp" ]) # yapf:disable -# Execute test to check equivalence -test.execute(executable=test.obj_dir + "/obj_opt/Vopt") - def check(name): name = name.lower() @@ -103,4 +100,10 @@ def check(name): for opt in optimizations: check(opt) +test.file_grep_not(test.obj_dir + "/obj_opt/Vopt__stats.txt", + r'DFG.*non-representable.*\s[1-9]\d*$') + +# Execute test to check equivalence +test.execute(executable=test.obj_dir + "/obj_opt/Vopt") + test.passes() diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index fb4644387..cd2ed056a 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -8,7 +8,7 @@ module t ( `include "portlist.vh" // Boilerplate generated by t_dfg_peephole.py - rand_a, rand_b, srand_a, srand_b + rand_a, rand_b, srand_a, srand_b, arand_a, arand_b ); `include "portdecl.vh" // Boilerplate generated by t_dfg_peephole.py @@ -17,10 +17,16 @@ module t ( input rand_b; input srand_a; input srand_b; + input arand_a; + input arand_b; wire logic [63:0] rand_a; wire logic [63:0] rand_b; wire logic signed [63:0] srand_a; wire logic signed [63:0] srand_b; + // verilator lint_off ASCRANGE + wire logic [0:63] arand_a; + wire logic [0:63] arand_b; + // verilator lint_on ASCRANGE wire logic randbit_a = rand_a[0]; wire logic [127:0] rand_ba = {rand_b, rand_a}; @@ -29,9 +35,13 @@ module t ( wire logic [63:0] const_b; wire logic signed [63:0] sconst_a; wire logic signed [63:0] sconst_b; - wire logic [63:0] array [3:0]; + logic [63:0] array [3:0]; assign array[0] = (rand_a << 32) | (rand_a >> 32); assign array[1] = (rand_a << 16) | (rand_a >> 48); + assign array[2][3:0] = rand_a[3:0]; + always @(rand_b) begin // Intentional non-combinational partial driver + array[2][7:4] = rand_a[7:4]; + end `signal(FOLD_UNARY_CLog2, $clog2(const_a)); `signal(FOLD_UNARY_CountOnes, $countones(const_a)); @@ -184,6 +194,7 @@ module t ( `signal(REPLACE_COND_WITH_ELSE_BRANCH_ZERO, rand_a[0] ? rand_a[1] : 1'd0); `signal(REPLACE_COND_WITH_ELSE_BRANCH_ONES, rand_a[0] ? rand_a[1] : 1'd1); `signal(INLINE_ARRAYSEL, array[0]); + `signal(NO_INLINE_ARRAYSEL_PARTIAL, array[2]); `signal(PUSH_BITWISE_THROUGH_REDUCTION_AND, (&(rand_a + 64'd105)) & (&(rand_b + 64'd108))); `signal(PUSH_BITWISE_THROUGH_REDUCTION_OR, (|(rand_a + 64'd106)) | (|(rand_b + 64'd109))); `signal(PUSH_BITWISE_THROUGH_REDUCTION_XOR, (^(rand_a + 64'd107)) ^ (^(rand_b + 64'd110))); @@ -223,6 +234,15 @@ module t ( `signal(PUSH_SEL_THROUGH_SHIFTL, sel_from_shiftl[20:0]); `signal(REPLACE_SEL_FROM_SEL, sel_from_sel[4:3]); + // Asscending ranges + `signal(ASCENDNG_SEL, arand_a[0:4]); + // verilator lint_off ASCRANGE + wire [0:7] ascending_assign; + // verilator lint_on ASCRANGE + assign ascending_assign[0:3] = arand_a[4:7]; + assign ascending_assign[4:7] = arand_b[0:3]; + `signal(ASCENDING_ASSIGN, ascending_assign); + // Sel from not requires the operand to have a sinle sink, so can't use // the chekc due to the raw expression referencing the operand wire [63:0] sel_from_not_tmp = ~(rand_a >> rand_b[2:0] << rand_a[3:0]); diff --git a/test_regress/t/t_inst_array_partial.v b/test_regress/t/t_inst_array_partial.v index 35f2d2252..2470c714c 100644 --- a/test_regress/t/t_inst_array_partial.v +++ b/test_regress/t/t_inst_array_partial.v @@ -10,7 +10,9 @@ module t (/*AUTOARG*/ ); input clk; + // verilator lint_off MULTIDRIVEN wire [19:10] bitout; + // verilator lint_on MULTIDRIVEN wire [29:24] short_bitout; wire [7:0] allbits; wire [15:0] twobits; From 7646e7d89c072e997e9760b701cb0b476249e542 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 21 Jul 2025 18:32:08 +0100 Subject: [PATCH 053/252] Exclude SystemC variables from DFG (#6208) SystemC variables are fairly special (they can only be assigned to/from, but not otherwise participate in expressions), which complicates some DFG code. These variables only ever appear as port on the top level wrapper, so excluding them from DFG does not make us loose any optimizations, but simplifies internals. --- src/V3Dfg.cpp | 3 --- src/V3Dfg.h | 2 ++ src/V3DfgAstToDfg.cpp | 6 ++++-- src/V3DfgPasses.cpp | 16 ++-------------- src/V3DfgPeephole.cpp | 2 +- src/V3DfgRegularize.cpp | 8 -------- 6 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 04679edd8..9d498a02b 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -639,9 +639,6 @@ DfgVertexVar* DfgVertex::getResultVar() { this->forEachSink([&resp](DfgVertex& sink) { DfgVertexVar* const varp = sink.cast(); if (!varp) return; - // Ignore SystemC variables, they cannot participate in expressions or - // be assigned rvalue expressions. - if (varp->varp()->isSc()) return; // First variable found if (!resp) { resp = varp; diff --git a/src/V3Dfg.h b/src/V3Dfg.h index be198cf47..a725e1a60 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -918,12 +918,14 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) , m_varp{varp} , m_varScopep{nullptr} { UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph"); + UASSERT_OBJ(!m_varp->isSc(), varp, "SystemC variable is not representable by DfgVertexVar"); } DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) : DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)} , m_varp{vscp->varp()} , m_varScopep{vscp} { UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph"); + UASSERT_OBJ(!m_varp->isSc(), vscp, "SystemC variable is not representable by DfgVertexVar"); } //------------------------------------------------------------------------------ diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 8691b1a53..f5f4f03ed 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -125,8 +125,9 @@ class AstToDfgVisitor final : public VNVisitor { void markReferenced(AstNode* nodep) { nodep->foreach([this](const AstVarRef* refp) { - // No need to (and in fact cannot) mark variables with unsupported dtypes - if (!DfgVertex::isSupportedDType(refp->varp()->dtypep())) return; + // No need to (and in fact cannot) mark variables if: + if (!DfgVertex::isSupportedDType(refp->varp()->dtypep())) return; // unsupported type + if (refp->varp()->isSc()) return; // SystemC VariableType* const tgtp = getTarget(refp); // Mark vertex as having a module reference outside current DFG getNet(tgtp)->setHasModRefs(); @@ -809,6 +810,7 @@ class AstToDfgVisitor final : public VNVisitor { || nodep->varp()->isIfaceRef() // Cannot handle interface references || nodep->varp()->delayp() // Cannot handle delayed variables || nodep->classOrPackagep() // Cannot represent cross module references + || nodep->varp()->isSc() // SystemC variables are special and rare, we can ignore ) { markReferenced(nodep); m_foundUnhandled = true; diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 717012a10..0168ba629 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -169,18 +169,8 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { void V3DfgPasses::inlineVars(DfgGraph& dfg) { for (DfgVertexVar& vtx : dfg.varVertices()) { if (DfgVarPacked* const varp = vtx.cast()) { - // Don't inline SystemC variables, as SystemC types are not interchangeable with - // internal types, and hence the variables are not interchangeable either. - if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) { + if (varp->hasSinks() && varp->isDrivenFullyByDfg()) { DfgVertex* const driverp = varp->srcp(); - - // We must keep the original driver in certain cases, when swapping them would - // not be functionally or technically (implementation reasons) equivalent: - // 1. If driven from a SystemC variable (assignment is non-trivial) - if (DfgVertexVar* const driverVarp = driverp->cast()) { - if (driverVarp->varp()->isSc()) continue; - } - varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); }); } } @@ -559,9 +549,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { varp->replaceWith(varp->srcp()); varp->nodep()->unlinkFrBack()->deleteTree(); } else if (DfgVarPacked* const driverp = varp->srcp()->cast()) { - // If it's driven from another variable, it can be replaced by that. However, we do not - // want to propagate SystemC variables into the design. - if (driverp->varp()->isSc()) continue; + // If it's driven from another variable, it can be replaced by that. // Mark it for replacement ++ctx.m_varsReplaced; UASSERT_OBJ(!varp->hasSinks(), varp, "Variable inlining should make this impossible"); diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index b91e34071..f308a0c7c 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1202,7 +1202,7 @@ class V3DfgPeephole final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { if (DfgConst* const idxp = vtxp->bitp()->cast()) { if (DfgVarArray* const varp = vtxp->fromp()->cast()) { - if (varp->srcp() && !varp->varp()->isForced() && !varp->varp()->isSc()) { + if (varp->srcp() && !varp->varp()->isForced()) { if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { if (DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT())) { if (!driverp->is()) { diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index d8de75898..64eef883a 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -54,14 +54,6 @@ class DfgRegularize final { }); return hasNonVarSink; } - // Anything that drives an SC variable needs an intermediate, - // as we can only assign simple variables to SC variables at runtime. - const bool hasScSink = vtx.findSink([](const DfgVertexVar& var) { // - return var.varp()->isSc(); - }); - if (hasScSink) return true; - // // Splice vertices always need a variable as they represent partial updates - // if (vtx.is()) return true; // Operations without multiple sinks need no variables if (!vtx.hasMultipleSinks()) return false; // Array selects need no variables, they are just memory references From 7401a8a43a9bc694af4a437d494388f23c90f035 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 21 Jul 2025 19:57:02 +0100 Subject: [PATCH 054/252] Refactor DFG IndependentBits analysis to work for any vertex. (#6209) This used to be restricted to variable vertices, but now can handle arbitrary circular vertices that represent packed values. It also converges faster than the earlier version. Prep for resolving loops through arrays. --- src/V3Dfg.h | 12 +++-- src/V3DfgBreakCycles.cpp | 114 +++++++++++++++++++++------------------ src/V3Number.cpp | 7 +++ src/V3Number.h | 1 + 4 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/V3Dfg.h b/src/V3Dfg.h index a725e1a60..cef0b5e72 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -230,7 +230,13 @@ public: // Retrieve user data, must be current. template - inline T& getUser(); + inline const T& getUser() const; + + // Retrieve user data, must be current. + template + T& getUser() { + return const_cast(const_cast(this)->getUser()); + } // Set user data, becomes current. template @@ -783,12 +789,12 @@ T& DfgVertex::user() { } template -T& DfgVertex::getUser() { +const T& DfgVertex::getUser() const { static_assert(sizeof(T) <= sizeof(UserDataStorage), "Size of user data type 'T' is too large for allocated storage"); static_assert(alignof(T) <= alignof(UserDataStorage), "Alignment of user data type 'T' is larger than allocated storage"); - T* const storagep = reinterpret_cast(&m_userDataStorage); + const T* const storagep = reinterpret_cast(&m_userDataStorage); #if VL_DEBUG const uint32_t userCurrent = m_graphp->m_userCurrent; UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"); diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 58405d8b5..222ef38e0 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -580,22 +580,25 @@ public: class IndependentBits final : public DfgVisitor { // STATE - DfgVarPacked* const m_varp; // The variable we are computing dependencies for - std::deque m_workList; // Work list for traversal + const uint32_t m_component; // The component the start vertex is part of // Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'. std::unordered_map m_vtxp2Mask; std::ofstream m_lineCoverageFile; // Line coverage file, just for testing // METHODS - // Retrieve the mask for the given vertex (create it with value 0 if needed) V3Number& mask(const DfgVertex* vtxp) { - return m_vtxp2Mask - .emplace(std::piecewise_construct, // - std::forward_as_tuple(vtxp), // - std::forward_as_tuple(vtxp->fileline(), static_cast(vtxp->width()), 0)) - .first->second; + // Look up (or create) mask for 'vtxp' + auto pair = m_vtxp2Mask.emplace( + std::piecewise_construct, // + std::forward_as_tuple(vtxp), // + std::forward_as_tuple(vtxp->fileline(), static_cast(vtxp->width()), 0)); + // Initialize to all ones if the vertex is part of the same component, otherwise zeroes + if (pair.second && vtxp->getUser() == m_component) { + pair.first->second.setAllBits1(); + } + return pair.first->second; } // Use this macro to call 'mask' in 'visit' methods. This also emits @@ -610,8 +613,7 @@ class IndependentBits final : public DfgVisitor { // VISITORS void visit(DfgVertex* vtxp) override { UINFO(9, "Unhandled vertex type " << vtxp->typeName()); - // Conservatively assume it depends on the variable... - mask(vtxp).setAllBits1(); // intentionally not using MASK here + // Conservative assumption about all bits being dependent prevails } void visit(DfgSplicePacked* vtxp) override { @@ -624,10 +626,6 @@ class IndependentBits final : public DfgVisitor { } void visit(DfgVarPacked* vtxp) override { - // The mask of the traced variable is known to be all ones - if (vtxp == m_varp) return; - - // Combine the masks of all drivers if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp); } @@ -656,7 +654,10 @@ class IndependentBits final : public DfgVisitor { void visit(DfgExtend* vtxp) override { const DfgVertex* const srcp = vtxp->srcp(); - MASK(vtxp).opSelInto(MASK(srcp), 0, srcp->width()); + const uint32_t sWidth = srcp->width(); + V3Number& m = MASK(vtxp); + m.opSelInto(MASK(srcp), 0, sWidth); + m.opSetRange(sWidth, vtxp->width() - sWidth, '0'); } void visit(DfgNot* vtxp) override { // @@ -684,7 +685,9 @@ class IndependentBits final : public DfgVisitor { if (shiftAmount >= width) return; V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; shiftedMask.opShiftR(MASK(lhsp), rConstp->num()); - MASK(vtxp).opSelInto(shiftedMask, 0, width - shiftAmount); + V3Number& m = MASK(vtxp); + m.opSelInto(shiftedMask, 0, width - shiftAmount); + m.opSetRange(width - shiftAmount, shiftAmount, '0'); return; } @@ -692,9 +695,9 @@ class IndependentBits final : public DfgVisitor { // the most significant dependent bit might be dependent V3Number& lMask = MASK(lhsp); V3Number& vMask = MASK(vtxp); - int idx = width - 1; - while (idx >= 0 && lMask.bitIs0(idx)) --idx; - while (idx >= 0) vMask.setBit(idx--, '1'); + uint32_t lzc = 0; // Leading zero count + while (lzc < width && lMask.bitIs0(width - 1 - lzc)) ++lzc; + while (lzc > 0) vMask.setBit(width - 1 - (--lzc), '0'); } void visit(DfgShiftL* vtxp) override { @@ -708,7 +711,9 @@ class IndependentBits final : public DfgVisitor { if (shiftAmount >= width) return; V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; shiftedMask.opShiftL(MASK(lhsp), rConstp->num()); - MASK(vtxp).opSelInto(shiftedMask, shiftAmount, width - shiftAmount); + V3Number& m = MASK(vtxp); + m.opSelInto(shiftedMask, shiftAmount, width - shiftAmount); + m.opSetRange(0, shiftAmount, '0'); return; } @@ -716,16 +721,16 @@ class IndependentBits final : public DfgVisitor { // the least significant dependent bit might be dependent V3Number& lMask = MASK(lhsp); V3Number& vMask = MASK(vtxp); - uint32_t idx = 0; - while (idx < width && lMask.bitIs0(idx)) ++idx; - while (idx < width) vMask.setBit(idx++, '1'); + uint32_t tzc = 0; // Trailing zero count + while (tzc < width && lMask.bitIs0(tzc)) ++tzc; + while (tzc > 0) vMask.setBit(--tzc, '0'); } #undef MASK // CONSTRUCTOR - IndependentBits(DfgVarPacked* varp) - : m_varp{varp} { + IndependentBits(DfgGraph& dfg, DfgVertex* vtxp) + : m_component{vtxp->getUser()} { if (v3Global.opt.debugCheck()) { m_lineCoverageFile.open( // v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() @@ -733,21 +738,24 @@ class IndependentBits final : public DfgVisitor { std::ios_base::out | std::ios_base::app); } - // The starting vertex depends on it's own value, duuhh... - mask(varp).setAllBits1(); - // Enqueue all sinks - varp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + // Work list for the traversal + std::deque workList; - // While there is an item on the worklist .. - while (!m_workList.empty()) { + // Enqueue every operation vertex in the analysed component + for (DfgVertex& vtx : dfg.opVertices()) { + if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + } + + // While there is an item on the worklist ... + while (!workList.empty()) { // Grab next item - DfgVertex* const currp = m_workList.front(); - m_workList.pop_front(); + DfgVertex* const currp = workList.front(); + workList.pop_front(); if (VN_IS(currp->dtypep(), UnpackArrayDType)) { // For an unpacked array vertex, just enque it's sinks. // (There can be no loops through arrays directly) - currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); }); continue; } @@ -761,33 +769,33 @@ class IndependentBits final : public DfgVisitor { // If mask changed, enqueue sinks if (!prevMask.isCaseEq(maskCurr)) { - currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); }); - // Check the mask only ever expands (no bit goes 1 -> 0) + // Check the mask only ever contrects (no bit goes 0 -> 1) if (VL_UNLIKELY(v3Global.opt.debugCheck())) { - V3Number notCurr{maskCurr}; - notCurr.opNot(maskCurr); - V3Number prevAndNotCurr{maskCurr}; - prevAndNotCurr.opAnd(prevMask, notCurr); - UASSERT_OBJ(prevAndNotCurr.isEqZero(), currp, "Mask should only expand"); + V3Number notPrev{prevMask}; + notPrev.opNot(prevMask); + V3Number notPrevAndCurr{maskCurr}; + notPrevAndCurr.opAnd(notPrev, maskCurr); + UASSERT_OBJ(notPrevAndCurr.isEqZero(), currp, "Mask should only contract"); } } } } public: - // Given a variable, compute which bits in this variable are independent of - // the variable itself (simple forward dataflow analysis). Returns a bit - // mask where a set bit indicates that bit is independent of the variable - // itself (logic is not circular). The result is a conservative estimate, - // so bits reported dependent might not actually be, but all bits reported - // independent are known to be so. - static V3Number apply(DfgVarPacked* varp) { - UASSERT_OBJ(varp->srcp(), varp, "Don't call on undriven variable"); - IndependentBits independentBits{varp}; + // Given a Vertex that is part of an SCC denoted by vtxp->user(), + // compute which bits of this vertex have a value that is independent of + // the current value of the Vertex itself (simple forward dataflow + // analysis). Returns a bit mask where a set bit indicates that bit is + // independent of the vertex itself (logic is not circular). The result is + // a conservative estimate, so bits reported dependent might not actually + // be, but all bits reported independent are known to be so. + static V3Number apply(DfgGraph& dfg, DfgVertex* vtxp) { + IndependentBits independentBits{dfg, vtxp}; // The mask represents the dependent bits, so invert it - V3Number result{varp->fileline(), static_cast(varp->width()), 0}; - result.opNot(independentBits.mask(varp->srcp())); + V3Number result{vtxp->fileline(), static_cast(vtxp->width()), 0}; + result.opNot(independentBits.mask(vtxp)); return result; } }; @@ -899,7 +907,7 @@ public: if (usedBits.isEqZero()) return 0; // Figure out which bits of 'varp' are dependent on themselves - const V3Number indpBits = IndependentBits::apply(varp); + const V3Number indpBits = IndependentBits::apply(dfg, varp); UINFO(9, "Independent bits of '" << varp->nodep()->name() << "' are " << indpBits.displayed(varp->nodep(), "%b")); // Can't do anything if all bits are dependent diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 09933f352..ce1477b25 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -2242,6 +2242,13 @@ V3Number& V3Number::opBufIf1(const V3Number& ens, const V3Number& if1s) { return *this; } +// Sets all bits in range to the given value +V3Number& V3Number::opSetRange(uint32_t lsb, uint32_t width, char bitValue) { + const uint32_t msb = lsb + width - 1; + for (uint32_t i = lsb; i <= msb; ++i) setBit(i, bitValue); + return *this; +} + V3Number& V3Number::opAssign(const V3Number& lhs) { return opAssignNonXZ(lhs, false); } V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) { // Note may be a width change during the assign. diff --git a/src/V3Number.h b/src/V3Number.h index 755fdc134..234956cb7 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -716,6 +716,7 @@ public: V3Number& opWildEq(const V3Number& lhs, const V3Number& rhs); V3Number& opWildNeq(const V3Number& lhs, const V3Number& rhs); V3Number& opBufIf1(const V3Number& ens, const V3Number& if1s); + V3Number& opSetRange(uint32_t lsb, uint32_t width, char bitValue); // "standard" math V3Number& opNot(const V3Number& lhs); V3Number& opLogNot(const V3Number& lhs); From 74d4b0c0ea8570c5eaf61800d299148bae1b2cdd Mon Sep 17 00:00:00 2001 From: Danny Oler Date: Mon, 21 Jul 2025 16:28:50 -0700 Subject: [PATCH 055/252] Fix automatic task variables in unrolled loops with forks (#6194) (#6201). --- docs/CONTRIBUTORS | 1 + src/V3Unroll.cpp | 16 ++++ .../t/t_unroll_automatic_task_fork.out | 41 ++++++++++ .../t/t_unroll_automatic_task_fork.py | 18 ++++ test_regress/t/t_unroll_automatic_task_fork.v | 82 +++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 test_regress/t/t_unroll_automatic_task_fork.out create mode 100755 test_regress/t/t_unroll_automatic_task_fork.py create mode 100644 test_regress/t/t_unroll_automatic_task_fork.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index bbe5f8c56..553cfa711 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -37,6 +37,7 @@ Chuxuan Wang Chykon Conor McCullough Dan Petrisko +Danny Oler Daniel Bates Dave Sargeant David Horton diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 67ed5f1d0..bac260f31 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -46,6 +46,7 @@ class UnrollVisitor final : public VNVisitor { bool m_varModeCheck; // Just checking RHS assignments bool m_varModeReplace; // Replacing varrefs bool m_varAssignHit; // Assign var hit + bool m_forkHit; // Fork hit bool m_generate; // Expand single generate For loop string m_beginName; // What name to give begin iterations VDouble0 m_statLoops; // Statistic tracking @@ -133,6 +134,7 @@ class UnrollVisitor final : public VNVisitor { // Now, make sure there's no assignment to this variable in the loop m_varModeCheck = true; m_varAssignHit = false; + m_forkHit = false; m_ignoreIncp = incp; iterateAndNextNull(precondsp); iterateAndNextNull(bodysp); @@ -141,6 +143,8 @@ class UnrollVisitor final : public VNVisitor { m_ignoreIncp = nullptr; if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); + if (m_forkHit) return cantUnroll(nodep, "fork inside loop"); + // if (m_forVscp) { UINFO(8, " Loop Variable: " << m_forVscp); @@ -463,6 +467,17 @@ class UnrollVisitor final : public VNVisitor { } } + void visit(AstFork* nodep) override { + if (m_varModeCheck) { + if (nodep->joinType().joinNone() || nodep->joinType().joinAny()) { + // Forks are not allowed to unroll for loops, so we just set a flag + m_forkHit = true; + } + } else { + iterateChildren(nodep); + } + } + //-------------------- // Default: Just iterate void visit(AstNode* nodep) override { @@ -489,6 +504,7 @@ public: m_varModeCheck = false; m_varModeReplace = false; m_varAssignHit = false; + m_forkHit = false; m_generate = generate; m_beginName = beginName; } diff --git a/test_regress/t/t_unroll_automatic_task_fork.out b/test_regress/t/t_unroll_automatic_task_fork.out new file mode 100644 index 000000000..456cdcfa0 --- /dev/null +++ b/test_regress/t/t_unroll_automatic_task_fork.out @@ -0,0 +1,41 @@ +task_example start: module 0, channel 0 +task_example end: module 0, channel 0 +task_example start: module 0, channel 1 +task_example end: module 0, channel 1 +task_example start: module 1, channel 0 +task_example end: module 1, channel 0 +task_example start: module 1, channel 1 +task_example end: module 1, channel 1 +*-* Test 1 Finished *-* +task_example start: module 0, channel 0 +task_example end: module 0, channel 0 +task_example start: module 0, channel 1 +task_example end: module 0, channel 1 +task_example start: module 1, channel 0 +task_example end: module 1, channel 0 +task_example start: module 1, channel 1 +task_example end: module 1, channel 1 +*-* Test 2 Finished *-* +task_example start: module 0, channel 0 +extra statement +task_example start: module 0, channel 1 +extra statement +task_example start: module 1, channel 0 +extra statement +task_example start: module 1, channel 1 +extra statement +task_example end: module 0, channel 0 +task_example end: module 0, channel 1 +task_example end: module 1, channel 0 +task_example end: module 1, channel 1 +*-* Test 3 Finished *-* +task_example start: module 0, channel 0 +task_example start: module 0, channel 1 +task_example start: module 1, channel 0 +task_example start: module 1, channel 1 +task_example end: module 0, channel 0 +task_example end: module 0, channel 1 +task_example end: module 1, channel 0 +task_example end: module 1, channel 1 +*-* Test 4 Finished *-* +*-* All Finished *-* diff --git a/test_regress/t/t_unroll_automatic_task_fork.py b/test_regress/t/t_unroll_automatic_task_fork.py new file mode 100755 index 000000000..d2498e873 --- /dev/null +++ b/test_regress/t/t_unroll_automatic_task_fork.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=['--binary']) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_unroll_automatic_task_fork.v b/test_regress/t/t_unroll_automatic_task_fork.v new file mode 100644 index 000000000..b77d25261 --- /dev/null +++ b/test_regress/t/t_unroll_automatic_task_fork.v @@ -0,0 +1,82 @@ +// 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 + +// Targets issue 6194 + +module t; + + logic clk; + + initial begin + clk = 0; + forever #(1) clk = ~clk; + end + + task automatic task_example(int module_id, int channel); + $display("task_example start: module %0d, channel %0d", module_id, channel); + @(posedge clk); + $display("task_example end: module %0d, channel %0d", module_id, channel); + endtask + + initial begin : initial_block + for (int m = 0; m < 2; m++) begin + for (int i = 0; i < 2; i++) begin : forked_loop + automatic int mod = m; + automatic int ch = i; + fork : forked_block + task_example(mod, ch); + join + end + end + + #10 + $write("*-* Test 1 Finished *-*\n"); + + for (int m = 0; m < 2; m++) begin + for (int i = 0; i < 2; i++) begin : forked_loop + automatic int mod = m; + automatic int ch = i; + fork : forked_block + task_example(mod, ch); + join_any + end + end + + #10 + $write("*-* Test 2 Finished *-*\n"); + + for (int m = 0; m < 2; m++) begin + for (int i = 0; i < 2; i++) begin : forked_loop + automatic int mod = m; + automatic int ch = i; + fork : forked_block + task_example(mod, ch); + $display("extra statement"); + join_any + end + end + + #10 + $write("*-* Test 3 Finished *-*\n"); + + for (int m = 0; m < 2; m++) begin + for (int i = 0; i < 2; i++) begin : forked_loop + automatic int mod = m; + automatic int ch = i; + fork : forked_block + task_example(mod, ch); + join_none + end + end + + #10 + $write("*-* Test 4 Finished *-*\n"); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From b5126a6abe777549b6beeb099fe1623cc5e9d343 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 21 Jul 2025 23:29:42 +0000 Subject: [PATCH 056/252] Apply 'make format' --- src/V3Unroll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index bac260f31..6b4332beb 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -46,7 +46,7 @@ class UnrollVisitor final : public VNVisitor { bool m_varModeCheck; // Just checking RHS assignments bool m_varModeReplace; // Replacing varrefs bool m_varAssignHit; // Assign var hit - bool m_forkHit; // Fork hit + bool m_forkHit; // Fork hit bool m_generate; // Expand single generate For loop string m_beginName; // What name to give begin iterations VDouble0 m_statLoops; // Statistic tracking From 9f04ee68c8d931ef7e55d6ae20c324eb71f524b5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 22 Jul 2025 08:23:45 +0100 Subject: [PATCH 057/252] Optimize combinational cycles through arrays in DFG (#6210) Extending V3DfgBreakCycles to handle common cases involving unpacked arrays. --- src/V3DfgBreakCycles.cpp | 223 +++++++++++++++++++++------- src/V3Number.h | 2 +- test_regress/t/t_dfg_break_cycles.v | 29 ++++ 3 files changed, 197 insertions(+), 57 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 222ef38e0..8e7fd7804 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -385,6 +385,27 @@ class TraceDriver final : public DfgVisitor { } } + void visit(DfgArraySel* vtxp) override { + // Only constant select + DfgConst* const idxp = vtxp->bitp()->cast(); + if (!idxp) return; + // From a variable + DfgVarArray* varp = vtxp->fromp()->cast(); + if (!varp) return; + // Skip through intermediate variables + while (varp->srcp() && varp->srcp()->is()) { + varp = varp->srcp()->as(); + } + // Find driver + if (!varp->srcp()) return; + DfgSpliceArray* const splicep = varp->srcp()->cast(); + if (!splicep) return; + DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + if (!driverp) return; + // Trace the driver + SET_RESULT(trace(driverp, m_msb, m_lsb)); + } + void visit(DfgConcat* vtxp) override { DfgVertex* const rhsp = vtxp->rhsp(); DfgVertex* const lhsp = vtxp->lhsp(); @@ -629,6 +650,27 @@ class IndependentBits final : public DfgVisitor { if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp); } + void visit(DfgArraySel* vtxp) override { + // Only constant select + DfgConst* const idxp = vtxp->bitp()->cast(); + if (!idxp) return; + // From a variable + DfgVarArray* varp = vtxp->fromp()->cast(); + if (!varp) return; + // Skip through intermediate variables + while (varp->srcp() && varp->srcp()->is()) { + varp = varp->srcp()->as(); + } + // Find driver + if (!varp->srcp()) return; + DfgSpliceArray* const splicep = varp->srcp()->cast(); + if (!splicep) return; + DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + if (!driverp) return; + // Update mask + MASK(vtxp) = MASK(driverp); + } + void visit(DfgConcat* vtxp) override { const DfgVertex* const rhsp = vtxp->rhsp(); const DfgVertex* const lhsp = vtxp->lhsp(); @@ -801,40 +843,78 @@ public: }; class FixUpSelDrivers final { -public: - // Attempt to push Sel form Var through to the driving - // expression of the selected bits. This can fix things like - // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. - // Returns the number of drivers fixed up. - static size_t apply(DfgGraph& dfg, DfgVarPacked* varp) { - UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name()); - const uint32_t component = varp->getUser(); + static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) { size_t nImprovements = 0; - varp->forEachSink([&](DfgVertex& sink) { - // Ignore if sink is not part of cycle + const uint32_t component = vtxp->getUser(); + vtxp->forEachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of same cycle if (sink.getUser() != component) return; - // Only Handle Sels now + // Only handle Sel DfgSel* const selp = sink.cast(); if (!selp) return; // Try to find of the driver of the selected bits outside the cycle DfgVertex* const fixp - = TraceDriver::apply(dfg, varp, selp->lsb(), selp->width(), false); + = TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false); if (!fixp) return; // Found an out-of-cycle driver. We can replace this sel with that. selp->replaceWith(fixp); selp->unlinkDelete(dfg); ++nImprovements; }); + return nImprovements; + } + + static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) { + size_t nImprovements = 0; + const uint32_t component = vtxp->getUser(); + vtxp->forEachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of same cycle + if (sink.getUser() != component) return; + // Only handle ArraySels + DfgArraySel* const aselp = sink.cast(); + if (!aselp) return; + // First, try to fix up the whole word + DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false); + if (fixp) { + // Found an out-of-cycle driver for the whole ArraySel + aselp->replaceWith(fixp); + aselp->unlinkDelete(dfg); + ++nImprovements; + } else { + // Attempt to fix up piece-wise at Sels applied to the ArraySel + nImprovements += fixUpSelSinks(dfg, aselp); + // Remove if became unused + if (!aselp->hasSinks()) aselp->unlinkDelete(dfg); + } + }); + return nImprovements; + } + +public: + // Attempt to push Sel form Var through to the driving + // expression of the selected bits. This can fix things like + // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. + // Returns the number of drivers fixed up. + static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) { + UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name()); + size_t nImprovements = 0; + if (varp->is()) { + // For Packed variables, fix up all Sels applied to it + nImprovements += fixUpSelSinks(dfg, varp); + } else if (varp->is()) { + // For Array variables, fix up each ArraySel applied to it + nImprovements += fixUpArraySelSinks(dfg, varp); + } UINFO(9, "FixUpSelDrivers made " << nImprovements << " improvements"); return nImprovements; } }; class FixUpIndependentRanges final { - // Returns a bitmask set if that bit of 'varp' is used (has a sink) - static V3Number computeUsedBits(DfgVarPacked* varp) { - V3Number result{varp->fileline(), static_cast(varp->width()), 0}; - varp->forEachSink([&](DfgVertex& sink) { + // Returns a bitmask set if that bit of 'vtxp' is used (has a sink) + static V3Number computeUsedBits(DfgVertex* vtxp) { + V3Number result{vtxp->fileline(), static_cast(vtxp->width()), 0}; + vtxp->forEachSink([&result](DfgVertex& sink) { // If used via a Sel, mark the selected bits used if (DfgSel* const selp = sink.cast()) { uint32_t lsb = selp->lsb(); @@ -842,19 +922,29 @@ class FixUpIndependentRanges final { for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1'); return; } - // Used without a Sel, so all bits are used result.setAllBits1(); }); return result; } - // Trace drivers of independent bits of 'varp' in the range '[hi:lo]' + static std::string debugStr(DfgVertex* vtxp) { + if (DfgArraySel* const aselp = vtxp->cast()) { + const size_t i = aselp->bitp()->as()->toSizeT(); + return debugStr(aselp->fromp()) + "[" + std::to_string(i) + "]"; + } + if (DfgVertexVar* const varp = vtxp->cast()) { + return varp->nodep()->name(); + } + vtxp->v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE + } + + // Trace drivers of independent bits of 'vtxp' in the range '[hi:lo]' // append replacement terms to 'termps'. Returns number of successful // replacements. static size_t gatherTerms(std::vector& termps, DfgGraph& dfg, - DfgVarPacked* const varp, const V3Number& indpBits, - const uint32_t hi, const uint32_t lo) { + DfgVertex* const vtxp, const V3Number& indpBits, const uint32_t hi, + const uint32_t lo) { size_t nImprovements = 0; // Iterate through consecutive dependent/non-dependet ranges within [hi:lo] bool isIndependent = indpBits.bitIs1(lo); @@ -866,19 +956,19 @@ class FixUpIndependentRanges final { DfgVertex* termp = nullptr; // If the range is not self-dependent, attempt to trace its driver if (isIndependent) { - termp = TraceDriver::apply(dfg, varp, lsb, width, true); + termp = TraceDriver::apply(dfg, vtxp, lsb, width, true); if (termp) { ++nImprovements; } else { UINFO(5, "TraceDriver of independent range failed for " - << varp->nodep()->name() << "[" << msb << ":" << lsb << "]"); + << debugStr(vtxp) << "[" << msb << ":" << lsb << "]"); } } // Fall back on using the part of the variable (if dependent, or trace failed) if (!termp) { AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); - DfgSel* const selp = new DfgSel{dfg, varp->fileline(), dtypep}; - // Do not connect selp->fromp yet, need to do afer replacing 'varp' + DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; + // Do not connect selp->fromp yet, need to do afer replacing 'vtxp' selp->lsb(lsb); termp = selp; } @@ -891,41 +981,37 @@ class FixUpIndependentRanges final { return nImprovements; } -public: - // Similar to FixUpSelDrivers, but first comptute which bits of the - // variable are self dependent, and fix up those that are independent - // but used. - static size_t apply(DfgGraph& dfg, DfgVarPacked* varp) { - UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name()); + static size_t fixUpPacked(DfgGraph& dfg, DfgVertex* vtxp) { + UASSERT_OBJ(VN_IS(vtxp->dtypep(), BasicDType), vtxp, "Should be a packed BasicDType"); size_t nImprovements = 0; - // Figure out which bits of 'varp' are used - const V3Number usedBits = computeUsedBits(varp); - UINFO(9, "Used bits of '" << varp->nodep()->name() << "' are " - << usedBits.displayed(varp->nodep(), "%b")); + // Figure out which bits of 'vtxp' are used + const V3Number usedBits = computeUsedBits(vtxp); + UINFO(9, "Used bits of '" << debugStr(vtxp) << "' are " + << usedBits.displayed(vtxp->fileline(), "%b")); // Nothing to do if no bits are used if (usedBits.isEqZero()) return 0; - // Figure out which bits of 'varp' are dependent on themselves - const V3Number indpBits = IndependentBits::apply(dfg, varp); - UINFO(9, "Independent bits of '" << varp->nodep()->name() << "' are " - << indpBits.displayed(varp->nodep(), "%b")); + // Figure out which bits of 'vtxp' are dependent of themselves + const V3Number indpBits = IndependentBits::apply(dfg, vtxp); + UINFO(9, "Independent bits of '" << debugStr(vtxp) << "' are " + << indpBits.displayed(vtxp->fileline(), "%b")); // Can't do anything if all bits are dependent if (indpBits.isEqZero()) return 0; { // Nothing to do if no used bits are independen (all used bits are dependent) - V3Number usedAndIndependent{varp->fileline(), static_cast(varp->width()), 0}; + V3Number usedAndIndependent{vtxp->fileline(), static_cast(vtxp->width()), 0}; usedAndIndependent.opAnd(usedBits, indpBits); if (usedAndIndependent.isEqZero()) return 0; } - // We are computing the terms to concatenate and replace 'varp' with + // We are computing the terms to concatenate and replace 'vtxp' with std::vector termps; // Iterate through consecutive used/unused ranges - FileLine* const flp = varp->fileline(); - const uint32_t width = varp->width(); + FileLine* const flp = vtxp->fileline(); + const uint32_t width = vtxp->width(); bool isUsed = usedBits.bitIs1(0); // Is current range used uint32_t lsb = 0; // LSB of current range for (uint32_t msb = 0; msb < width; ++msb) { @@ -933,7 +1019,7 @@ public: if (!endRange) continue; if (isUsed) { // The range is used, compute the replacement terms - nImprovements += gatherTerms(termps, dfg, varp, indpBits, msb, lsb); + nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb); } else { // The range is not used, just use constant 0 as a placeholder termps.emplace_back(new DfgConst{dfg, flp, msb - lsb + 1}); @@ -957,16 +1043,47 @@ public: replacementp = catp; } - // Replace the variable - varp->replaceWith(replacementp); + // Replace the vertex + vtxp->replaceWith(replacementp); // Connect the Sel nodes in the replacement for (DfgVertex* const termp : termps) { if (DfgSel* const selp = termp->cast()) { - if (!selp->fromp()) selp->fromp(varp); + if (!selp->fromp()) selp->fromp(vtxp); } } } + return nImprovements; + } + +public: + // Similar to FixUpSelDrivers, but first comptute which bits of the + // variable are self dependent, and fix up those that are independent + // but used. + static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) { + UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name()); + size_t nImprovements = 0; + + if (varp->is()) { + // For Packed variables, fix up as whole + nImprovements += fixUpPacked(dfg, varp); + } else if (varp->is()) { + // For array variables, fix up element-wise + const uint32_t component = varp->getUser(); + varp->forEachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of cycle + if (sink.getUser() != component) return; + // Only handle ArraySels with constant index + DfgArraySel* const aselp = sink.cast(); + if (!aselp) return; + if (!aselp->bitp()->is()) return; + // Fix up the word + nImprovements += fixUpPacked(dfg, aselp); + // Remove if became unused + if (!aselp->hasSinks()) aselp->unlinkDelete(dfg); + }); + } + UINFO(9, "FixUpIndependentRanges made " << nImprovements << " improvements"); return nImprovements; } @@ -1022,14 +1139,11 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { // Method 1: FixUpSelDrivers for (DfgVertexVar& vtx : res.varVertices()) { - // Only handle DfgVarPacked at this point - DfgVarPacked* const varp = vtx.cast(); - if (!varp) continue; // If Variable is not part of a cycle, move on - const uint32_t component = varp->getUser(); + const uint32_t component = vtx.getUser(); if (!component) continue; - const size_t nFixed = FixUpSelDrivers::apply(res, varp); + const size_t nFixed = FixUpSelDrivers::apply(res, &vtx); if (nFixed) { nImprovements += nFixed; ctx.m_breakCyclesContext.m_nImprovements += nFixed; @@ -1042,14 +1156,11 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { // Method 2. FixUpIndependentRanges for (DfgVertexVar& vtx : res.varVertices()) { - // Only handle DfgVarPacked at this point - DfgVarPacked* const varp = vtx.cast(); - if (!varp) continue; // If Variable is not part of a cycle, move on - const uint32_t component = varp->getUser(); + const uint32_t component = vtx.getUser(); if (!component) continue; - const size_t nFixed = FixUpIndependentRanges::apply(res, varp); + const size_t nFixed = FixUpIndependentRanges::apply(res, &vtx); if (nFixed) { nImprovements += nFixed; ctx.m_breakCyclesContext.m_nImprovements += nFixed; diff --git a/src/V3Number.h b/src/V3Number.h index 234956cb7..c43678bea 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -564,7 +564,6 @@ private: } } static string displayPad(size_t fmtsize, char pad, bool left, const string& in) VL_PURE; - string displayed(FileLine* fl, const string& vformat) const VL_MT_STABLE; string displayed(const string& vformat) const VL_MT_STABLE { return displayed(m_fileline, vformat); } @@ -592,6 +591,7 @@ public: // ACCESSORS string ascii(bool prefixed = true, bool cleanVerilog = false) const VL_MT_STABLE; string displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE; + string displayed(FileLine* fl, 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 widthToFit() const; // Minimum width that can represent this number (~== log2(num)+1) diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index b7f461835..5a44799e8 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -111,4 +111,33 @@ module t ( assign PARTIAL[0] = rand_a[0]; // PARTIAL[1] intentionally unconnected assign PARTIAL[3:2] = rand_a[3:2] ^ {PARTIAL[2], PARTIAL[0]}; + + wire [2:0] array_0 [2]; + assign array_0[0] = rand_a[2:0]; + assign array_0[1] = array_0[0]; + `signal(ARRAY_0, 3); + assign ARRAY_0 = array_0[1]; + + wire [2:0] array_1 [1]; + assign array_1[0][0] = rand_a[0]; + assign array_1[0][1] = array_1[0][0]; + assign array_1[0][2] = array_1[0][1]; + `signal(ARRAY_1, 3); + assign ARRAY_1 = array_1[0]; + + wire [2:0] array_2a [2]; + wire [2:0] array_2b [2]; + assign array_2a[0][0] = rand_a[0]; + assign array_2a[0][1] = array_2b[1][0]; + assign array_2a[0][2] = array_2b[1][1]; + assign array_2a[1] = array_2a[0]; + assign array_2b = array_2a; + `signal(ARRAY_2, 3); + assign ARRAY_2 = array_2a[0]; + + wire [2:0] array_3 [2]; + assign array_3[0] = rand_a[2:0] ^ array_3[1] >> 1; + assign array_3[1] = array_3[0]; + `signal(ARRAY_3, 3); + assign ARRAY_3 = array_3[0]; endmodule From 344fabf56a16e407b8e1e1d8d40b8c3d512271a5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 22 Jul 2025 14:03:35 +0100 Subject: [PATCH 058/252] Fix incorrect assumption in V3DfgDecomposition (#6215) Due to SCC merging the deleted assumption/assertion can be violated. Fixes #6211. --- src/V3DfgDecomposition.cpp | 21 +++++++++------- test_regress/t/t_dfg_circular_merged_scc.py | 16 ++++++++++++ test_regress/t/t_dfg_circular_merged_scc.v | 27 +++++++++++++++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100755 test_regress/t/t_dfg_circular_merged_scc.py create mode 100644 test_regress/t/t_dfg_circular_merged_scc.v diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 4647d1484..38f31288e 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -345,16 +345,19 @@ class ExtractCyclicComponents final { void fixEdges(DfgVertexVar& vtx) { const size_t component = state(vtx).component; - // All variable vertices have at most a single source, and only variable - // vertices can have multiple sinks, therefore the source must be either: - // - in the same component as the variable vertex - // - be a variable vertex itself, which might be in a different component - // The later case will be fixed up when handling the source variable - DfgVertex* const srcp = vtx.srcp(); - UASSERT_OBJ(!srcp || srcp->is() || state(*srcp).component == component, &vtx, - "Driver of DfgVertexVar must be in the same component"); + // Fix up sources in a different component + vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) { + DfgVertex* const srcp = edge.sourcep(); + if (!srcp) return; + const size_t sourceComponent = state(*srcp).component; + // Same component is OK + if (sourceComponent == component) return; + // Relink the source to write the clone + edge.unlinkSource(); + getClone(vtx, sourceComponent).srcp(srcp); + }); - // Fix up sinks in a differetn component + // Fix up sinks in a different component vtx.forEachSinkEdge([&](DfgEdge& edge) { const size_t sinkComponent = state(*edge.sinkp()).component; // Same component is OK diff --git a/test_regress/t/t_dfg_circular_merged_scc.py b/test_regress/t/t_dfg_circular_merged_scc.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_dfg_circular_merged_scc.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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_dfg_circular_merged_scc.v b/test_regress/t/t_dfg_circular_merged_scc.v new file mode 100644 index 000000000..c9ccaddb9 --- /dev/null +++ b/test_regress/t/t_dfg_circular_merged_scc.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 Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +module mul (input [8:0] A, input [16:0] B, output [25:0] Y); + assign Y = $signed(A) * $signed(B); +endmodule + +module A; + wire [26:0] C; + wire [26:0] D; + wire [8:0] E; + + // This yields a circular DFG with a fairly special form that used to trip + // decomposition. + mul mul ( + .A(9'd10), + .B(17'h0cccd), + .Y({ C[26], C[9:0], D[15:1] }) + ); + + assign E = { C[8:0] }; + assign C[25:10] = {16{C[26]}}; + +endmodule From 2958a5aaaea15db1a7c11e280397487a5fc5aff8 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 23 Jul 2025 10:48:55 +0100 Subject: [PATCH 059/252] Internals: Do not emit temporaries for atomic assignments. (#6217) Added test for a particularly convoluted case requiring fixup in V3Premit. To help with statistics stability, also prevent V3Premit from introducing temporaries for assignment where the RHS reads the LHS, but the assignment is known to be atomic (by emitted C++ semantics). Also rename `createWideTemp` to `createTemp`, as it is used for non-wide expressions as well. --- src/V3Premit.cpp | 23 ++++++++++++-------- test_regress/t/t_wide_temp_while_cond.cpp | 12 +++++++++++ test_regress/t/t_wide_temp_while_cond.py | 22 +++++++++++++++++++ test_regress/t/t_wide_temp_while_cond.v | 26 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 test_regress/t/t_wide_temp_while_cond.cpp create mode 100755 test_regress/t/t_wide_temp_while_cond.py create mode 100644 test_regress/t/t_wide_temp_while_cond.v diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index a9d7f9581..d9b5f805f 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -46,6 +46,7 @@ class PremitVisitor final : public VNVisitor { // STATE - across all visitors VDouble0 m_extractedToConstPool; // Statistic tracking + VDouble0 m_temporaryVarsCreated; // Statistic tracking // STATE - for current visit position (use VL_RESTORER) AstCFunc* m_cfuncp = nullptr; // Current block @@ -62,12 +63,12 @@ class PremitVisitor final : public VNVisitor { if (!nodep->isWide()) return; // Not wide if (m_assignLhs) return; // This is an lvalue! UASSERT_OBJ(!VN_IS(nodep->firstAbovep(), ArraySel), nodep, "Should have been ignored"); - createWideTemp(nodep); + createTemp(nodep); } - AstVar* createWideTemp(AstNodeExpr* nodep) { + AstVar* createTemp(AstNodeExpr* nodep) { UASSERT_OBJ(m_stmtp, nodep, "Attempting to create temporary with no insertion point"); - UINFO(4, "createWideTemp: " << nodep); + UINFO(4, "createTemp: " << nodep); VNRelinker relinker; nodep->unlinkFrBack(&relinker); @@ -93,6 +94,7 @@ class PremitVisitor final : public VNVisitor { const std::string name = "__Vtemp_" + std::to_string(++m_tmpVarCnt); varp = new AstVar{flp, VVarType::STMTTEMP, name, nodep->dtypep()}; m_cfuncp->addInitsp(varp); + ++m_temporaryVarsCreated; // Put assignment before the referencing statement assignp = new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, nodep}; @@ -221,9 +223,10 @@ class PremitVisitor final : public VNVisitor { } } - if (rhsReadsLhs(nodep)) { - // Need to do this even if not wide, as e.g. a select may be on a wide operator - createWideTemp(nodep->rhsp()); + // If the RHS reads the LHS, we need a temporary unless the update is atomic + const bool isAtomic = VN_IS(nodep->lhsp(), VarRef) && !nodep->lhsp()->isWide(); + if (!isAtomic && rhsReadsLhs(nodep)) { + createTemp(nodep->rhsp()); } else { iterateAndNextNull(nodep->rhsp()); } @@ -290,7 +293,7 @@ class PremitVisitor final : public VNVisitor { void visit(AstCvtPackedToArray* nodep) override { iterateChildren(nodep); checkNode(nodep); - if (!VN_IS(nodep->backp(), NodeAssign)) createWideTemp(nodep); + if (!VN_IS(nodep->backp(), NodeAssign)) createTemp(nodep); } void visit(AstCvtUnpackedToQueue* nodep) override { iterateChildren(nodep); @@ -330,7 +333,7 @@ class PremitVisitor final : public VNVisitor { && !VN_IS(nodep->condp(), VarRef)) { // We're going to need the expression several times in the expanded code, // so might as well make it a common expression - createWideTemp(nodep->condp()); + createTemp(nodep->condp()); VIsCached::clearCacheTree(); } checkNode(nodep); @@ -342,7 +345,7 @@ class PremitVisitor final : public VNVisitor { for (AstNodeExpr *expp = nodep->exprsp(), *nextp; expp; expp = nextp) { nextp = VN_AS(expp->nextp(), NodeExpr); if (expp->isString() && !VN_IS(expp, VarRef)) { - AstVar* const varp = createWideTemp(expp); + AstVar* const varp = createTemp(expp); // Do not remove VarRefs to this in V3Const varp->noSubst(true); } @@ -360,6 +363,8 @@ public: ~PremitVisitor() override { V3Stats::addStat("Optimizations, Prelim extracted value to ConstPool", m_extractedToConstPool); + V3Stats::addStat("Optimizations, Prelim temporary variables created", + m_temporaryVarsCreated); } }; diff --git a/test_regress/t/t_wide_temp_while_cond.cpp b/test_regress/t/t_wide_temp_while_cond.cpp new file mode 100644 index 000000000..d6e745f07 --- /dev/null +++ b/test_regress/t/t_wide_temp_while_cond.cpp @@ -0,0 +1,12 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2025 by Geza Lore. 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 +//************************************************************************* + +extern "C" int identity(int x) { return x; } diff --git a/test_regress/t/t_wide_temp_while_cond.py b/test_regress/t/t_wide_temp_while_cond.py new file mode 100755 index 000000000..9df9caa86 --- /dev/null +++ b/test_regress/t/t_wide_temp_while_cond.py @@ -0,0 +1,22 @@ +#!/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=["--exe", "--main", "--timing", "--stats", "t/t_wide_temp_while_cond.cpp"]) + +if test.vlt or test.vltmt: + test.file_grep(test.stats, r'Optimizations, Prelim temporary variables created\s+(\d+)', 3) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_wide_temp_while_cond.v b/test_regress/t/t_wide_temp_while_cond.v new file mode 100644 index 000000000..2a88840c4 --- /dev/null +++ b/test_regress/t/t_wide_temp_while_cond.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +import "DPI-C" pure function int identity(input int value); + +module t; + initial begin + int n = 0; + logic [127:0] val = 128'b1; + logic [15:0] one = 16'b1; + + // This condition involves multiple wide temporaries, and an over-width + // shift, all of which requires V3Premit to fix up. + while (|((val[ 7'(one >> identity(32)) +: 96] << n) >> n)) begin + ++n; + end + + $display("n=%0d", n); + if (n != 96) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 763183f06731701c4cd288e87c23bd80ebfbf64d Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 23 Jul 2025 13:50:39 +0100 Subject: [PATCH 060/252] Internals: Remove AstWhile::precondsp() (#6219). No functional change intended. --- src/V3AstNodeOther.h | 7 ++- src/V3AstNodes.cpp | 7 +-- src/V3Const.cpp | 12 ++--- src/V3Coverage.cpp | 1 - src/V3EmitCFunc.h | 2 - src/V3EmitV.cpp | 2 - src/V3EmitXml.cpp | 3 -- src/V3Life.cpp | 1 - src/V3LinkInc.cpp | 54 ++++++-------------- src/V3LinkJump.cpp | 1 - src/V3Premit.cpp | 24 ++++----- src/V3SchedVirtIface.cpp | 1 - src/V3Simulate.h | 2 - src/V3Task.cpp | 5 +- src/V3Unroll.cpp | 26 +++------- src/V3Width.cpp | 1 - src/verilog.y | 1 - test_regress/t/t_interface_virtual_unsup.out | 3 -- test_regress/t/t_json_only_debugcheck.out | 6 +-- test_regress/t/t_json_only_flat_vlvbound.out | 4 +- test_regress/t/t_xml_debugcheck.out | 6 --- test_regress/t/t_xml_flat_vlvbound.out | 4 -- 22 files changed, 47 insertions(+), 126 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 6f7e3195b..b46c18028 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3737,10 +3737,9 @@ public: bool isTimingControl() const override { return true; } }; class AstWhile final : public AstNodeStmt { - // @astgen op1 := precondsp : List[AstNode] - // @astgen op2 := condp : AstNodeExpr - // @astgen op3 := stmtsp : List[AstNode] - // @astgen op4 := incsp : List[AstNode] + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] + // @astgen op3 := incsp : List[AstNode] VOptionBool m_unrollFull; // Full, disable, or default unrolling public: AstWhile(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp = nullptr, AstNode* incsp = nullptr) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 92915d5eb..8ed5b6d2e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1518,12 +1518,7 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) { void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point - // Preconditions insert first just before themselves (the normal rule - // for other statement types) - if (belowp == precondsp()) { - // Next in precond list - belowp->addNextHere(newp); - } else if (belowp == condp()) { + if (belowp == condp()) { // Becomes first statement in body, body may have been empty if (stmtsp()) { stmtsp()->addHereThisAsNext(newp); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 31a8688a1..ae52fad2c 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3352,14 +3352,10 @@ class ConstVisitor final : public VNVisitor { if (m_doNConst) { if (nodep->condp()->isZero()) { UINFO(4, "WHILE(0) => nop " << nodep); - if (nodep->precondsp()) { - nodep->replaceWith(nodep->precondsp()); - } else { - nodep->v3warn(UNUSEDLOOP, - "Loop condition is always false; body will never execute"); - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true); - nodep->unlinkFrBack(); - } + nodep->v3warn(UNUSEDLOOP, + "Loop condition is always false; body will never execute"); + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true); + nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (nodep->condp()->isNeqZero()) { if (!thisWhileHasJumpDelay) { diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 68e98835c..72adcf0a0 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -310,7 +310,6 @@ class CoverageVisitor final : public VNVisitor { { VL_RESTORER(m_inLoopNotBody); m_inLoopNotBody = true; - iterateAndNextNull(nodep->precondsp()); iterateNull(nodep->condp()); iterateAndNextNull(nodep->incsp()); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index b3720f3e7..63d57aaf3 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1014,13 +1014,11 @@ public: } void visit(AstWhile* nodep) override { VL_RESTORER(m_createdScopeHash); - iterateAndNextConstNull(nodep->precondsp()); putns(nodep, "while ("); iterateAndNextConstNull(nodep->condp()); puts(") {\n"); iterateAndNextConstNull(nodep->stmtsp()); iterateAndNextConstNull(nodep->incsp()); - iterateAndNextConstNull(nodep->precondsp()); // Need to recompute before next loop puts("}\n"); } void visit(AstNodeIf* nodep) override { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index d0973120d..ca95d85b5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -365,13 +365,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, "end\n"); } void visit(AstWhile* nodep) override { - iterateAndNextConstNull(nodep->precondsp()); putfs(nodep, "while ("); iterateAndNextConstNull(nodep->condp()); puts(") begin\n"); iterateAndNextConstNull(nodep->stmtsp()); iterateAndNextConstNull(nodep->incsp()); - iterateAndNextConstNull(nodep->precondsp()); // Need to recompute before next loop putfs(nodep, "end\n"); } void visit(AstNodeIf* nodep) override { diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index ee43b621f..6545aaf35 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -129,9 +129,6 @@ class EmitXmlFileVisitor final : public VNVisitorConst { void visit(AstWhile* nodep) override { outputTag(nodep, "while"); puts(">\n"); - puts("\n"); - iterateAndNextConstNull(nodep->precondsp()); - puts("\n"); if (nodep->condp()) { puts("\n"); iterateAndNextConstNull(nodep->condp()); diff --git a/src/V3Life.cpp b/src/V3Life.cpp index d58525ca8..1cdd0c17d 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -401,7 +401,6 @@ class LifeVisitor final : public VNVisitor { LifeBlock* const bodyLifep = new LifeBlock{prevLifep, m_statep}; { m_lifep = condLifep; - iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); } { diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index c49a6cf47..bb17df158 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -54,18 +54,11 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### class LinkIncVisitor final : public VNVisitor { - // TYPES - enum InsertMode : uint8_t { - IM_BEFORE, // Pointing at statement ref is in, insert before this - IM_AFTER, // Pointing at last inserted stmt, insert after - IM_WHILE_PRECOND // Pointing to for loop, add to body end - }; - // STATE AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside AstNodeModule* m_modp = nullptr; // Module we're inside int m_modIncrementsNum = 0; // Var name counter - InsertMode m_insMode = IM_BEFORE; // How to insert + AstWhile* m_inWhileCondp = nullptr; // Inside condition of this while loop AstNode* m_insStmtp = nullptr; // Where to insert statement bool m_unsupportedHere = false; // Used to detect where it's not supported yet @@ -86,23 +79,14 @@ class LinkIncVisitor final : public VNVisitor { m_modp->addStmtsp(newp); } } - void insertNextToStmt(AstNode* nodep, AstNode* newp) { + void insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any if (debug() >= 9) newp->dumpTree("- newstmt: "); - UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement"); - if (m_insMode == IM_BEFORE) { - // Add the whole thing before insertAt - if (debug() >= 9) newp->dumpTree("- newfunc: "); - m_insStmtp->addHereThisAsNext(newp); - } else if (m_insMode == IM_AFTER) { - m_insStmtp->addNextHere(newp); - } else if (m_insMode == IM_WHILE_PRECOND) { - AstWhile* const whilep = VN_AS(m_insStmtp, While); - UASSERT_OBJ(whilep, nodep, "Insert should be under WHILE"); - whilep->addPrecondsp(newp); - } else { - nodep->v3fatalSrc("Unknown InsertMode"); - } + UASSERT_OBJ(m_insStmtp, nodep, "Expression not underneath a statement"); + // In a while condition, the statement also needs to go on the + // back-edge to the loop header, 'incsp' is that place. + if (m_inWhileCondp) m_inWhileCondp->addIncsp(newp->cloneTreePure(true)); + m_insStmtp->addHereThisAsNext(newp); } // VISITORS @@ -120,14 +104,13 @@ class LinkIncVisitor final : public VNVisitor { } void visit(AstWhile* nodep) override { // Special, as statements need to be put in different places - // Preconditions insert first just before themselves (the normal - // rule for other statement types) - m_insStmtp = nullptr; // First thing should be new statement - iterateAndNextNull(nodep->precondsp()); - // Conditions insert first at end of precondsp. - m_insMode = IM_WHILE_PRECOND; m_insStmtp = nodep; - iterateAndNextNull(nodep->condp()); + { + // Conditions insert before the loop and into incsp + VL_RESTORER(m_inWhileCondp); + m_inWhileCondp = nodep; + iterateAndNextNull(nodep->condp()); + } // Body insert just before themselves m_insStmtp = nullptr; // First thing should be new statement iterateAndNextNull(nodep->stmtsp()); @@ -160,7 +143,6 @@ class LinkIncVisitor final : public VNVisitor { m_insStmtp = nullptr; } void visit(AstCaseItem* nodep) override { - m_insMode = IM_BEFORE; { VL_RESTORER(m_unsupportedHere); m_unsupportedHere = true; @@ -193,7 +175,6 @@ class LinkIncVisitor final : public VNVisitor { m_insStmtp = nullptr; } void visit(AstNodeStmt* nodep) override { - m_insMode = IM_BEFORE; m_insStmtp = nodep; iterateChildren(nodep); m_insStmtp = nullptr; // Next thing should be new statement @@ -332,18 +313,17 @@ class LinkIncVisitor final : public VNVisitor { // Immediately after declaration - increment it by one AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp}; - insertNextToStmt(nodep, assignp); // Immediately after incrementing - assign it to the original variable - assignp->addNextHere( - new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}}); + assignp->addNext(new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}}); + insertBeforeStmt(nodep, assignp); } else { // PostAdd/PostSub operations // Assign the original variable to the temporary one AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, readp->cloneTreePure(true)}; - insertNextToStmt(nodep, assignp); // Increment the original variable by one - assignp->addNextHere(new AstAssign{fl, writep, operp}); + assignp->addNext(new AstAssign{fl, writep, operp}); + insertBeforeStmt(nodep, assignp); } // Replace the node with the temporary diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index ae8b6d328..2fbae615d 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -323,7 +323,6 @@ class LinkJumpVisitor final : public VNVisitor { VL_RESTORER(m_loopInc); m_loopp = nodep; m_loopInc = false; - iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); iterateAndNextNull(nodep->stmtsp()); m_loopInc = true; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index d9b5f805f..1883d0929 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -81,7 +81,6 @@ class PremitVisitor final : public VNVisitor { && !constp->num().isString(); // Not a string AstVar* varp = nullptr; - AstAssign* assignp = nullptr; if (useConstPool) { // Extract into constant pool. @@ -96,24 +95,20 @@ class PremitVisitor final : public VNVisitor { m_cfuncp->addInitsp(varp); ++m_temporaryVarsCreated; - // Put assignment before the referencing statement - assignp = new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, nodep}; - if (m_inWhileCondp) { - // Statements that are needed for the 'condition' in a while - // actually have to be put before & after the loop, since we - // can't do any statements in a while's (cond). - m_inWhileCondp->addPrecondsp(assignp); - } else { - m_stmtp->addHereThisAsNext(assignp); - } + // Assignment to put before the referencing statement + AstAssign* const assignp + = new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, nodep}; + // Insert before the statement + m_stmtp->addHereThisAsNext(assignp); + // Statements that are needed for the 'condition' in a while also + // need to be inserted on the back-edge to the loop header. + // 'incsp' is just right palce to do this + if (m_inWhileCondp) m_inWhileCondp->addIncsp(assignp->cloneTree(false)); } // Replace node with VarRef to new Var relinker.relink(new AstVarRef{flp, varp, VAccess::READ}); - // Handle wide expressions inside the expression recursively - if (assignp) iterate(assignp); - // Return the temporary variable return varp; } @@ -186,7 +181,6 @@ class PremitVisitor final : public VNVisitor { UINFO(4, " WHILE " << nodep); // cppcheck-suppress shadowVariable // Also restored below START_STATEMENT_OR_RETURN(nodep); - iterateAndNextNull(nodep->precondsp()); { // cppcheck-suppress shadowVariable // Also restored above VL_RESTORER(m_inWhileCondp); diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index 06818e5cd..d4ef3a5e8 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -201,7 +201,6 @@ private: } } void visit(AstWhile* nodep) override { - unsupportedWriteToVirtIface(nodep->precondsp(), "loop condition"); unsupportedWriteToVirtIface(nodep->condp(), "loop condition"); unsupportedWriteToVirtIface(nodep->incsp(), "loop increment statement"); { diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 106a83ca9..9baaec0f4 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -1043,8 +1043,6 @@ private: int loops = 0; while (true) { UINFO(5, " WHILE-ITER " << nodep); - iterateAndNextConstNull(nodep->precondsp()); - if (jumpingOver(nodep)) break; iterateAndNextConstNull(nodep->condp()); if (jumpingOver(nodep)) break; if (!optimizable()) break; diff --git a/src/V3Task.cpp b/src/V3Task.cpp index d569ad780..c66dcb463 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1588,11 +1588,8 @@ class TaskVisitor final : public VNVisitor { } void visit(AstWhile* nodep) override { // Special, as statements need to be put in different places - // Preconditions insert first just before themselves (the normal - // rule for other statement types) - m_insStmtp = nullptr; // First thing should be new statement - iterateAndNextNull(nodep->precondsp()); // Conditions insert first at end of precondsp. + // TODO: is this right? This is how it used to be. m_insStmtp = nodep; iterateAndNextNull(nodep->condp()); // Body insert just before themselves diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 6b4332beb..06d931db5 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -82,13 +82,12 @@ class UnrollVisitor final : public VNVisitor { AstNode* const nodep, const VOptionBool& unrollFull, // Pragma unroll_full, unroll_disable AstNode* const initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) - AstNode* const precondsp, AstNode* condp, + AstNode* condp, AstNode* const incp, // Maybe under nodep or in bodysp AstNode* bodysp) { // To keep the IF levels low, we return as each test fails. UINFO(4, " FOR Check " << nodep); if (initp) UINFO(6, " Init " << initp); - if (precondsp) UINFO(6, " Pcon " << precondsp); if (condp) UINFO(6, " Cond " << condp); if (incp) UINFO(6, " Inc " << incp); @@ -136,7 +135,6 @@ class UnrollVisitor final : public VNVisitor { m_varAssignHit = false; m_forkHit = false; m_ignoreIncp = incp; - iterateAndNextNull(precondsp); iterateAndNextNull(bodysp); iterateAndNextNull(incp); m_varModeCheck = false; @@ -172,15 +170,14 @@ class UnrollVisitor final : public VNVisitor { int bodySize = 0; int bodyLimit = v3Global.opt.unrollStmts(); if (loops > 0) bodyLimit = v3Global.opt.unrollStmts() / loops; - if (bodySizeOverRecurse(precondsp, bodySize /*ref*/, bodyLimit) - || bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit) + if (bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit) || bodySizeOverRecurse(incp, bodySize /*ref*/, bodyLimit)) { return cantUnroll(nodep, "too many statements"); } } } // Finally, we can do it - if (!forUnroller(nodep, unrollFull, initAssp, condp, precondsp, incp, bodysp)) { + if (!forUnroller(nodep, unrollFull, initAssp, condp, incp, bodysp)) { return cantUnroll(nodep, "Unable to unroll loop"); } VL_DANGLING(nodep); @@ -268,7 +265,7 @@ class UnrollVisitor final : public VNVisitor { } bool forUnroller(AstNode* nodep, const VOptionBool& unrollFull, AstAssign* initp, - AstNode* condp, AstNode* precondsp, AstNode* incp, AstNode* bodysp) { + AstNode* condp, AstNode* incp, AstNode* bodysp) { UINFO(9, "forUnroller " << nodep); V3Number loopValue{nodep}; if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { // @@ -280,10 +277,6 @@ class UnrollVisitor final : public VNVisitor { // Don't add to list, we do it once, and setting loop index isn't // needed if we have > 1 loop, as we're constant propagating it } - if (precondsp) { - precondsp->unlinkFrBackWithNext(); - stmtsp = AstNode::addNext(stmtsp, precondsp); - } if (bodysp) { bodysp->unlinkFrBackWithNext(); stmtsp = AstNode::addNext(stmtsp, bodysp); // Maybe null if no body @@ -374,7 +367,6 @@ class UnrollVisitor final : public VNVisitor { nodep->unlinkFrBack(); } if (bodysp) VL_DO_DANGLING(pushDeletep(bodysp), bodysp); - if (precondsp) VL_DO_DANGLING(pushDeletep(precondsp), precondsp); if (initp) VL_DO_DANGLING(pushDeletep(initp), initp); if (incp && !incp->backp()) VL_DO_DANGLING(pushDeletep(incp), incp); if (debug() >= 9 && newbodysp) newbodysp->dumpTree("- _new: "); @@ -386,9 +378,6 @@ class UnrollVisitor final : public VNVisitor { if (m_varModeCheck || m_varModeReplace) { } else { // Constify before unroll call, as it may change what is underneath. - if (nodep->precondsp()) { - V3Const::constifyEdit(nodep->precondsp()); // precondsp may change - } if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change // Grab initial value AstNode* initp = nullptr; // Should be statement before the while. @@ -411,8 +400,7 @@ class UnrollVisitor final : public VNVisitor { if (incp == stmtsp) stmtsp = nullptr; } // And check it - if (forUnrollCheck(nodep, nodep->unrollFull(), initp, nodep->precondsp(), - nodep->condp(), incp, stmtsp)) { + if (forUnrollCheck(nodep, nodep->unrollFull(), initp, nodep->condp(), incp, stmtsp)) { VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement } } @@ -436,8 +424,8 @@ class UnrollVisitor final : public VNVisitor { // condition, but they'll become while's which can be // deleted by V3Const. VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - } else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nullptr, - nodep->condp(), nodep->incsp(), nodep->stmtsp())) { + } else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nodep->condp(), + nodep->incsp(), nodep->stmtsp())) { VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement } else { nodep->v3error("For loop doesn't have genvar index, or is malformed"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 20903fede..ff6de8dee 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5189,7 +5189,6 @@ class WidthVisitor final : public VNVisitor { } void visit(AstWhile* nodep) override { assertAtStatement(nodep); - userIterateAndNext(nodep->precondsp(), nullptr); iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH); // it's like an if() condition. userIterateAndNext(nodep->stmtsp(), nullptr); diff --git a/src/verilog.y b/src/verilog.y index 0b57eeab1..834b79b96 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3061,7 +3061,6 @@ genvar_iteration: // ==IEEE: genvar_iteration | varRefBase yP_SSRIGHTEQ expr { $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTreePure(true), $3}}; } // // inc_or_dec_operator - // When support ++ as a real AST type, maybe AstWhile::precondsp() becomes generic AstNodeExprStmt? | yP_PLUSPLUS varRefBase { $$ = new AstAssign{$1, $2, new AstAdd{$1, $2->cloneTreePure(true), new AstConst{$1, AstConst::StringToParse{}, "'b1"}}}; } diff --git a/test_regress/t/t_interface_virtual_unsup.out b/test_regress/t/t_interface_virtual_unsup.out index aec39ddb7..028c515d1 100644 --- a/test_regress/t/t_interface_virtual_unsup.out +++ b/test_regress/t/t_interface_virtual_unsup.out @@ -11,7 +11,4 @@ %Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:66: Unsupported: write to virtual interface in loop increment statement 24 | for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data))); | ^~~ -%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:25:34: Unsupported: write to virtual interface in loop condition - 25 | for (int i = 0; write_data(vif.data++); i++); - | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_json_only_debugcheck.out b/test_regress/t/t_json_only_debugcheck.out index dd7f9d6b7..8c36a21da 100644 --- a/test_regress/t/t_json_only_debugcheck.out +++ b/test_regress/t/t_json_only_debugcheck.out @@ -879,7 +879,7 @@ "lhsp": [ {"type":"VARREF","name":"t.unnamedblk1.e","addr":"(LN)","loc":"d,52:17,52:18","dtypep":"(AC)","access":"WR","varp":"(UB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"WHILE","name":"","addr":"(MN)","loc":"d,52:7,52:10","precondsp": [], + {"type":"WHILE","name":"","addr":"(MN)","loc":"d,52:7,52:10", "condp": [ {"type":"NEQ","name":"","addr":"(NN)","loc":"d,52:32,52:34","dtypep":"(FB)", "lhsp": [ @@ -2635,7 +2635,7 @@ "lhsp": [ {"type":"VARREF","name":"__VnbaContinue","addr":"(JMB)","loc":"d,11:8,11:9","dtypep":"(FB)","access":"WR","varp":"(DMB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"WHILE","name":"","addr":"(KMB)","loc":"a,0:0,0:0","precondsp": [], + {"type":"WHILE","name":"","addr":"(KMB)","loc":"a,0:0,0:0", "condp": [ {"type":"VARREF","name":"__VnbaContinue","addr":"(LMB)","loc":"a,0:0,0:0","dtypep":"(FB)","access":"RD","varp":"(DMB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], @@ -2700,7 +2700,7 @@ "lhsp": [ {"type":"VARREF","name":"__VactContinue","addr":"(LNB)","loc":"d,11:8,11:9","dtypep":"(FB)","access":"WR","varp":"(O)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"WHILE","name":"","addr":"(MNB)","loc":"a,0:0,0:0","precondsp": [], + {"type":"WHILE","name":"","addr":"(MNB)","loc":"a,0:0,0:0", "condp": [ {"type":"VARREF","name":"__VactContinue","addr":"(NNB)","loc":"a,0:0,0:0","dtypep":"(FB)","access":"RD","varp":"(O)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], diff --git a/test_regress/t/t_json_only_flat_vlvbound.out b/test_regress/t/t_json_only_flat_vlvbound.out index 906354bc8..9d1451e4e 100644 --- a/test_regress/t/t_json_only_flat_vlvbound.out +++ b/test_regress/t/t_json_only_flat_vlvbound.out @@ -85,7 +85,7 @@ "lhsp": [ {"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(OC)","loc":"d,18:10,18:11","dtypep":"(GB)","access":"WR","varp":"(HB)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"WHILE","name":"","addr":"(PC)","loc":"d,18:5,18:8","precondsp": [], + {"type":"WHILE","name":"","addr":"(PC)","loc":"d,18:5,18:8", "condp": [ {"type":"GTS","name":"","addr":"(QC)","loc":"d,18:18,18:19","dtypep":"(RC)", "lhsp": [ @@ -195,7 +195,7 @@ "lhsp": [ {"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(JE)","loc":"d,18:10,18:11","dtypep":"(GB)","access":"WR","varp":"(PB)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"WHILE","name":"","addr":"(KE)","loc":"d,18:5,18:8","precondsp": [], + {"type":"WHILE","name":"","addr":"(KE)","loc":"d,18:5,18:8", "condp": [ {"type":"GTS","name":"","addr":"(LE)","loc":"d,18:18,18:19","dtypep":"(RC)", "lhsp": [ diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index bbe0ef27b..85640c38d 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -529,8 +529,6 @@ - - @@ -1595,8 +1593,6 @@ - - @@ -1640,8 +1636,6 @@ - - diff --git a/test_regress/t/t_xml_flat_vlvbound.out b/test_regress/t/t_xml_flat_vlvbound.out index 9361e33ee..d0aaf98a7 100644 --- a/test_regress/t/t_xml_flat_vlvbound.out +++ b/test_regress/t/t_xml_flat_vlvbound.out @@ -74,8 +74,6 @@ - - @@ -142,8 +140,6 @@ - - From 7c5d46256428ed4638fa99ea0d113f05cc531ac0 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 23 Jul 2025 17:51:16 +0100 Subject: [PATCH 061/252] Remove AstJumpLabel (#6221) Remove AstJumpLabel AstJumpGo now references one if its enclosing AstJumpBlocks, and branches straight after the referenced block. That is: ``` JumpBlock a { ... JumpGo(a); ... } // <--- the JumpGo(a) goes here ``` This is sufficient for all use cases and makes control flow much easier to reason about. As a result, V3Const can optimize a bit more aggressively. Second half of, and fixes #6216 --- src/V3Ast.h | 2 - src/V3AstNodeOther.h | 54 ++++------------ src/V3AstNodes.cpp | 25 ++------ src/V3Const.cpp | 79 ++++++++--------------- src/V3EmitCFunc.h | 24 ++++--- src/V3EmitV.cpp | 19 ++++-- src/V3Hasher.cpp | 4 +- src/V3LinkJump.cpp | 104 +++++++++++++++---------------- src/V3Simulate.h | 75 ++++++++++------------ test_regress/t/t_debug_emitv.out | 14 ++--- 10 files changed, 164 insertions(+), 236 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index a7161e2a1..910ad9697 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2194,8 +2194,6 @@ public: // Used by AstNode::broken() bool brokeExists() const { return V3Broken::isLinkable(this); } bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } - bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); } - // Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well // CONSTRUCTORS virtual ~AstNode() = default; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index b46c18028..549e304eb 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3279,14 +3279,13 @@ public: bool isDelayed() const { return m_delayed; } }; class AstJumpBlock final : public AstNodeStmt { - // Block of code including a single JumpLabel, and 0+ JumpGo's to that label + // Block of code that might contain AstJumpGo statements as children, + // which when exectued branch to right after the referenced AstJumpBlock. + // AstJumpBlocks can nest, and an AstJumpGo can reference any of the + // enclosing AstJumpBlocks (can break out of mulitple levels). // Parents: {statement list} - // Children: {statement list, with JumpGo and JumpLabel below} + // Children: {statement list, with JumpGo below} // @astgen op1 := stmtsp : List[AstNode] - // @astgen op2 := endStmtsp : List[AstNode] - // - // @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration - int m_labelNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment VIsCached m_purity; // Pure state public: // After construction must call ->labelp to associate with appropriate label @@ -3295,66 +3294,35 @@ public: addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstJumpBlock; - const char* broken() const override; int instrCount() const override { return 0; } bool maybePointedTo() const override VL_MT_SAFE { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - int labelNum() const { return m_labelNum; } - void labelNum(int flag) { m_labelNum = flag; } - AstJumpLabel* labelp() const { return m_labelp; } - void labelp(AstJumpLabel* labelp) { m_labelp = labelp; } bool isPure() override; private: bool getPurityRecurse() const; }; class AstJumpGo final : public AstNodeStmt { - // Jump point; branch down to a JumpLabel - // No support for backward jumps at present - // Parents: {statement list with JumpBlock above} + // Branch to right after the referenced encloding AstJumpBlock + // Parents: statement, including the referenced AstJumpBlock // Children: none // - // @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration + // @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after public: - AstJumpGo(FileLine* fl, AstJumpLabel* labelp) + AstJumpGo(FileLine* fl, AstJumpBlock* blockp) : ASTGEN_SUPER_JumpGo(fl) - , m_labelp{labelp} {} + , m_blockp{blockp} {} ASTGEN_MEMBERS_AstJumpGo; const char* broken() const override; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* samep) const override { - return labelp() == VN_DBG_AS(samep, JumpGo)->labelp(); + return blockp() == VN_DBG_AS(samep, JumpGo)->blockp(); } bool isGateOptimizable() const override { return false; } bool isBrancher() const override { return true; // SPECIAL: We don't process code after breaks } - AstJumpLabel* labelp() const { return m_labelp; } -}; -class AstJumpLabel final : public AstNodeStmt { - // Jump point declaration - // Parents: {statement list with JumpBlock above} - // Children: none - // @astgen ptr := m_blockp : AstJumpBlock // [After V3Jump] Pointer to declaration -public: - AstJumpLabel(FileLine* fl, AstJumpBlock* blockp) - : ASTGEN_SUPER_JumpLabel(fl) - , m_blockp{blockp} {} - ASTGEN_MEMBERS_AstJumpLabel; - bool maybePointedTo() const override VL_MT_SAFE { return true; } - const char* broken() const override { - BROKEN_RTN(!blockp()->brokeExistsAbove()); - BROKEN_RTN(blockp()->labelp() != this); - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 0; } - bool sameNode(const AstNode* samep) const override { - return blockp() == VN_DBG_AS(samep, JumpLabel)->blockp(); - } AstJumpBlock* blockp() const { return m_blockp; } }; class AstMonitorOff final : public AstNodeStmt { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 8ed5b6d2e..7cfc48210 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1139,10 +1139,6 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { return nodep; } -const char* AstJumpBlock::broken() const { - BROKEN_RTN(!labelp()->brokeExistsBelow()); - return nullptr; -} bool AstJumpBlock::isPure() { if (!m_purity.isCached()) m_purity.set(getPurityRecurse()); return m_purity.get(); @@ -1977,21 +1973,6 @@ AstNodeExpr* AstInitArray::getIndexDefaultedValuep(uint64_t index) const { } void AstJumpGo::dump(std::ostream& str) const { - this->AstNodeStmt::dump(str); - str << " -> "; - if (labelp()) { - labelp()->dump(str); - } else { - str << "%E:UNLINKED"; - } -} -void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); } -const char* AstJumpGo::broken() const { - BROKEN_RTN(!labelp()->brokeExistsBelow()); - return nullptr; -} - -void AstJumpLabel::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); str << " -> "; if (blockp()) { @@ -2000,7 +1981,11 @@ void AstJumpLabel::dump(std::ostream& str) const { str << "%E:UNLINKED"; } } -void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +const char* AstJumpGo::broken() const { + BROKEN_RTN(!blockp()->brokeExistsAbove()); + return nullptr; +} void AstMemberDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ae52fad2c..c8d54a8c9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -38,6 +38,7 @@ #include #include #include +#include VL_DEFINE_DEBUG_FUNCTIONS; @@ -912,7 +913,6 @@ class ConstVisitor final : public VNVisitor { // ** only when m_warn/m_doExpensive is set. If state is needed other times, // ** must track down everywhere V3Const is called and make sure no overlaps. // AstVar::user4p -> Used by variable marking/finding - // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label // AstEnum::user4 -> bool. Recursing. // STATE @@ -938,6 +938,7 @@ class ConstVisitor final : public VNVisitor { static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass V3UniqueNames m_concswapNames; // For generating unique temporary variable names std::map m_containsMemberAccess; // Caches results of matchBiopToBitwise + std::unordered_set m_usedJumpBlocks; // JumpBlocks used by some JumpGo // METHODS @@ -2303,19 +2304,6 @@ class ConstVisitor final : public VNVisitor { } return false; } - bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) { - // If JumpGo has an upper JumpBlock that is to same label, then - // code will by normal sequential operation do the JUMPGO and it - // can be removed. - if (nodep->nextp()) return false; // Label jumps other statements - AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock); - if (!aboveBlockp) return false; - if (aboveBlockp != nodep->labelp()->blockp()) return false; - if (aboveBlockp->endStmtsp() != nodep->labelp()) return false; - UINFO(4, "JUMPGO => last remove " << nodep); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return true; - } // Boolean replacements bool operandBoolShift(const AstNode* nodep) { @@ -3402,52 +3390,39 @@ class ConstVisitor final : public VNVisitor { // Jump elimination void visit(AstJumpGo* nodep) override { - iterateChildren(nodep); - // Jump to label where label immediately follows this JumpGo is not useful - if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) { - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - // Keep the label, might be other jumps pointing to it, gets cleaned later - return; - } - if (m_doExpensive) { - // Any non-label statements (at this statement level) can never execute - while (nodep->nextp() && !VN_IS(nodep->nextp(), JumpLabel)) { - pushDeletep(nodep->nextp()->unlinkFrBack()); + // Any statements following the JumpGo (at this statement level) never execute, delete + if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext()); + + // JumpGo as last statement in target JumpBlock (including last in a last sub-list), + // is a no-op, remove it. + for (AstNode* abovep = nodep->abovep(); abovep; abovep = abovep->abovep()) { + if (abovep == nodep->blockp()) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; } - // If last statement in a jump label we have JumpLabel(...., JumpGo) - // Often caused by "return" in a Verilog function. The Go is pointless, remove. - if (replaceJumpGoNext(nodep, nodep->abovep())) return; - // Also optimize If with a then or else's final statement being this JumpGo - // We only do single ifs... Ideally we'd look at control flow and delete any - // Jumps where any following control flow point is the label - if (!nodep->nextp()) { - if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) { - if (!aboveIfp->nextp()) { - if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return; - } - } - } - nodep->labelp()->blockp()->user4(true); + // Stop if not doing expensive, or if the above node is not the last in its list, + // ... or if it's not an 'if' TODO: it would be enough if it was not a branch. + if (!m_doExpensive || abovep->nextp() || !VN_IS(abovep, If)) break; } + // Mark JumpBlock as used + m_usedJumpBlocks.emplace(nodep->blockp()); m_hasJumpDelay = true; } void visit(AstJumpBlock* nodep) override { - // Because JumpLabels disable many optimizations, - // remove JumpLabels that are not pointed to by any AstJumpGos - // Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this iterateChildren(nodep); - // AstJumpGo's below here that point to this node will set user4 - if (m_doExpensive && !nodep->user4()) { + + // Remove if empty + if (!nodep->stmtsp()) { + UINFO(4, "JUMPLABEL => empty " << nodep); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + + // If no JumpGo points to this node, replace it with its body + if (!m_usedJumpBlocks.count(nodep)) { UINFO(4, "JUMPLABEL => unused " << nodep); - AstNode* underp = nullptr; - if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext(); - if (underp) { - nodep->replaceWith(underp); - } else { - nodep->unlinkFrBack(); - } - pushDeletep(nodep->labelp()->unlinkFrBack()); + nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext()); VL_DO_DANGLING(pushDeletep(nodep), nodep); } } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 63d57aaf3..348e2f558 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -117,7 +118,7 @@ public: class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { VMemberMap m_memberMap; AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting - int m_labelNum = 0; // Next label number + std::unordered_map m_labelNumbers; // Label numbers for JumpBlocks bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr bool m_emitConstInit = false; // Emitting constant initializer bool m_createdScopeHash = false; // Already created a scope hash @@ -325,6 +326,7 @@ public: VL_RESTORER(m_createdScopeHash); m_cfuncp = nodep; m_instantiatesOwnProcess = false; + m_labelNumbers.clear(); // No need to save/restore, all Jumps must be within the function splitSizeInc(nodep); @@ -993,25 +995,29 @@ public: puts(";\n"); } void visit(AstJumpBlock* nodep) override { - nodep->labelNum(++m_labelNum); + // Allocate label number + const size_t n = m_labelNumbers.size(); + const bool newEntry = m_labelNumbers.emplace(nodep, n).second; + UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide"); + // Emit putns(nodep, "{\n"); // Make it visually obvious label jumps outside these VL_RESTORER(m_createdScopeHash); iterateAndNextConstNull(nodep->stmtsp()); - iterateAndNextConstNull(nodep->endStmtsp()); + puts("__Vlabel" + std::to_string(n) + ": ;\n"); puts("}\n"); } + void visit(AstJumpGo* nodep) override { + // Retrieve target label number - must already exist (from enclosing AstJumpBlock) + const size_t n = m_labelNumbers.at(nodep->blockp()); + // Emit + putns(nodep, "goto __Vlabel" + std::to_string(n) + ";\n"); + } void visit(AstCLocalScope* nodep) override { putns(nodep, "{\n"); VL_RESTORER(m_createdScopeHash); iterateAndNextConstNull(nodep->stmtsp()); puts("}\n"); } - void visit(AstJumpGo* nodep) override { - putns(nodep, "goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n"); - } - void visit(AstJumpLabel* nodep) override { - putns(nodep, "__Vlabel" + cvtToStr(nodep->blockp()->labelNum()) + ": ;\n"); - } void visit(AstWhile* nodep) override { VL_RESTORER(m_createdScopeHash); putns(nodep, "while ("); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index ca95d85b5..be6addce6 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -20,6 +20,7 @@ #include "V3EmitCBase.h" +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -37,6 +38,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_arrayPost = false; // Print array information that goes after identifier (vs after) std::deque m_packedps; // Packed arrays to print with BasicDType + std::unordered_map m_labelNumbers; // Label numbers for JumpBlocks // METHODS virtual void puts(const string& str) = 0; @@ -308,14 +310,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(");\n"); } void visit(AstJumpBlock* nodep) override { - putbs("begin : label" + cvtToStr(nodep->labelNum()) + "\n"); - if (nodep->stmtsp()) iterateAndNextConstNull(nodep->stmtsp()); + // Allocate label number + const size_t n = m_labelNumbers.size(); + const bool newEntry = m_labelNumbers.emplace(nodep, n).second; + UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide"); + // Emit + putbs("begin : label" + std::to_string(n) + "\n"); + iterateAndNextConstNull(nodep->stmtsp()); puts("end\n"); } void visit(AstJumpGo* nodep) override { - putbs("disable label" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n"); + // Retrieve target label number - Sometimes EmitV is used by debug code, + // so allow printing with an unknown target + const auto it = m_labelNumbers.find(nodep->blockp()); + const std::string label + = it != m_labelNumbers.end() ? "label" + std::to_string(it->second) : ""; + putbs("disable " + label + ";\n"); } - void visit(AstJumpLabel* nodep) override { putbs("// " + cvtToStr(nodep->blockp()) + ":\n"); } void visit(AstNodeReadWriteMem* nodep) override { putfs(nodep, nodep->verilogKwd()); putbs("("); diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index c9561cc2f..7608bbaed 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -320,9 +320,7 @@ class HasherVisitor final : public VNVisitorConst { }); } void visit(AstJumpGo* nodep) override { - m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // - iterateConstNull(nodep->labelp()); - }); + m_hash += hashNodeAndIterate(nodep, false, false, []() {}); } void visit(AstTraceInc* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 2fbae615d..e572b87fb 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -45,8 +45,8 @@ VL_DEFINE_DEBUG_FUNCTIONS; class LinkJumpVisitor final : public VNVisitor { // NODE STATE - // AstNode::user1() -> AstJumpLabel*, for this block if endOfIter - // AstNode::user2() -> AstJumpLabel*, for this block if !endOfIter + // AstNode::user1() -> AstJumpBlock*, for body of this loop + // AstNode::user2() -> AstJumpBlock*, for this block // AstNodeBlock::user3() -> bool, true if contains a fork const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; @@ -65,36 +65,34 @@ class LinkJumpVisitor final : public VNVisitor { "__VprocessQueue"}; // Names for queues needed for 'disable' handling // METHODS - AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { - // Put label under given node, and if WHILE optionally at end of iteration - UINFO(4, "Create label for " << nodep); - if (VN_IS(nodep, JumpLabel)) return VN_AS(nodep, JumpLabel); // Done + // Get (and create if necessary) the JumpBlock for this statement + AstJumpBlock* getJumpBlock(AstNode* nodep, bool endOfIter) { + // Wrap 'nodep' in JumpBlock. If loop, wrap the body instead if endOfIter is true + UINFO(4, "Create JumpBlock for " << nodep); // Made it previously? We always jump to the end, so this works out if (endOfIter) { - if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpLabel); + if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpBlock); } else { - if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpLabel); + if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpBlock); } AstNode* underp = nullptr; bool under_and_next = true; - if (VN_IS(nodep, NodeBlock)) { - underp = VN_AS(nodep, NodeBlock)->stmtsp(); - } else if (VN_IS(nodep, NodeFTask)) { - underp = VN_AS(nodep, NodeFTask)->stmtsp(); - } else if (VN_IS(nodep, Foreach)) { + if (AstNodeBlock* const blockp = VN_CAST(nodep, NodeBlock)) { + underp = blockp->stmtsp(); + } else if (AstNodeFTask* const fTaskp = VN_CAST(nodep, NodeFTask)) { + underp = fTaskp->stmtsp(); + } else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) { if (endOfIter) { - underp = VN_AS(nodep, Foreach)->stmtsp(); + underp = foreachp->stmtsp(); } else { underp = nodep; under_and_next = false; // IE we skip the entire foreach } - } else if (VN_IS(nodep, While)) { + } else if (AstWhile* const whilep = VN_CAST(nodep, While)) { if (endOfIter) { - // Note we jump to end of bodysp; a FOR loop has its - // increment under incsp() which we don't skip - underp = VN_AS(nodep, While)->stmtsp(); + underp = whilep->stmtsp(); } else { underp = nodep; under_and_next = false; // IE we skip the entire while @@ -118,36 +116,32 @@ class LinkJumpVisitor final : public VNVisitor { UASSERT_OBJ(underp, nodep, "Break/disable/continue not under expected statement"); UINFO(5, " Underpoint is " << underp); - if (VN_IS(underp, JumpLabel)) { - return VN_AS(underp, JumpLabel); - } else { // Move underp stuff to be under a new label - AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), nullptr}; - AstJumpLabel* const labelp = new AstJumpLabel{nodep->fileline(), blockp}; - blockp->labelp(labelp); - - VNRelinker repHandle; - if (under_and_next) { - underp->unlinkFrBackWithNext(&repHandle); - } else { - underp->unlinkFrBack(&repHandle); - } - repHandle.relink(blockp); - - blockp->addStmtsp(underp); - // Keep any AstVars under the function not under the new JumpLabel - for (AstNode *nextp, *varp = underp; varp; varp = nextp) { - nextp = varp->nextp(); - if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack()); - } - // Label goes last - blockp->addEndStmtsp(labelp); - if (endOfIter) { - nodep->user1p(labelp); - } else { - nodep->user2p(labelp); - } - return labelp; + // If already wrapped, we are done ... + if (!underp->nextp() || !under_and_next) { + if (AstJumpBlock* const blockp = VN_CAST(underp, JumpBlock)) return blockp; } + + // Move underp stuff to be under a new AstJumpBlock + VNRelinker repHandle; + if (under_and_next) { + underp->unlinkFrBackWithNext(&repHandle); + } else { + underp->unlinkFrBack(&repHandle); + } + AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), underp}; + if (endOfIter) { + nodep->user1p(blockp); + } else { + nodep->user2p(blockp); + } + repHandle.relink(blockp); + + // Keep any AstVars under the function not under the new JumpLabel + for (AstNode *nextp, *varp = underp; varp; varp = nextp) { + nextp = varp->nextp(); + if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack()); + } + return blockp; } void addPrefixToBlocksRecurse(const std::string& prefix, AstNode* const nodep) { // Add a prefix to blocks @@ -379,8 +373,8 @@ class LinkJumpVisitor final : public VNVisitor { nodep->lhsp()->unlinkFrBackWithNext()}); } // Jump to the end of the function call - AstJumpLabel* const labelp = findAddLabel(m_ftaskp, false); - nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_ftaskp, false); + nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -391,8 +385,8 @@ class LinkJumpVisitor final : public VNVisitor { nodep->v3error("break isn't underneath a loop"); } else { // Jump to the end of the loop - AstJumpLabel* const labelp = findAddLabel(m_loopp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_loopp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -404,8 +398,8 @@ class LinkJumpVisitor final : public VNVisitor { } else { // Jump to the end of this iteration // If a "for" loop then need to still do the post-loop increment - AstJumpLabel* const labelp = findAddLabel(m_loopp, true); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_loopp, true); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -444,8 +438,8 @@ class LinkJumpVisitor final : public VNVisitor { "Unsupported: disabling block that contains a fork"); } else { // Jump to the end of the named block - AstJumpLabel* const labelp = findAddLabel(beginp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(beginp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } } else { nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 9baaec0f4..59f2614b6 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -397,10 +397,9 @@ private: UASSERT_OBJ(vscp, nodep, "Not linked"); return vscp; } - bool jumpingOver(const AstNode* nodep) const { - // True to jump over this node - all visitors must call this up front - return (m_jumpp && m_jumpp->labelp() != nodep); - } + + // True if current node might be jumped over - all visitors must call this up front + bool jumpingOver() const { return m_jumpp; } void assignOutValue(AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) { if (VN_IS(nodep, AssignDly)) { // Don't do setValue, as value isn't yet visible to following statements @@ -413,7 +412,7 @@ private: // VISITORS void visit(AstAlways* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } @@ -421,7 +420,7 @@ private: // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } void visit(AstVarRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate UASSERT_OBJ(nodep->varp(), nodep, "Unlinked"); iterateChildrenConst(nodep->varp()); @@ -490,7 +489,7 @@ private: } } void visit(AstVarXRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (m_scoped) { badNodeType(nodep); return; @@ -500,7 +499,7 @@ private: } } void visit(AstNodeFTask* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -520,7 +519,7 @@ private: iterateChildrenConst(nodep); } void visit(AstInitialStatic* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -529,7 +528,7 @@ private: iterateChildrenConst(nodep); } void visit(AstNodeIf* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " IF " << nodep); checkNodeInfo(nodep); if (m_checkOnly) { @@ -856,7 +855,7 @@ private: } } void visit(AstNodeAssign* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); @@ -900,7 +899,7 @@ private: iterateChildrenConst(nodep); } void visit(AstNodeCase* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " CASE " << nodep); checkNodeInfo(nodep); if (m_checkOnly) { @@ -939,7 +938,7 @@ private: void visit(AstCaseItem* nodep) override { // Real handling is in AstNodeCase - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } @@ -947,12 +946,12 @@ private: void visit(AstComment*) override {} void visit(AstStmtExpr* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } void visit(AstExprStmt* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateAndNextConstNull(nodep->stmtsp()); if (!optimizable()) return; @@ -962,30 +961,24 @@ private: } void visit(AstJumpBlock* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; iterateChildrenConst(nodep); + if (m_jumpp && m_jumpp->blockp() == nodep) { + UINFO(5, " JUMP DONE " << nodep); + m_jumpp = nullptr; + } } void visit(AstJumpGo* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); if (!m_checkOnly) { UINFO(5, " JUMP GO " << nodep); m_jumpp = nodep; } } - void visit(AstJumpLabel* nodep) override { - // This only supports forward jumps. That's all we make at present, - // AstJumpGo::broken uses brokeExistsBelow() to check this. - if (jumpingOver(nodep)) return; - checkNodeInfo(nodep); - iterateChildrenConst(nodep); - if (m_jumpp && m_jumpp->labelp() == nodep) { - UINFO(5, " JUMP DONE " << nodep); - m_jumpp = nullptr; - } - } + void visit(AstStop* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (m_params) { // This message seems better than an obscure $stop // The spec says $stop is just ignored, it seems evil to ignore assertions clearOptimizable( @@ -1030,7 +1023,7 @@ private: void visit(AstWhile* nodep) override { // Doing lots of Whiles is slow, so only for parameters - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " WHILE " << nodep); if (!m_params) { badNodeType(nodep); @@ -1044,15 +1037,15 @@ private: while (true) { UINFO(5, " WHILE-ITER " << nodep); iterateAndNextConstNull(nodep->condp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; if (!optimizable()) break; if (!fetchConst(nodep->condp())->num().isNeqZero()) { // break; } iterateAndNextConstNull(nodep->stmtsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; iterateAndNextConstNull(nodep->incsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; // Prep for next loop if (loops++ @@ -1068,7 +1061,7 @@ private: } void visit(AstFuncRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate UINFO(5, " FUNCREF " << nodep); checkNodeInfo(nodep); @@ -1140,7 +1133,7 @@ private: } void visit(AstVar* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -1148,12 +1141,12 @@ private: } void visit(AstScopeName* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; // Ignore } void visit(AstSFormatF* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); iterateChildrenConst(nodep); @@ -1214,7 +1207,7 @@ private: } void visit(AstDisplay* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate // We ignore isPredictOptimizable as $display is often in constant // functions and we want them to work if used with parameters @@ -1242,11 +1235,11 @@ private: // Some CMethods such as size() on queues could be supported, but // instead we should change those methods to new Ast types so we can // properly dispatch them - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; knownBadNodeType(nodep); } void visit(AstMemberSel* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; knownBadNodeType(nodep); } // ==== @@ -1255,7 +1248,7 @@ private: // AstCoverInc, AstFinish, // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt void visit(AstNode* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; badNodeType(nodep); } diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index dfb607cbe..03c910a7f 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -116,10 +116,10 @@ module Vt_debug_emitv_t; endtask function f; input int signed v; - begin : label0 + begin : label1 $display("stmt"); f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1)); - disable label0; + disable label1; end endfunction initial begin @@ -203,12 +203,12 @@ module Vt_debug_emitv_t; begin : unnamedblk3 int signed i; i = 'sh0; - begin : label0 + begin : label2 while ((i < cyc)) begin begin sum = (sum + i); if ((sum > 'sha)) begin - disable label0; + disable label2; end else begin sum = (sum + 'sh1); @@ -390,13 +390,13 @@ module Vt_debug_emitv_sub; endtask function f; input int signed v; - begin : label0 + begin : label3 if ((v == 'sh0)) begin f = 'sh21; - disable label0; + disable label3; end f = ({32'h1{{31'h0, v[2]}}} + 32'h1); - disable label0; + disable label3; end endfunction real r; From 393f0e4acbbeea543d395a464288e533ecf54c72 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 22 Jul 2025 03:15:40 -0400 Subject: [PATCH 062/252] Tests: Example format --- test_regress/t/t_EXAMPLE.v | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index c7a315515..75659496a 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -16,19 +16,21 @@ // any use, without warranty, 2025 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off `define stop $stop `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); `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); +// verilog_format: on -module t(/*AUTOARG*/ +module t ( /*AUTOARG*/ // Inputs clk ); input clk; - int cyc; - reg [63:0] crc; - reg [63:0] sum; + int cyc; + reg [63:0] crc; + reg [63:0] sum; // Take CRC data and apply to testblock inputs wire [31:0] in = crc[31:0]; @@ -38,12 +40,12 @@ module t(/*AUTOARG*/ wire [31:0] out; // From test of Test.v // End of automatics - Test test(/*AUTOINST*/ - // Outputs - .out (out[31:0]), - // Inputs - .clk (clk), - .in (in[31:0])); + Test test ( /*AUTOINST*/ + // Outputs + .out (out[31:0]), + // Inputs + .clk (clk), + .in (in[31:0])); // Aggregate outputs into a single result vector wire [63:0] result = {32'h0, out}; @@ -78,7 +80,7 @@ module t(/*AUTOARG*/ endmodule -module Test(/*AUTOARG*/ +module Test ( /*AUTOARG*/ // Outputs out, // Inputs From 050e5ddb5b0d9d729257313d01755cde241dbf01 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 23 Jul 2025 17:17:35 -0400 Subject: [PATCH 063/252] Fix internal error after bad method --- src/V3Options.cpp | 2 +- src/V3Timing.cpp | 2 +- src/V3Width.cpp | 2 +- .../t/t_covergroup_coverpoints_unsup.out | 27 +++++++++++++++++-- test_regress/t/t_fuzz_triand_bad.out | 9 ++++--- test_regress/t/t_queue_method3_bad.out | 6 +---- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 1b64307ce..0d2bafc62 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -2143,7 +2143,7 @@ void V3Options::setDebugMode(int level) { if (!m_dumpLevel.count("tree")) m_dumpLevel["tree"] = 3; // Don't override if already set. m_stats = true; m_debugCheck = true; - cout << "Starting " << version() << "\n"; + if (level) cout << "Starting " << version() << "\n"; } unsigned V3Options::debugLevel(const string& tag) const VL_MT_SAFE { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index e077b9518..c23799239 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -266,7 +266,7 @@ class TimingSuspendableVisitor final : public VNVisitor { getNeedsProcDepVtx(nodep); addFlags(nodep, T_ALLOCS_PROC); if (VN_IS(nodep, Always)) { - UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process"); + UINFO(9, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process"); } iterateChildren(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ff6de8dee..32eacdca9 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3340,7 +3340,7 @@ class WidthVisitor final : public VNVisitor { << nodep->fromp()->prettyTypeName() << "' which is a '" << nodep->fromp()->dtypep()->prettyTypeName() << "'"); - nodep->dtypep(m_vup->dtypeNullp()); + nodep->dtypeSetVoid(); } } AstWith* methodWithArgument(AstNodeFTaskRef* nodep, bool required, bool arbReturn, diff --git a/test_regress/t/t_covergroup_coverpoints_unsup.out b/test_regress/t/t_covergroup_coverpoints_unsup.out index 456364f10..7e822aa5a 100644 --- a/test_regress/t/t_covergroup_coverpoints_unsup.out +++ b/test_regress/t/t_covergroup_coverpoints_unsup.out @@ -25,5 +25,28 @@ 35 | $display("coverage a = %f", the_cg.a.get_inst_coverage()); | ^~~~~~~~~~~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Verilator internal fault, sorry. Suggest trying --debug --gdbbt -%Error: Command Failed +%Error: t/t_covergroup_coverpoints_unsup.v:36:48: Member 'b' not found in class 'cg' + : ... note: In instance 't' + 36 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:36:50: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'logic'' + : ... note: In instance 't' + 36 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); + | ^~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_coverpoints_unsup.v:37:24: Member 'a' not found in class 'cg' + : ... note: In instance 't' + 37 | if (the_cg.a.get_inst_coverage() != 15/16.0) $stop(); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:37:26: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'logic'' + : ... note: In instance 't' + 37 | if (the_cg.a.get_inst_coverage() != 15/16.0) $stop(); + | ^~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_coverpoints_unsup.v:38:24: Member 'b' not found in class 'cg' + : ... note: In instance 't' + 38 | if (the_cg.b.get_inst_coverage() != 4/5.0) $stop(); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:38:26: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'logic'' + : ... note: In instance 't' + 38 | if (the_cg.b.get_inst_coverage() != 4/5.0) $stop(); + | ^~~~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_fuzz_triand_bad.out b/test_regress/t/t_fuzz_triand_bad.out index df4c63b13..497744d83 100644 --- a/test_regress/t/t_fuzz_triand_bad.out +++ b/test_regress/t/t_fuzz_triand_bad.out @@ -3,8 +3,9 @@ 8 | tri g=g.and.g; | ^~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Internal Error: t/t_fuzz_triand_bad.v:8:12: ../V3Width.cpp:#: Unlinked data type - : ... note: In instance 't' +%Error: t/t_fuzz_triand_bad.v:8:16: Member selection of non-struct/union object 'METHODCALL 'and'' which is a 'VOIDDTYPE' + : ... note: In instance 't' 8 | tri g=g.and.g; - | ^~~ - ... This fatal error may be caused by the earlier error(s); resolve those first. + | ^ + ... 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_queue_method3_bad.out b/test_regress/t/t_queue_method3_bad.out index df38b71fb..041af3fa2 100644 --- a/test_regress/t/t_queue_method3_bad.out +++ b/test_regress/t/t_queue_method3_bad.out @@ -3,8 +3,4 @@ 16 | points_qv = points_q.find_first(a) with (a.x.index == 0); | ^~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Internal Error: t/t_queue_method3_bad.v:16:58: ../V3Width.cpp:#: Node has no type - : ... note: In instance 't' - 16 | points_qv = points_q.find_first(a) with (a.x.index == 0); - | ^~ - ... This fatal error may be caused by the earlier error(s); resolve those first. +%Error: Exiting due to From 460bfbf181ba80da30847cd04ec5ab7c41cb6924 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 23 Jul 2025 18:07:50 -0400 Subject: [PATCH 064/252] Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048). --- Changes | 1 + src/V3AstNodeExpr.h | 6 +++++- src/V3Number.cpp | 6 +----- src/V3Width.cpp | 12 ++++++++++-- test_regress/t/t_math_repl2_bad.out | 11 ++++++++--- test_regress/t/t_math_repl3_bad.out | 28 ++++++++++++++++++++++++++++ test_regress/t/t_math_repl3_bad.py | 16 ++++++++++++++++ test_regress/t/t_math_repl3_bad.v | 18 ++++++++++++++++++ 8 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 test_regress/t/t_math_repl3_bad.out create mode 100755 test_regress/t/t_math_repl3_bad.py create mode 100644 test_regress/t/t_math_repl3_bad.v diff --git a/Changes b/Changes index ae6acf5d3..a6eee82ae 100644 --- a/Changes +++ b/Changes @@ -25,6 +25,7 @@ Verilator 5.039 devel * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] +* Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048). * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] * Fix class extends dotted error (#6162). [Igor Zaworski, Antmicro Ltd.] * Fix genvar error with `-O0` (#6165). [Max Wipfli] diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index b4c2f9a81..d816fa2d5 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -3367,7 +3367,11 @@ public: : ASTGEN_SUPER_Replicate(fl, lhsp, rhsp) { if (lhsp) { if (const AstConst* const constp = VN_CAST(rhsp, Const)) { - dtypeSetLogicSized(lhsp->width() * constp->toUInt(), VSigning::UNSIGNED); + if (constp->num().isFourState() || constp->num().isNegative()) { // V3Width warns + dtypeSetLogicSized(lhsp->width(), VSigning::UNSIGNED); + } else { + dtypeSetLogicSized(lhsp->width() * constp->toSInt(), VSigning::UNSIGNED); + } } } } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index ce1477b25..d483c0cf3 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1471,11 +1471,7 @@ V3Number& V3Number::opRepl(const V3Number& lhs, // i op repl, L(i)*value(rhs) bit return NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); - if (rhsval > (1UL << 24)) { - v3error("More than a 16 Mbit replication, perhaps the replication factor" - " was two's-complement negative: " - << rhsval << " (" << static_cast(rhsval) << ")"); - } else if (rhsval > 8192) { + if (rhsval > 8192) { v3warn(WIDTHCONCAT, "More than a 8k bit replication is probably wrong: " << rhsval); } setZero(); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 32eacdca9..973ab54aa 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -798,10 +798,18 @@ class WidthVisitor final : public VNVisitor { iterateCheckSizedSelf(nodep, "RHS", nodep->countp(), SELF, BOTH); V3Const::constifyParamsNoWarnEdit(nodep->countp()); // rhsp may change - uint32_t times = 1; + int32_t times = 1; // IEEE replicate value is integral const AstConst* const constp = VN_CAST(nodep->countp(), Const); - if (constp) times = constp->toUInt(); + if (constp) { + if (constp->num().isFourState() || constp->num().isNegative()) { + nodep->v3error("Replication value of < 0 or X/Z not legal" + " (IEEE 1800-2023 11.4.12.1): " + << constp->prettyNameQ()); + } else { + times = constp->toSInt(); + } + } AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType) diff --git a/test_regress/t/t_math_repl2_bad.out b/test_regress/t/t_math_repl2_bad.out index 3f21714bb..9f5fecdc7 100644 --- a/test_regress/t/t_math_repl2_bad.out +++ b/test_regress/t/t_math_repl2_bad.out @@ -1,7 +1,12 @@ -%Error: t/t_math_repl2_bad.v:28:30: More than a 16 Mbit replication, perhaps the replication factor was two's-complement negative: 4294967291 (-5) +%Error: t/t_math_repl2_bad.v:28:30: Replication value of < 0 or X/Z not legal (IEEE 1800-2023 11.4.12.1): '32'hfffffffb' : ... note: In instance 't' 28 | out <= {{(P24 - P29){1'b0}}, in}; | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: Internal Error: ../V3Number.h:#: `num` member accessed when data type is UNINITIALIZED - ... This fatal error may be caused by the earlier error(s); resolve those first. +%Warning-WIDTHTRUNC: t/t_math_repl2_bad.v:28:14: Operator ASSIGNDLY expects 24 bits on the Assign RHS, but Assign RHS's REPLICATE generates 30 bits. + : ... note: In instance 't' + 28 | out <= {{(P24 - P29){1'b0}}, in}; + | ^~ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_math_repl3_bad.out b/test_regress/t/t_math_repl3_bad.out new file mode 100644 index 000000000..33fa422b5 --- /dev/null +++ b/test_regress/t/t_math_repl3_bad.out @@ -0,0 +1,28 @@ +%Error: t/t_math_repl3_bad.v:14:50: Replication value of < 0 or X/Z not legal (IEEE 1800-2023 11.4.12.1): '32'hfffffff8' + : ... note: In instance 't' + 14 | link_data_reg_in = {{((NUM_LANES - 2) * 8) {1'b0}}, link_data_reg[15:8]}; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Warning-SELRANGE: t/t_math_repl3_bad.v:14:72: Selection index out of range: 15:8 outside 7:0 + : ... note: In instance 't' + 14 | link_data_reg_in = {{((NUM_LANES - 2) * 8) {1'b0}}, link_data_reg[15:8]}; + | ^ + ... For warning description see https://verilator.org/warn/SELRANGE?v=latest + ... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message. +%Warning-WIDTHTRUNC: t/t_math_repl3_bad.v:14:24: Operator ASSIGN expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits. + : ... note: In instance 't' + 14 | link_data_reg_in = {{((NUM_LANES - 2) * 8) {1'b0}}, link_data_reg[15:8]}; + | ^ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Error: t/t_math_repl3_bad.v:16:19: Replication value of < 0 or X/Z not legal (IEEE 1800-2023 11.4.12.1): '32'bzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' + : ... note: In instance 't' + 16 | other = {32'bz{1'b1}}; + | ^ +%Warning-WIDTHEXPAND: t/t_math_repl3_bad.v:16:11: Operator ASSIGN expects 2 bits on the Assign RHS, but Assign RHS's REPLICATE generates 1 bits. + : ... note: In instance 't' + 16 | other = {32'bz{1'b1}}; + | ^ + ... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=latest + ... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_math_repl3_bad.py b/test_regress/t/t_math_repl3_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_math_repl3_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_math_repl3_bad.v b/test_regress/t/t_math_repl3_bad.v new file mode 100644 index 000000000..b3f86c0ab --- /dev/null +++ b/test_regress/t/t_math_repl3_bad.v @@ -0,0 +1,18 @@ +// 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 #( + parameter NUM_LANES = 1); + + reg [(NUM_LANES*8)-1:0] link_data_reg, link_data_reg_in; + reg [1:0] other; + always @(*) begin + if (NUM_LANES >= 2) begin // Not a generate if + link_data_reg_in = {{((NUM_LANES - 2) * 8) {1'b0}}, link_data_reg[15:8]}; + end + other = {32'bz{1'b1}}; + end +endmodule From db5b2669fc18261e3e0b1e66aaaf2fbdf56a4a57 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 24 Jul 2025 01:44:33 -0400 Subject: [PATCH 065/252] Add current memory usage statistic (#6192 partial) --- include/verilated.cpp | 4 ++- include/verilatedos.h | 2 +- include/verilatedos_c.h | 43 +++++++++++++++++++++---------- src/V3Stats.cpp | 5 +--- src/V3StatsReport.cpp | 10 ++++--- src/Verilator.cpp | 3 ++- test_regress/t/t_display_merge.py | 2 +- 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index 74ceae09d..33cf917d4 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3061,7 +3061,9 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE { = vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s"); VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(), simtime.c_str(), walltime, simtimePerf.c_str()); - const double modelMB = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; + uint64_t memPeak, memCurrent; + VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/); + const double modelMB = memPeak / 1024.0 / 1024.0; VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime, threadsInModels(), modelMB); } diff --git a/include/verilatedos.h b/include/verilatedos.h index 085bb2544..6b289292f 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -637,7 +637,7 @@ extern std::string getenvStr(const std::string& envvar, extern uint16_t getcpu() VL_MT_SAFE; /// Return memory usage in bytes, or 0 if unknown -extern uint64_t memPeakUsageBytes() VL_MT_SAFE; +extern void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE; // Internal: Record CPU time, starting point on construction, and current delta from that class DeltaCpuTime final { diff --git a/include/verilatedos_c.h b/include/verilatedos_c.h index 6ecdf7dba..2b4619816 100644 --- a/include/verilatedos_c.h +++ b/include/verilatedos_c.h @@ -25,6 +25,9 @@ #include "verilatedos.h" +#include +#include + // clang-format off #if defined(_WIN32) || defined(__MINGW32__) # include // LONG for bcrypt.h on MINGW @@ -104,28 +107,40 @@ uint16_t getcpu() VL_MT_SAFE { //========================================================================= // VlOs::memPeakUsageBytes implementation -uint64_t memPeakUsageBytes() VL_MT_SAFE { +void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE { + peakr = 0; + currentr = 0; #if defined(_WIN32) || defined(__MINGW32__) const HANDLE process = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) { // The best we can do using simple Windows APIs is to get the size of the working set. - return pmc.WorkingSetSize; + peakr = pmc.PeakWorkingSetSize; + currentr = pmc.WorkingSetSize; } - return 0; #else // Highly unportable. Sorry - const char* const statmFilename = "/proc/self/statm"; - FILE* const fp = fopen(statmFilename, "r"); - if (!fp) return 0; - uint64_t size, resident, share, text, lib, data, dt; // All in pages - const int items = fscanf( - fp, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, - &size, &resident, &share, &text, &lib, &data, &dt); - fclose(fp); - if (VL_UNCOVERABLE(7 != items)) return 0; - // Return the vm size, not the current active set size (/proc/self/status VmRSS + VmSwap) - return (text + data) * getpagesize(); + std::ifstream is{"/proc/self/status"}; + if (!is) return; + std::string line; + uint64_t vmPeak = 0; + uint64_t vmRss = 0; + uint64_t vmSwap = 0; + std::string field; + while (std::getline(is, line)) { + if (line.rfind("VmPeak:", 0) == 0) { + std::stringstream ss{line}; + ss >> field >> vmPeak; + } else if (line.rfind("VmRSS:", 0) == 0) { + std::stringstream ss{line}; + ss >> field >> vmRss; + } else if (line.rfind("VmSwap:", 0) == 0) { + std::stringstream ss{line}; + ss >> field >> vmSwap; + } + } + peakr = vmPeak * 1024; + currentr = (vmRss + vmSwap) * 1024; #endif } diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index 394ba1de2..cee37d85a 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -179,7 +179,4 @@ void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fa StatsVisitor{nodep, stage, fastOnly}; } -void V3Stats::statsFinalAll(AstNetlist* nodep) { - statsStageAll(nodep, "Final all"); - statsStageAll(nodep, "Final fast", true); -} +void V3Stats::statsFinalAll(AstNetlist* nodep) { statsStageAll(nodep, "Final"); } diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 2c5dadb80..3efe8e947 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -215,8 +215,10 @@ void V3Stats::statsStage(const string& name) { V3Stats::addStatPerf("Stage, Elapsed time (sec), " + digitName, wallTimeDelta); V3Stats::addStatPerf("Stage, Elapsed time (sec), TOTAL", wallTimeDelta); - const double memory = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; - V3Stats::addStatPerf("Stage, Memory (MB), " + digitName, memory); + uint64_t memPeak, memCurrent; + VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/); + V3Stats::addStatPerf("Stage, Memory current (MB), " + digitName, memCurrent / 1024.0 / 1024.0); + V3Stats::addStatPerf("Stage, Memory peak (MB), " + digitName, memPeak / 1024.0 / 1024.0); } void V3Stats::infoHeader(std::ofstream& os, const string& prefix) { @@ -266,7 +268,9 @@ void V3Stats::summaryReport() { << ", cvt=" << walltimeCvt << ", bld=" << walltimeBuild << "); cpu " << cputime << " s on " << std::max(v3Global.opt.verilateJobs(), v3Global.opt.buildJobs()) << " threads"; - const double memory = VlOs::memPeakUsageBytes() / 1024.0 / 1024.0; + uint64_t memPeak, memCurrent; + VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/); + const double memory = memPeak / 1024.0 / 1024.0; if (VL_UNCOVERABLE(memory != 0.0)) std::cout << "; alloced " << memory << " MB"; std::cout << "\n"; } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index f712412b8..61cc3b9b5 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -768,7 +768,8 @@ static void verilate(const string& argString) { V3Os::filesystemFlushBuildDir(v3Global.opt.makeDir()); if (v3Global.opt.hierTop()) V3Os::filesystemFlushBuildDir(v3Global.opt.hierTopDataDir()); - if (v3Global.opt.stats()) V3Stats::statsStage("wrote"); + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "WroteAll"); + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "WroteFast"); // Final writing shouldn't throw warnings, but... V3Error::abortIfWarnings(); diff --git a/test_regress/t/t_display_merge.py b/test_regress/t/t_display_merge.py index 03f439c26..21d0b3b95 100755 --- a/test_regress/t/t_display_merge.py +++ b/test_regress/t/t_display_merge.py @@ -9,7 +9,7 @@ import vltest_bootstrap -test.scenarios('simulator') +test.scenarios('simulator_st') test.compile(verilator_flags2=["--stats"]) From 2287d420ee78bb76be9fe5c1abdf6aa1e8018cea Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 24 Jul 2025 01:47:16 -0400 Subject: [PATCH 066/252] Optimize to return memory when using -build (#6192) (#6226). --- Changes | 2 +- configure.ac | 3 +++ src/V3AstNodeOther.h | 1 + src/V3AstNodes.cpp | 19 +++++++++++++++++++ src/V3Os.cpp | 13 +++++++++++++ src/V3Os.h | 3 +++ src/Verilator.cpp | 6 ++++++ src/config_package.h.in | 3 +++ 8 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index a6eee82ae..0a9ca458b 100644 --- a/Changes +++ b/Changes @@ -24,6 +24,7 @@ Verilator 5.039 devel * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). +* Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048). * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] @@ -39,7 +40,6 @@ Verilator 5.039 devel * Fix VPI signal range order (#6189) (#6200). [Ibrahim Burak Yorulmaz] * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] - Verilator 5.038 2025-07-08 ========================== diff --git a/configure.ac b/configure.ac index 05757edbb..c72b048e8 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,7 @@ AC_ARG_ENABLE([tcmalloc], *) AC_MSG_ERROR([bad value '${enableval}' for --enable-tcmalloc]) ;; esac], [CFG_WITH_TCMALLOC=check;]) +AC_SUBST(CFG_WITH_TCMALLOC) AC_MSG_RESULT($CFG_WITH_TCMALLOC) # Flag to enable coverage build @@ -546,10 +547,12 @@ _MY_LDLIBS_CHECK_IFELSE( _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free) + AC_DEFINE([HAVE_TCMALLOC],[1],[Defined if have tcmalloc]) fi], [if test "$CFG_WITH_TCMALLOC" = "yes"; then AC_MSG_ERROR([--enable-tcmalloc was given but test for ${LTCMALLOC} failed]) fi]) +AC_SUBST(HAVE_TCMALLOC) AC_SUBST(CFG_LIBS) # Need C++14 at least diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 549e304eb..b819ee829 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1406,6 +1406,7 @@ class AstNetlist final : public AstNode { public: AstNetlist(); ASTGEN_MEMBERS_AstNetlist; + void deleteContents(); void cloneRelink() override { V3ERROR_NA; } string name() const override VL_MT_STABLE { return "$root"; } void dump(std::ostream& str) const override; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7cfc48210..0caf0d379 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2296,6 +2296,25 @@ void AstNetlist::dumpJson(std::ostream& str) const { dumpJsonStr(str, "timeprecision", timeprecision().ascii()); dumpJsonGen(str); } +void AstNetlist::deleteContents() { + // Delete all netlist memory. Only for use by Verilator.cpp + m_typeTablep = nullptr; + m_constPoolp = nullptr; + m_dollarUnitPkgp = nullptr; + m_stdPackagep = nullptr; + m_evalp = nullptr; + m_evalNbap = nullptr; + m_dpiExportTriggerp = nullptr; + m_delaySchedulerp = nullptr; + m_nbaEventp = nullptr; + m_nbaEventTriggerp = nullptr; + m_topScopep = nullptr; + if (op1p()) op1p()->unlinkFrBackWithNext()->deleteTree(); + if (op2p()) op2p()->unlinkFrBackWithNext()->deleteTree(); + if (op3p()) op3p()->unlinkFrBackWithNext()->deleteTree(); + if (op4p()) op4p()->unlinkFrBackWithNext()->deleteTree(); +#undef VN_DELETE_ONE +} AstPackage* AstNetlist::dollarUnitPkgAddp() { if (!m_dollarUnitPkgp) { m_dollarUnitPkgp = new AstPackage{fileline(), AstPackage::dollarUnitName(), "work"}; diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 5b6eea6f7..13be637a4 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -54,6 +54,10 @@ VL_DEFINE_DEBUG_FUNCTIONS; #include #include +#ifdef HAVE_TCMALLOC +#include +#endif + // clang-format off #if defined(_WIN32) || defined(__MINGW32__) # include // LONG for bcrypt.h on MINGW @@ -402,6 +406,15 @@ void V3Os::unlinkRegexp(const string& dir, const string& regexp) { #endif } +//###################################################################### +// METHODS (memory) + +void V3Os::releaseMemory() { +#ifdef HAVE_TCMALLOC + MallocExtension::instance()->ReleaseFreeMemory(); +#endif +} + //###################################################################### // METHODS (random) diff --git a/src/V3Os.h b/src/V3Os.h index 79714136d..8a2e66e1a 100644 --- a/src/V3Os.h +++ b/src/V3Os.h @@ -71,6 +71,9 @@ public: static void filesystemFlushBuildDir(const string& dirname); static void unlinkRegexp(const string& dir, const string& regexp); + // METHODS (memory) + static void releaseMemory(); + // METHODS (random) static uint64_t rand64(std::array& stater); static string trueRandom(size_t size) VL_MT_SAFE; diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 61cc3b9b5..3ef409ebc 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -773,6 +773,12 @@ static void verilate(const string& argString) { // Final writing shouldn't throw warnings, but... V3Error::abortIfWarnings(); + + // Free memory so compiler has more for --build + UINFO(1, "Releasing netlist memory"); + v3Global.rootp()->deleteContents(); + V3Os::releaseMemory(); + if (v3Global.opt.stats()) V3Stats::statsStage("released"); } static string buildMakeCmd(const string& makefile, const string& target) { diff --git a/src/config_package.h.in b/src/config_package.h.in index 7a7088a82..f8c02101a 100644 --- a/src/config_package.h.in +++ b/src/config_package.h.in @@ -41,6 +41,9 @@ PACKAGE_VERSION_STRING_CHAR // Define if coroutines are supported on this platform #undef HAVE_COROUTINES +// Define if compiled with tcmalloc +#undef HAVE_TCMALLOC + //********************************************************************** //**** This file sometimes gets truncated, so check in consumers #define HAVE_CONFIG_PACKAGE From e995646898e85abbcf1e4ea5a71151bfe0997e7e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 24 Jul 2025 02:27:56 -0400 Subject: [PATCH 067/252] Internals: Favor std::array. No functional change intended. --- src/V3GraphPathChecker.cpp | 6 ++---- src/V3OrderParallel.cpp | 3 +-- src/V3Stats.cpp | 9 +++------ src/V3Undriven.cpp | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/V3GraphPathChecker.cpp b/src/V3GraphPathChecker.cpp index f59d76cb4..aeed65c71 100644 --- a/src/V3GraphPathChecker.cpp +++ b/src/V3GraphPathChecker.cpp @@ -35,7 +35,7 @@ struct GraphPCNode final { // // Unlike the LogicMTasks's, we have no cost info for the generic graph // accepted by GraphPathChecker, so assume each node has unit cost. - std::array m_cp; + std::array m_cp = {}; // Detect if we've seen this node before in a given recursive // operation. We'll use this in pathExistsInternal() to avoid checking @@ -44,9 +44,7 @@ struct GraphPCNode final { uint64_t m_seenAtGeneration = 0; // CONSTRUCTORS - GraphPCNode() { - for (unsigned int& w : m_cp) w = 0; - } + GraphPCNode() {} ~GraphPCNode() = default; }; diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 52057018e..c640df090 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -332,7 +332,7 @@ private: // Cost of critical paths going FORWARD from graph-start to the start // of this vertex, and also going REVERSE from the end of the graph to // the end of the vertex. Same units as m_cost. - std::array m_critPathCost; + std::array m_critPathCost = {}; const uint32_t m_id; // Unique LogicMTask ID number static uint32_t s_nextId; // Next ID number to use @@ -361,7 +361,6 @@ public: : V3GraphVertex{graphp} , m_id{s_nextId++} { UASSERT(s_nextId < 0xFFFFFFFFUL, "Too many mTaskGraphp"); - for (uint64_t& item : m_critPathCost) item = 0; if (mVtxp) { m_mVertices.linkBack(mVtxp); if (const OrderLogicVertex* const olvp = mVtxp->logicp()) { diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index cee37d85a..1baaadcf1 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -34,11 +34,11 @@ V3Mutex V3Stats::s_mutex; class StatsVisitor final : public VNVisitorConst { struct Counters final { // Nodes of given type - uint64_t m_statTypeCount[VNType::_ENUM_END]; + std::array m_statTypeCount = {}; // Nodes of given type with given type immediate child - uint64_t m_statAbove[VNType::_ENUM_END][VNType::_ENUM_END]; + std::array, VNType::_ENUM_END> m_statAbove = {}; // Prediction of given type - uint64_t m_statPred[VBranchPred::_ENUM_END]; + std::array m_statPred = {}; }; // STATE @@ -103,9 +103,6 @@ public: : m_fastOnly{fastOnly} , m_accump{fastOnly ? &m_dumpster : &m_counters} { UINFO(9, "Starting stats, fastOnly=" << fastOnly); - memset(&m_counters, 0, sizeof(m_counters)); - memset(&m_dumpster, 0, sizeof(m_dumpster)); - iterateConst(nodep); // Shorthand diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 800b367c3..7f1d5b21c 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -293,7 +293,7 @@ class UndrivenVisitor final : public VNVisitorConst { const VNUser2InUse m_inuser2; // STATE - std::array, 3> m_entryps; // Nodes to delete when finished + 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 From 4882a3c827456225f36a9c07c0d306f24a5502fc Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 24 Jul 2025 02:44:54 -0400 Subject: [PATCH 068/252] Commentary: Changes update --- Changes | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 0a9ca458b..01df78779 100644 --- a/Changes +++ b/Changes @@ -21,10 +21,13 @@ Verilator 5.039 devel * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] -* Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]] +* Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] +* Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] +* Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] +* Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048). * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] @@ -32,13 +35,17 @@ Verilator 5.039 devel * Fix genvar error with `-O0` (#6165). [Max Wipfli] * Fix uninitialized thread PGO counters (#6167). [Bartłomiej Chmiel, Antmicro Ltd.] * Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173) (#6176). [Geza Lore] -* Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.]] +* Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.] +* Fix parameter-dependent type linking (#6170). [Igor Zaworski, Antmicro Ltd.] * Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.] * Fix virtual interface member propagation (#6175) (#6184). [Yilou Wang] * Fix `--coverage-expr` null pointer dereference (#6181). [Igor Zaworski, Antmicro Ltd.] * Fix conflicting function/class name linking error (#6182). [Igor Zaworski, Antmicro Ltd.] +* Fix automatic task variables in unrolled loops with forks (#6194) (#6201). [Danny Oler] * Fix VPI signal range order (#6189) (#6200). [Ibrahim Burak Yorulmaz] * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] +* Fix 'driver same component' assertion (#6211) (#6215). [Geza Lore] + Verilator 5.038 2025-07-08 ========================== From 04c38d5b3bfd49ca7ebca4f1620f67f7ebbc3922 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Thu, 24 Jul 2025 11:27:02 +0200 Subject: [PATCH 069/252] Tests: Switch to measuring CPU time instead of real time in test timeouts (#6224) Signed-off-by: Artur Bieniek --- test_regress/driver.py | 15 ++++++++++++ test_regress/t/t_concat_string.py | 3 +-- .../t/t_const_number_unsized_parse.py | 3 +-- test_regress/t/t_timeout.py | 24 +++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_timeout.py diff --git a/test_regress/driver.py b/test_regress/driver.py index 3cf46ab10..a5292afe7 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -4,6 +4,7 @@ import argparse import collections +import ctypes import glob import hashlib import json @@ -14,6 +15,7 @@ import pickle import platform import pty import re +import resource import runpy import shutil import signal @@ -1366,6 +1368,19 @@ class VlTest: ] self.run(logfile=self.obj_dir + "/pli_compile.log", fails=param['fails'], cmd=cmd) + def timeout(self, seconds): + """Limit the CPU time of the test - this limit is inherited + by all of the spawned child processess""" + # An unprivileged process may set only its soft limit + # to a value in the range from 0 up to the hard limit + _, hardlimit = resource.getrlimit(resource.RLIMIT_CPU) + softlimit = ctypes.c_long(min(seconds, ctypes.c_ulong(hardlimit).value)).value + # Casting is required due to a quirk in Python, + # rlimit values are interpreted as LONG, instead of ULONG + # https://github.com/python/cpython/issues/137044 + rlimit = (softlimit, hardlimit) + resource.setrlimit(resource.RLIMIT_CPU, rlimit) + def execute(self, **kwargs) -> None: """Run simulation executable. Arguments similar to run(); default arguments are from self""" diff --git a/test_regress/t/t_concat_string.py b/test_regress/t/t_concat_string.py index 262a0d4b4..52c58524e 100755 --- a/test_regress/t/t_concat_string.py +++ b/test_regress/t/t_concat_string.py @@ -7,12 +7,11 @@ # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -import signal import vltest_bootstrap test.scenarios('simulator') -signal.alarm(15) # 15s timeout +test.timeout(15) test.compile() diff --git a/test_regress/t/t_const_number_unsized_parse.py b/test_regress/t/t_const_number_unsized_parse.py index 6b8b2c82e..2920c1617 100755 --- a/test_regress/t/t_const_number_unsized_parse.py +++ b/test_regress/t/t_const_number_unsized_parse.py @@ -7,7 +7,6 @@ # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -import signal import vltest_bootstrap test.scenarios('vlt') @@ -20,7 +19,7 @@ with open(test.top_filename, "w", encoding="utf8") as f: f.write(f" int x{i} = 'd{i};\n") f.write("endmodule\n") -signal.alarm(30) # 30s timeout +test.timeout(30) test.lint(verilator_flags2=[f"--max-num-width {2**29}"]) diff --git a/test_regress/t/t_timeout.py b/test_regress/t/t_timeout.py new file mode 100755 index 000000000..bd3c3c3c0 --- /dev/null +++ b/test_regress/t/t_timeout.py @@ -0,0 +1,24 @@ +#!/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('dist') + +test.passes() + +test.timeout(1) + +# Check whether the soft limit is set to 1s +test.run(cmd=["bash", "-c", "\"[ '$(ulimit -St)' -eq 1 ] || exit 1\""]) + +# Set the hard limit to 2s (in case soft limit fails) and run a command which spins until signalled +test.run(cmd=["bash", "-c", "\"trap 'exit 0' SIGXCPU; ulimit -Ht 2; while :; do :; done\""]) + +test.passes() From 2be257369acc0eab28c2f427a66194e8dac0385a Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 24 Jul 2025 15:31:09 +0100 Subject: [PATCH 070/252] Fix component numbers of new Vertices in V3DfgBreakCycles (#6228) Fix component numbers of new Vertices in V3DfgBreakCycles Fixes part of #6225 --- src/V3DfgBreakCycles.cpp | 32 ++++++++++++++++++++++------ test_regress/t/t_dfg_break_cycles.py | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 8e7fd7804..46a53c6c2 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -210,7 +210,12 @@ class TraceDriver final : public DfgVisitor { // Create and return a new Vertex and add it to m_newVtxps. Fileline is // taken from 'refp', but 'refp' is otherwise not used. You should // always use this to create new vertices, so unused ones (if a trace - // eventually fails) can be cleaned up at the end. + // eventually fails) can be cleaned up at the end. This also sets the + // vertex user to 0, indicating the new vertex is not part of a + // strongly connected component. This should always be true, as all the + // vertices we create here are driven from outside the component we are + // trying to escape, and will sink into that component. Given those are + // separate SCCs, these new vertices must be acyclic. template Vertex* make(const DfgVertex* refp, uint32_t width) { static_assert(std::is_base_of::value // @@ -229,6 +234,7 @@ class TraceDriver final : public DfgVisitor { if VL_CONSTEXPR_CXX17 (std::is_same::value) { DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width}; + vtxp->template setUser(0); m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } else { @@ -239,6 +245,7 @@ class TraceDriver final : public DfgVisitor { Vertex>::type; AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; + vtxp->template setUser(0); m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } @@ -276,7 +283,7 @@ class TraceDriver final : public DfgVisitor { // Trace the vertex onStackr = true; - if (vtxp->user() != m_component) { + if (vtxp->getUser() != m_component) { // If the currently traced vertex is in a different component, // then we found what we were looking for. if (msb != vtxp->width() - 1 || lsb != 0) { @@ -579,7 +586,7 @@ public: // waste a lot of compute. static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width, bool aggressive) { - TraceDriver traceDriver{dfg, vtxp->user(), aggressive}; + TraceDriver traceDriver{dfg, vtxp->getUser(), aggressive}; // Find the out-of-component driver of the given vertex DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb); // Delete unused newly created vertices (these can be created if a @@ -852,7 +859,7 @@ class FixUpSelDrivers final { // Only handle Sel DfgSel* const selp = sink.cast(); if (!selp) return; - // Try to find of the driver of the selected bits outside the cycle + // Try to find the driver of the selected bits outside the cycle DfgVertex* const fixp = TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false); if (!fixp) return; @@ -968,6 +975,8 @@ class FixUpIndependentRanges final { if (!termp) { AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; + // Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp' + selp->setUser(vtxp->getUser()); // Do not connect selp->fromp yet, need to do afer replacing 'vtxp' selp->lsb(lsb); termp = selp; @@ -1022,7 +1031,9 @@ class FixUpIndependentRanges final { nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb); } else { // The range is not used, just use constant 0 as a placeholder - termps.emplace_back(new DfgConst{dfg, flp, msb - lsb + 1}); + DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1}; + constp->setUser(0); + termps.emplace_back(constp); } // Next iteration isUsed = !isUsed; @@ -1033,6 +1044,7 @@ class FixUpIndependentRanges final { if (nImprovements) { // Concatenate all the terms to create the replacement DfgVertex* replacementp = termps.front(); + const uint32_t vComp = vtxp->getUser(); for (size_t i = 1; i < termps.size(); ++i) { DfgVertex* const termp = termps[i]; const uint32_t catWidth = replacementp->width() + termp->width(); @@ -1040,6 +1052,14 @@ class FixUpIndependentRanges final { DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; catp->rhsp(replacementp); catp->lhsp(termp); + // Need to figure out which component the replacement vertex + // belongs to. If any of the terms are of the original + // component, it's part of that component, otherwise its not + // cyclic (all terms are from outside the original component, + // and feed into the original component). + const uint32_t tComp = termp->getUser(); + const uint32_t rComp = replacementp->getUser(); + catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); replacementp = catp; } @@ -1071,7 +1091,7 @@ public: // For array variables, fix up element-wise const uint32_t component = varp->getUser(); varp->forEachSink([&](DfgVertex& sink) { - // Ignore if sink is not part of cycle + // Ignore if sink is not part of same cycle if (sink.getUser() != component) return; // Only handle ArraySels with constant index DfgArraySel* const aselp = sink.cast(); diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index bd94a1b10..f20d6352b 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -88,6 +88,7 @@ test.compile(verilator_flags2=[ "--prefix", "Vopt", "-Werror-UNOPTFLAT", "--dumpi-V3DfgBreakCycles", "9", # To fill code coverage + "--debug", "--debugi", "0", "--dumpi-tree", "0", "-CFLAGS \"-I .. -I ../obj_ref\"", "../obj_ref/Vref__ALL.a", "../../t/" + test.name + ".cpp" From 94bebb2bcb69eea8d199e0fad2bd4e7846310eab Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 24 Jul 2025 15:31:31 +0100 Subject: [PATCH 071/252] Fix dereferencing stale iterator in DfgVertex::scopep() (#6227) Fix dereferencing stale iterator in DfgVertex::scopep() Fixes part of #6225 --- src/V3Dfg.cpp | 16 +++++++++------- test_regress/t/t_enum_int.py | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 9d498a02b..a619ee9a7 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -682,9 +682,11 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED if (DfgVertexVar* const varp = this->getResultVar()) return varp->varScopep()->scopep(); } - // Look up cache - const auto pair = cache.emplace(this, nullptr); - if (pair.second) { + // Note: the recursive invocation can cause a re-hash but that will not invalidate references + AstScope*& resultr = cache[this]; + if (!resultr) { + // Mark to prevent infinite recursion on circular graphs - should never be called on such + resultr = reinterpret_cast(1); // Find scope based on sources, falling back on the root scope AstScope* const rootp = v3Global.rootp()->topScopep()->scopep(); AstScope* foundp = rootp; @@ -694,15 +696,15 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED foundp = edge.sourcep()->scopep(cache, true); if (foundp != rootp) break; } - pair.first->second = foundp; + resultr = foundp; } - // If the cache entry exists, but have not set the mapping yet, then we have a circualr graph - UASSERT_OBJ(pair.first->second, this, + // Die on a graph circular through operation vertices + UASSERT_OBJ(resultr != reinterpret_cast(1), this, "DfgVertex::scopep called on graph with circular operations"); // Done - return pair.first->second; + return resultr; } void DfgVertex::unlinkDelete(DfgGraph& dfg) { diff --git a/test_regress/t/t_enum_int.py b/test_regress/t/t_enum_int.py index d4f986441..aff901aa6 100755 --- a/test_regress/t/t_enum_int.py +++ b/test_regress/t/t_enum_int.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["--debug", "--debugi", "0", "--dumpi-tree", "0"]) test.execute() From b408e097f6bae380c415934de81d0472508b5276 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 24 Jul 2025 22:55:26 -0400 Subject: [PATCH 072/252] Fix unsigned replicate (#6229) --- Changes | 2 +- src/V3Width.cpp | 3 ++- test_regress/t/t_math_repl3_bad.v | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 01df78779..b857da768 100644 --- a/Changes +++ b/Changes @@ -29,7 +29,7 @@ Verilator 5.039 devel * Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] * Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] -* Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048). +* Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] * Fix class extends dotted error (#6162). [Igor Zaworski, Antmicro Ltd.] * Fix genvar error with `-O0` (#6165). [Max Wipfli] diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 973ab54aa..83ef4b38e 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -802,7 +802,8 @@ class WidthVisitor final : public VNVisitor { const AstConst* const constp = VN_CAST(nodep->countp(), Const); if (constp) { - if (constp->num().isFourState() || constp->num().isNegative()) { + if (constp->num().isFourState() + || (constp->dtypep()->isSigned() && constp->num().isNegative())) { nodep->v3error("Replication value of < 0 or X/Z not legal" " (IEEE 1800-2023 11.4.12.1): " << constp->prettyNameQ()); diff --git a/test_regress/t/t_math_repl3_bad.v b/test_regress/t/t_math_repl3_bad.v index b3f86c0ab..b0e33eaca 100644 --- a/test_regress/t/t_math_repl3_bad.v +++ b/test_regress/t/t_math_repl3_bad.v @@ -15,4 +15,7 @@ module t #( end other = {32'bz{1'b1}}; end + + wire ok1 = 1'b1; + wire [6:0] ok7 = {3'b111{ok1}}; // Ok endmodule From 10ac99ac0563c9a95b462b29897597574c453614 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Fri, 25 Jul 2025 12:13:46 +0200 Subject: [PATCH 073/252] Support randomization of scope variables with 'std::randomize()' (#5438) (#6185) --- include/verilated_random.h | 18 +++ src/V3Ast.h | 26 ++-- src/V3AstNodeDType.h | 3 + src/V3AstNodes.cpp | 5 +- src/V3Common.cpp | 3 +- src/V3EmitCFunc.cpp | 2 +- src/V3Randomize.cpp | 138 +++++++++++++++++-- src/V3Randomize.h | 3 + src/V3Width.cpp | 45 +++++- test_regress/t/t_std_randomize.py | 16 +++ test_regress/t/t_std_randomize.v | 81 +++++++++++ test_regress/t/t_std_randomize_bad1.out | 10 ++ test_regress/t/t_std_randomize_bad1.py | 16 +++ test_regress/t/t_std_randomize_bad1.v | 23 ++++ test_regress/t/t_std_randomize_no_args.py | 16 +++ test_regress/t/t_std_randomize_no_args.v | 26 ++++ test_regress/t/t_std_randomize_unsup_bad.out | 10 +- 17 files changed, 409 insertions(+), 32 deletions(-) create mode 100755 test_regress/t/t_std_randomize.py create mode 100644 test_regress/t/t_std_randomize.v create mode 100644 test_regress/t/t_std_randomize_bad1.out create mode 100755 test_regress/t/t_std_randomize_bad1.py create mode 100644 test_regress/t/t_std_randomize_bad1.v create mode 100755 test_regress/t/t_std_randomize_no_args.py create mode 100644 test_regress/t/t_std_randomize_no_args.v diff --git a/include/verilated_random.h b/include/verilated_random.h index 1fdf61629..bc32c73c7 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -580,4 +580,22 @@ public: #endif }; +//============================================================================= +// VlStdRandomizer provides a light wrapper for RNG used by std::randomize() +// to support scope-level randomization. +class VlStdRandomizer final { + // MEMBERS + VlRNG m_rng; // Random number generator + +public: + // CONSTRUCTORS + VlStdRandomizer() = default; + ~VlStdRandomizer() = default; + + template + bool basicStdRandomization(T& value, size_t width) { + value = VL_MASK_I(width) & VL_RANDOM_RNG_I(m_rng); + return true; + } +}; #endif // Guard diff --git a/src/V3Ast.h b/src/V3Ast.h index 910ad9697..cb19127d5 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -591,6 +591,7 @@ public: FORK_SYNC, PROCESS_REFERENCE, RANDOM_GENERATOR, + RANDOM_STDGENERATOR, // Unsigned and two state; fundamental types UINT32, UINT64, @@ -625,6 +626,7 @@ public: "VlFork", "VlProcessRef", "VlRandomizer", + "VlStdRandomizer", "IData", "QData", "LOGIC_IMPLICIT", @@ -632,13 +634,16 @@ public: return names[m_e]; } const char* dpiType() const { - static const char* const names[] - = {"%E-unk", "svBit", "char", "void*", "char", - "int", "%E-integer", "svLogic", "long long", "double", - "short", "%E-time", "const char*", "%E-untyped", "dpiScope", - "const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched", - "%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData", - "QData", "%E-logic-implct", " MAX"}; + static const char* const names[] = {"%E-unk", "svBit", "char", + "void*", "char", "int", + "%E-integer", "svLogic", "long long", + "double", "short", "%E-time", + "const char*", "%E-untyped", "dpiScope", + "const char*", "%E-mtaskstate", "%E-triggervec", + "%E-dly-sched", "%E-trig-sched", "%E-dyn-sched", + "%E-fork", "%E-proc-ref", "%E-rand-gen", + "%E-stdrand-gen", "IData", "QData", + "%E-logic-implct", " MAX"}; return names[m_e]; } static void selfTest() { @@ -679,6 +684,7 @@ public: case FORK_SYNC: return 0; // opaque case PROCESS_REFERENCE: return 0; // opaque case RANDOM_GENERATOR: return 0; // opaque + case RANDOM_STDGENERATOR: return 0; // opaque case UINT32: return 32; case UINT64: return 64; default: return 0; @@ -718,8 +724,8 @@ public: return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER || m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC - || m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE - || m_e == UNTYPED); + || m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR + || m_e == RANDOM_STDGENERATOR || m_e == DOUBLE || m_e == UNTYPED); } bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; } bool isEvent() const { return m_e == EVENT; } @@ -771,6 +777,8 @@ public: /* DYNAMIC_TRIGGER_SCHEDULER: */ "", // Should not be traced /* FORK_SYNC: */ "", // Should not be traced /* PROCESS_REFERENCE: */ "", // Should not be traced + /* RANDOM_GENERATOR: */ "", // Should not be traced + /* RANDOM_STD_GENERATOR: */ "", // Should not be traced /* UINT32: */ "BIT", /* UINT64: */ "BIT", /* LOGIC_IMPLICIT: */ "", // Should not be traced diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 1a43f4ef0..22e272e95 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -473,6 +473,9 @@ public: bool isRandomGenerator() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR; } + bool isStdRandomGenerator() const VL_MT_SAFE { + return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR; + } bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); } bool isString() const VL_MT_STABLE { return keyword().isString(); } bool isZeroInit() const { return keyword().isZeroInit(); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 0caf0d379..d62af4471 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1007,6 +1007,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe info.m_type = "VlProcessRef"; } else if (bdtypep->isRandomGenerator()) { info.m_type = "VlRandomizer"; + } else if (bdtypep->isStdRandomGenerator()) { + info.m_type = "VlStdRandomizer"; } else if (bdtypep->isEvent()) { info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent"; } else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width @@ -3098,7 +3100,8 @@ void AstCMethodHard::setPurity() { {"unique", true}, {"unique_index", true}, {"word", true}, - {"write_var", false}}; + {"write_var", false}, + {"basicStdRandomization", false}}; if (name() == "atWriteAppend" || name() == "atWriteAppendBack") { m_pure = false; diff --git a/src/V3Common.cpp b/src/V3Common.cpp index e41360422..08b310e2b 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -127,7 +127,8 @@ static void makeToStringMiddle(AstClass* nodep) { if (const auto* const varp = VN_CAST(itemp, Var)) { if (!varp->isParam() && !varp->isInternal() && !(varp->dtypeSkipRefp()->basicp() - && varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) { + && (varp->dtypeSkipRefp()->basicp()->isRandomGenerator() + || varp->dtypeSkipRefp()->basicp()->isStdRandomGenerator()))) { string stmt = "out += \""; stmt += comma; comma = ", "; diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 8031e4f5b..833127d24 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -745,7 +745,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing, return ""; } else if (basicp && basicp->isDynamicTriggerScheduler()) { return ""; - } else if (basicp && basicp->isRandomGenerator()) { + } else if (basicp && (basicp->isRandomGenerator() || basicp->isStdRandomGenerator())) { return ""; } else if (basicp) { const bool zeroit diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 7c905f697..30581654a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -55,6 +55,7 @@ enum ClassRandom : uint8_t { NONE, // randomize() is not called IS_RANDOMIZED, // randomize() is called IS_RANDOMIZED_INLINE, // randomize() with args is called + IS_STD_RANDOMIZED, // std::randomize() is called }; // ###################################################################### @@ -118,11 +119,13 @@ class RandomizeMarkVisitor final : public VNVisitor { // NODE STATE // Cleared on Netlist // AstClass::user1() -> bool. Set true to indicate needs randomize processing + // AstNodeModule::user1() -> bool. Set true to indicate needs std::randomize processing // AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a // randomized variable // AstVar::user1() -> bool. Set true to indicate needs rand_mode // AstVar::user2p() -> AstNodeModule*. Pointer to containing module // AstNodeFTask::user2p() -> AstNodeModule*. Pointer to containing module + // AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module const VNUser1InUse m_inuser1; const VNUser2InUse m_inuser2; @@ -367,6 +370,33 @@ class RandomizeMarkVisitor final : public VNVisitor { if (!classp->user1()) classp->user1(IS_RANDOMIZED); markMembers(classp); } + if (nodep->classOrPackagep()->name() == "std") { + for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { + AstArg* const argp = VN_CAST(pinp, Arg); + if (!argp) continue; + AstNodeExpr* exprp = argp->exprp(); + while (exprp) { + AstVar* randVarp = nullptr; + if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) { + randVarp = memberSelp->varp(); + exprp = memberSelp->fromp(); + } else { + AstVarRef* const varrefp = VN_AS(exprp, VarRef); + randVarp = varrefp->varp(); + exprp = nullptr; + } + UASSERT_OBJ(randVarp, nodep, "No rand variable found"); + AstNode* backp = randVarp; + while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) { + backp = backp->backp(); + } + UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp, + "No class or module found for rand variable"); + backp->user1(IS_STD_RANDOMIZED); + } + } + return; + } for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { AstArg* const argp = VN_CAST(pinp, Arg); if (!argp) continue; @@ -431,6 +461,7 @@ class RandomizeMarkVisitor final : public VNVisitor { // of type AstLambdaArgRef. They are randomized too. const bool randObject = nodep->fromp()->user1() || VN_IS(nodep->fromp(), LambdaArgRef); nodep->user1(randObject && nodep->varp()->rand().isRandomizable()); + nodep->user2p(m_modp); } void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); @@ -472,7 +503,8 @@ public: class ConstraintExprVisitor final : public VNVisitor { // NODE STATE // AstVar::user3() -> bool. Handled in constraints - // AstNodeExpr::user1() -> bool. Depending on a randomized variable + // AstNodeExpr::user1() -> bool. Depending on a randomized variable + // AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module // VNuser3InUse m_inuser3; (Allocated for use in RandomizeVisitor) AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to @@ -598,19 +630,25 @@ class ConstraintExprVisitor final : public VNVisitor { // VISITORS void visit(AstNodeVarRef* nodep) override { - AstVar* const varp = nodep->varp(); + AstVar* varp = nodep->varp(); if (varp->user4p()) { varp->user4p()->v3warn( CONSTRAINTIGN, "Size constraint combined with element constraint may not work correctly"); } - + AstMemberSel* membersel = VN_IS(nodep->backp(), MemberSel) + ? VN_AS(nodep->backp(), MemberSel)->cloneTree(false) + : nullptr; + if (membersel) varp = membersel->varp(); AstNodeModule* const classOrPackagep = nodep->classOrPackagep(); const RandomizeMode randMode = {.asInt = varp->user1()}; if (!randMode.usesMode && editFormat(nodep)) return; // In SMT just variable name, but we also ensure write_var for the variable - const std::string smtName = nodep->name(); // Can be anything unique + const std::string smtName = membersel + ? membersel->fromp()->name() + "." + membersel->name() + : nodep->name(); // Can be anything unique + VNRelinker relinker; nodep->unlinkFrBack(&relinker); AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr}; @@ -648,11 +686,12 @@ class ConstraintExprVisitor final : public VNVisitor { dimension = 1; } methodp->dtypeSetVoid(); - AstClass* const classp = VN_AS(varp->user2p(), Class); + AstClass* const classp + = membersel ? VN_AS(membersel->user2p(), Class) : VN_AS(varp->user2p(), Class); AstVarRef* const varRefp = new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE}; varRefp->classOrPackagep(classOrPackagep); - methodp->addPinsp(varRefp); + membersel ? methodp->addPinsp(membersel) : methodp->addPinsp(varRefp); AstNodeDType* tmpDtypep = varp->dtypep(); while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType) || VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType)) @@ -838,6 +877,12 @@ class ConstraintExprVisitor final : public VNVisitor { if (nodep->user1()) { nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)"); } + if (VN_IS(nodep->fromp(), NodeVarRef) && nodep->varp()->isRand() && m_inlineInitTaskp) { + iterateChildren(nodep); + nodep->replaceWith(nodep->fromp()->unlinkFrBack()); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; + } editFormat(nodep); } void visit(AstSFormatF* nodep) override {} @@ -1121,9 +1166,11 @@ class CaptureVisitor final : public VNVisitor { void captureRefByThis(AstNodeVarRef* nodep, CaptureMode capModeFlags) { AstVar* const thisp = importThisp(nodep->fileline()); AstVarRef* const thisRefp = new AstVarRef{nodep->fileline(), thisp, nodep->access()}; + thisRefp->user1(true); m_ignore.emplace(thisRefp); AstMemberSel* const memberSelp = new AstMemberSel(nodep->fileline(), thisRefp, nodep->varp()); + memberSelp->user2p(m_targetp); nodep->replaceWith(memberSelp); VL_DO_DANGLING(pushDeletep(nodep), nodep); m_ignore.emplace(memberSelp); @@ -1249,6 +1296,7 @@ class RandomizeVisitor final : public VNVisitor { // AstConstraint::user3p() -> AstTask*. Pointer to resize procedure // AstClass::user4p() -> AstVar*. Constraint mode state variable // AstVar::user4p() -> AstVar*. Size variable for constrained queues + // AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module // VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor) // VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor) const VNUser3InUse m_inuser3; @@ -1258,8 +1306,10 @@ class RandomizeVisitor final : public VNVisitor { V3UniqueNames m_inlineUniqueNames; // For generating unique function names V3UniqueNames m_modeUniqueNames{"__Vmode"}; // For generating unique rand/constraint // mode state var names + V3UniqueNames m_inlineUniqueStdName{"__VStdrand"}; VMemberMap m_memberMap; // Member names cached for fast lookup AstNodeModule* m_modp = nullptr; // Current module + std::unordered_map m_stdMap; // Map from module/class to AST Var const AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNodeStmt* m_stmtp = nullptr; // Current statement AstDynArrayDType* m_dynarrayDtp = nullptr; // Dynamic array type (for rand mode) @@ -1281,6 +1331,18 @@ class RandomizeVisitor final : public VNVisitor { classp->addMembersp(genp); classp->user3p(genp); } + AstVar* createStdRandomGenerator(AstNodeModule* const modp) { + auto it = m_stdMap.find(modp); + if (it == m_stdMap.end()) { + AstVar* const stdgenp + = new AstVar{modp->fileline(), VVarType::MEMBER, "stdrand", + modp->findBasicDType(VBasicDTypeKwd::RANDOM_STDGENERATOR)}; + modp->addStmtsp(stdgenp); + m_stdMap.emplace(modp, stdgenp); + return stdgenp; + } + return it->second; + } AstVar* getRandomGenerator(AstClass* const classp) { if (classp->user3p()) return VN_AS(classp->user3p(), Var); if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp()); @@ -2184,16 +2246,53 @@ class RandomizeVisitor final : public VNVisitor { if (nodep->name() != "randomize") return; + if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") { + AstVar* const stdrand = createStdRandomGenerator(m_modp); + AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc( + m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep)); + randomizeFuncp->addStmtsp( + new AstAssign{nodep->fileline(), + new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), + VAccess::WRITE}, + new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}}); + for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { + AstArg* const argp = VN_CAST(pinp, Arg); + if (!argp) continue; + AstNodeExpr* exprp = argp->exprp(); + AstCMethodHard* const basicMethodp = new AstCMethodHard{ + nodep->fileline(), + new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE}, + "basicStdRandomization"}; + const size_t width = exprp->width(); + basicMethodp->addPinsp(exprp->unlinkFrBack()); + basicMethodp->addPinsp( + new AstConst{nodep->fileline(), AstConst::Unsized64{}, width}); + basicMethodp->dtypeSetBit(); + randomizeFuncp->addStmtsp(new AstAssign{ + nodep->fileline(), + new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), + VAccess::WRITE}, + new AstAnd{nodep->fileline(), + new AstVarRef{nodep->fileline(), + VN_AS(randomizeFuncp->fvarp(), Var), VAccess::READ}, + basicMethodp}}); + } + // Replace the node with a call to that function + nodep->name(randomizeFuncp->name()); + nodep->taskp(randomizeFuncp); + nodep->dtypeFrom(randomizeFuncp->dtypep()); + if (VN_IS(m_modp, Class)) nodep->classOrPackagep(m_modp); + if (nodep->pinsp()) pushDeletep(nodep->pinsp()->unlinkFrBackWithNext()); + return; + } handleRandomizeArgs(nodep); AstWith* const withp = VN_CAST(nodep->pinsp(), With); - if (!withp) { iterateChildren(nodep); return; } withp->unlinkFrBack(); - iterateChildren(nodep); AstClass* classp = nullptr; @@ -2232,7 +2331,6 @@ class RandomizeVisitor final : public VNVisitor { // Detach the expression and prepare variable copies const CaptureVisitor captured{withp->exprp(), m_modp, classp}; - // Add function arguments captured.addFunctionArguments(randomizeFuncp); @@ -2406,6 +2504,28 @@ AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep, return funcp; } +AstFunc* V3Randomize::newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep, + const std::string& name) { + AstFunc* funcp = nullptr; + v3Global.useRandomizeMethods(true); + AstNodeDType* const dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED); + AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep}; + fvarp->lifetime(VLifetime::AUTOMATIC); + fvarp->funcLocal(true); + fvarp->funcReturn(true); + fvarp->direction(VDirection::OUTPUT); + funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp}; + funcp->dtypep(dtypep); + if (VN_IS(nodep, Class)) { + funcp->classMethod(true); + } else { + funcp->classMethod(false); + funcp->isStatic(true); + } + nodep->addStmtsp(funcp); + return funcp; +} + AstFunc* V3Randomize::newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) { AstClass* const basep = nodep->baseMostClassp(); AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func); diff --git a/src/V3Randomize.h b/src/V3Randomize.h index f37b6a7fe..1fd277e90 100644 --- a/src/V3Randomize.h +++ b/src/V3Randomize.h @@ -23,6 +23,7 @@ class AstClass; class AstFunc; class AstNetlist; +class AstNodeModule; class VMemberMap; @@ -34,6 +35,8 @@ public: const std::string& name = "randomize", bool allowVirtual = true, bool childDType = false) VL_MT_DISABLED; + static AstFunc* newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep, + const std::string& name) VL_MT_DISABLED; static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED; }; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 83ef4b38e..5edaa1cd6 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6282,9 +6282,39 @@ class WidthVisitor final : public VNVisitor { } } } + + void handleStdRandomizeArgs(AstNodeFTaskRef* const nodep) { + AstConst* nullp = nullptr; + for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) { + nextp = pinp->nextp(); + AstArg* const argp = VN_CAST(pinp, Arg); + if (!argp) continue; + AstNodeExpr* const exprp = argp->exprp(); + if (AstConst* const constp = VN_CAST(exprp, Const)) { + if (constp->num().isNull()) { + nullp = constp; + continue; + } + } + if (VN_IS(exprp, MemberSel)) { + // Non-standard usage: std::randomize() with class-scoped member + // IEEE 1800-2023 (18.12) limits args to current scope variables. + // Verilator accepts this for compatibility with other simulators. + continue; + } else if (VN_IS(exprp, VarRef)) { + // Valid usage + continue; + } else { + argp->v3error("Non-variable arguments for 'std::randomize()'."); + } + if (!argp) continue; + } + if (nullp) { nullp->v3error("'std::randomize()' does not accept 'null' as arguments."); } + } void visit(AstNodeFTaskRef* nodep) override { // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign // Function hasn't been widthed, so make it so. + if (nodep->didWidth()) return; UINFO(5, " FTASKREF " << nodep); AstWith* withp = nullptr; if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") { @@ -6297,16 +6327,23 @@ class WidthVisitor final : public VNVisitor { || nodep->name() == "set_randstate"))) { // TODO perhaps this should move to V3LinkDot AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class); - if (!classp) { + if (nodep->classOrPackagep()->name() == "std") { + v3Global.useRandomizeMethods(true); AstNodeDType* const adtypep = nodep->findBitDType(); withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(), adtypep->findBitDType(), adtypep); for (const AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) userIterateAndNext(VN_AS(argp, Arg)->exprp(), WidthVP{SELF, BOTH}.p()); + handleStdRandomizeArgs(nodep); // Provided args should be in current scope + if (withp) { + nodep->v3warn(CONSTRAINTIGN, "Unsupported: std::randomize()'s 'with'"); + nodep->replaceWith(new AstConst{nodep->fileline(), 0}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + processFTaskRefArgs(nodep); nodep->addPinsp(withp); - nodep->v3warn(CONSTRAINTIGN, "std::randomize ignored (unsupported)"); - nodep->replaceWith(new AstConst{nodep->fileline(), 0}); - VL_DO_DANGLING(pushDeletep(nodep), nodep); + nodep->didWidth(true); return; } UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot"); diff --git a/test_regress/t/t_std_randomize.py b/test_regress/t/t_std_randomize.py new file mode 100755 index 000000000..6815786ce --- /dev/null +++ b/test_regress/t/t_std_randomize.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.lint(verilator_flags2=["--binary"]) + +test.passes() diff --git a/test_regress/t/t_std_randomize.v b/test_regress/t/t_std_randomize.v new file mode 100644 index 000000000..d62656019 --- /dev/null +++ b/test_regress/t/t_std_randomize.v @@ -0,0 +1,81 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class std_randomize_class; + + rand bit [7:0] addr; + rand bit [31:0] data; + rand bit [63:0] data_x_4; + + bit [7:0] old_addr; + bit [31:0] old_data; + bit [63:0] old_data_x_4; + + function bit std_randomize(); + int success; + bit valid; + + old_addr = addr; + old_data = data; + old_data_x_4 = data_x_4; + + success = std::randomize(addr, data); + + valid = (success == 1) && !(addr == old_addr || data == old_data) && data_x_4 == old_data_x_4; + + return valid; + endfunction + +endclass + +module t_scope_std_randomize; + bit [7:0] addr; + bit [15:0] data; + + function bit run(); + int ready; + int success; + + bit [7:0] old_addr; + bit [15:0] old_data; + int old_ready; + + old_addr = addr; + old_data = data; + old_ready = ready; + success = randomize(addr, ready); // std::randomize + if (success == 0) return 0; + if (addr == old_addr && data != old_data && ready == old_ready) begin + return 0; + end + return 1; + endfunction + + std_randomize_class test; + + initial begin + bit ok = 0; + int success; + + test = new(); + test.old_addr = test.addr; + test.old_data = test.data; + test.old_data_x_4 = test.data_x_4; + success = std::randomize(test.addr, test.data); + ok = (success == 1) && !(test.addr == test.old_addr || test.data == test.old_data) && test.data_x_4 == test.old_data_x_4; + if (!ok) $stop; + + ok = 0; + ok = run(); + if (!ok) $stop; + ok = 0; + ok = test.std_randomize(); + if (!ok) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_std_randomize_bad1.out b/test_regress/t/t_std_randomize_bad1.out new file mode 100644 index 000000000..57bd3584d --- /dev/null +++ b/test_regress/t/t_std_randomize_bad1.out @@ -0,0 +1,10 @@ +%Error: t/t_std_randomize_bad1.v:12:36: Non-variable arguments for 'std::randomize()'. + : ... note: In instance 't_std_randomize_bad1' + 12 | success = std::randomize(a + 1); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_std_randomize_bad1.v:19:30: 'std::randomize()' does not accept 'null' as arguments. + : ... note: In instance 't_std_randomize_bad1' + 19 | void'(std::randomize(null)); + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_std_randomize_bad1.py b/test_regress/t/t_std_randomize_bad1.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_std_randomize_bad1.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_std_randomize_bad1.v b/test_regress/t/t_std_randomize_bad1.v new file mode 100644 index 000000000..5097f59e6 --- /dev/null +++ b/test_regress/t/t_std_randomize_bad1.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +module t_std_randomize_bad1; + bit [3:0] a; + + function int run(); + int success; + success = std::randomize(a + 1); // ERROR: argument is not a variable + return success; + endfunction + + initial begin + int ok, x; + ok = run(); + void'(std::randomize(null)); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_std_randomize_no_args.py b/test_regress/t/t_std_randomize_no_args.py new file mode 100755 index 000000000..6815786ce --- /dev/null +++ b/test_regress/t/t_std_randomize_no_args.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.lint(verilator_flags2=["--binary"]) + +test.passes() diff --git a/test_regress/t/t_std_randomize_no_args.v b/test_regress/t/t_std_randomize_no_args.v new file mode 100644 index 000000000..e2b52504c --- /dev/null +++ b/test_regress/t/t_std_randomize_no_args.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +module t_no_args; + bit [7:0] addr; + bit [15:0] data; + bit [7:0] old_addr; + bit [15:0] old_data; + int success; + bit valid; + + initial begin + old_addr = addr; + old_data = data; + + success = std::randomize(); + valid = (success == 1) && (addr == old_addr) && (data == old_data); + if (!valid) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_std_randomize_unsup_bad.out b/test_regress/t/t_std_randomize_unsup_bad.out index ebaa0ee6a..b939e9951 100644 --- a/test_regress/t/t_std_randomize_unsup_bad.out +++ b/test_regress/t/t_std_randomize_unsup_bad.out @@ -1,11 +1,7 @@ -%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:10:16: std::randomize ignored (unsupported) - : ... note: In instance 't' - 10 | if (std::randomize(a, b) != 1) $stop; - | ^~~~~~~~~ - ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest - ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:11:16: std::randomize ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:11:16: Unsupported: std::randomize()'s 'with' : ... note: In instance 't' 11 | if (std::randomize(a, b) with { 2 < a; a < 7; b < a; } != 1) $stop; | ^~~~~~~~~ + ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest + ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. %Error: Exiting due to From fa62cd34863fe9fe91e833aebe3289a9c9677904 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 25 Jul 2025 07:23:02 -0400 Subject: [PATCH 074/252] Fix `--stats` overridden by skipping identical build (#6220). --- Changes | 1 + src/Verilator.cpp | 12 ++++++---- test_regress/t/t_flag_skipidentical.py | 32 ++++++++++++++++---------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Changes b/Changes index b857da768..b1d19faac 100644 --- a/Changes +++ b/Changes @@ -45,6 +45,7 @@ Verilator 5.039 devel * Fix VPI signal range order (#6189) (#6200). [Ibrahim Burak Yorulmaz] * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] * Fix 'driver same component' assertion (#6211) (#6215). [Geza Lore] +* Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] Verilator 5.038 2025-07-08 diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 3ef409ebc..2e1be9ac0 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -651,7 +651,8 @@ static void process() { if (v3Global.opt.stats()) V3Stats::statsStage("emit"); } -static void verilate(const string& argString) { +static bool verilate(const string& argString) { + // Run verilation, and return false if skipped UINFO(1, "Option --verilate: Start Verilation"); // Can we skip doing everything if times are ok? @@ -661,7 +662,7 @@ static void verilate(const string& argString) { + "__verFiles.dat", argString)) { UINFO(1, "--skip-identical: No change to any source files, exiting"); - return; + return false; } // Undocumented debugging - cannot be a switch as then command line // would mismatch forcing non-identicalness when we set it @@ -775,10 +776,12 @@ static void verilate(const string& argString) { V3Error::abortIfWarnings(); // Free memory so compiler has more for --build + // No need to do this if skipped (above) as didn't alloc much UINFO(1, "Releasing netlist memory"); v3Global.rootp()->deleteContents(); V3Os::releaseMemory(); if (v3Global.opt.stats()) V3Stats::statsStage("released"); + return true; } static string buildMakeCmd(const string& makefile, const string& target) { @@ -862,8 +865,9 @@ int main(int argc, char** argv) { V3Error::abortIfErrors(); + bool didVerilate = false; if (v3Global.opt.verilate()) { - verilate(argString); + didVerilate = verilate(argString); } else { UINFO(1, "Option --no-verilate: Skip Verilation"); } @@ -874,7 +878,7 @@ int main(int argc, char** argv) { execBuildJob(); } - reportStatsIfEnabled(); + if (didVerilate) reportStatsIfEnabled(); V3DiagSarif::output(true); // Explicitly release resources diff --git a/test_regress/t/t_flag_skipidentical.py b/test_regress/t/t_flag_skipidentical.py index 5b20bd00b..c69949f33 100755 --- a/test_regress/t/t_flag_skipidentical.py +++ b/test_regress/t/t_flag_skipidentical.py @@ -10,27 +10,35 @@ import vltest_bootstrap import time +FileTimes = {} + + +def prep_output_file(filename): + oldstats = os.path.getmtime(filename) + if not oldstats: + test.error("No output file found: " + filename) + print("Old %s mtime=%d" % (filename, oldstats)) + FileTimes[filename] = oldstats + + test.scenarios('vlt') -test.compile() +test.compile(verilator_flags2=['--stats']) print("NOTE: use --debugi, as --debug in driver turns off skip-identical") -outfile = test.obj_dir + "/V" + test.name + ".cpp" -oldstats = os.path.getmtime(outfile) -if not oldstats: - test.error("No output file found: " + outfile) -print("Old mtime=", oldstats) +prep_output_file(test.obj_dir + "/V" + test.name + ".cpp") +prep_output_file(test.obj_dir + "/V" + test.name + "__stats.txt") time.sleep(2) # Or else it might take < 1 second to compile and see no diff. test.setenv('VERILATOR_DEBUG_SKIP_IDENTICAL', "1") -test.compile() +test.compile(verilator_flags2=['--stats']) -newstats = os.path.getmtime(outfile) -print("New mtime=", newstats) - -if oldstats != newstats: - test.error("--skip-identical was ignored -- recompiled") +for filename, oldtime in FileTimes.items(): + newstats = os.path.getmtime(filename) + print("New %s mtime=%d" % (filename, newstats)) + if oldtime != newstats: + test.error("--skip-identical was ignored -- regenerated %s" % (filename)) test.passes() From f1826a7c205fb89bd756dbfd372271f1a728dbcc Mon Sep 17 00:00:00 2001 From: George Polack Date: Fri, 25 Jul 2025 14:05:36 -0400 Subject: [PATCH 075/252] Support Verilog real to SystemC double (#6136) (#6158) --- docs/CONTRIBUTORS | 1 + include/verilated_funcs.h | 14 +++++- src/V3AstNodeDType.h | 2 +- src/V3AstNodes.cpp | 2 + src/V3EmitCFunc.h | 1 + test_regress/t/t_var_sc_double.cpp | 75 ++++++++++++++++++++++++++++++ test_regress/t/t_var_sc_double.py | 20 ++++++++ test_regress/t/t_var_sc_double.v | 17 +++++++ 8 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 test_regress/t/t_var_sc_double.cpp create mode 100755 test_regress/t/t_var_sc_double.py create mode 100644 test_regress/t/t_var_sc_double.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 553cfa711..f20d2519b 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -66,6 +66,7 @@ Fuad Ismail Furqan Nadir G-A. Kamendje Garrett Smith +George Polack Geza Lore Gianfranco Costamagna Gijs Burghoorn diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 03b3ed685..2bcb7f5fc 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -458,9 +458,11 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { //=================================================================== // SYSTEMC OPERATORS -// Copying verilog format to systemc integers and bit vectors. +// Copying verilog format to systemc integers, doubles, and bit vectors. // Get a SystemC variable +#define VL_ASSIGN_DSD(obits, vvar, svar) \ + { (vvar) = (svar).read(); } #define VL_ASSIGN_ISI(obits, vvar, svar) \ { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } #define VL_ASSIGN_QSQ(obits, vvar, svar) \ @@ -504,9 +506,11 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { (owp)[words - 1] &= VL_MASK_E(obits); \ } -// Copying verilog format from systemc integers and bit vectors. +// Copying verilog format from systemc integers, doubles, and bit vectors. // Set a SystemC variable +#define VL_ASSIGN_SDD(obits, svar, vvar) \ + { (svar).write(vvar); } #define VL_ASSIGN_SII(obits, svar, vvar) \ { (svar).write(vvar); } #define VL_ASSIGN_SQQ(obits, svar, vvar) \ @@ -2804,6 +2808,12 @@ extern void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int bool hasSuffix, const std::string& suffix, bool hasWidth, int width, VerilatedContext* contextp) VL_MT_SAFE; extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; +inline IData VL_VALUEPLUSARGS_IND(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); + return got; +} inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { VlWide<2> rwp; const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 22e272e95..74cc9d005 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -162,7 +162,7 @@ public: uint32_t arrayUnpackedElements() const; // 1, or total multiplication of all dimensions static int uniqueNumInc() { return ++s_uniqueNum; } const char* charIQWN() const { - return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); + return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I"); } string cType(const string& name, bool forFunc, bool isRef, bool packed = false) const; // Represents a C++ LiteralType? (can be constexpr) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d62af4471..4d21a7392 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -818,6 +818,8 @@ string AstVar::scType() const { } else { return "uint32_t"; } + } else if (isDouble()) { + return "double"; } else { return "uint64_t"; } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 348e2f558..c5e5c1efb 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -207,6 +207,7 @@ public: puts(nodep->isScBigUint() ? "SB" : (nodep->isScUint() || nodep->isScUintBool()) ? "SU" : nodep->isScBv() ? "SW" + : nodep->isDouble() ? "SD" : (nodep->isScQuad() ? "SQ" : "SI")); } void emitDatap(AstNode* nodep) { diff --git a/test_regress/t/t_var_sc_double.cpp b/test_regress/t/t_var_sc_double.cpp new file mode 100644 index 000000000..d8642ac12 --- /dev/null +++ b/test_regress/t/t_var_sc_double.cpp @@ -0,0 +1,75 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by George Polack. +// SPDX-License-Identifier: CC0-1.0 + +#include VM_PREFIX_INCLUDE +#include +#include + +using namespace sc_core; +using namespace sc_dt; + +VM_PREFIX* tb = nullptr; +bool pass = true; + +double sc_time_stamp() { return 0; } + +void compareDoubles(double const lwp, double const rwp, double epsilon = std::numeric_limits::epsilon()) { + auto diff = std::fabs(lwp - rwp); + if (diff >= epsilon) { + pass &= false; + VL_PRINTF("%%Error: There is a difference of %f, in double variables\n", diff); + } +} + +#ifdef SYSTEMC_VERSION +int sc_main(int, char**) +#else +int main() +#endif +{ + Verilated::debug(0); + tb = new VM_PREFIX{"tb"}; + + double input_var = 1.5; + double out_var; + +#ifdef SYSTEMC_VERSION + // clang-format off + sc_signal SC_NAMED(i_a), SC_NAMED(o_z); + + tb->i_a(i_a); tb->o_z(o_z); + // clang-format on +#endif + +#ifdef SYSTEMC_VERSION + sc_start(1, SC_NS); +#else + tb->eval(); +#endif + + // This testcase is testing conversion to/from Verilog real to/from SystemC double. + VL_ASSIGN_SDD(0, i_a, input_var); + +#ifdef SYSTEMC_VERSION + sc_start(1, SC_NS); +#else + tb->eval(); +#endif + + VL_ASSIGN_DSD(0, out_var, o_z); + + compareDoubles(input_var, out_var); + + tb->final(); + VL_DO_DANGLING(delete tb, tb); + + if (pass) { + VL_PRINTF("*-* All Finished *-*\n"); + } else { + vl_fatal(__FILE__, __LINE__, "top", "Unexpected results from test\n"); + } + return 0; +} diff --git a/test_regress/t/t_var_sc_double.py b/test_regress/t/t_var_sc_double.py new file mode 100755 index 000000000..fb4049cf1 --- /dev/null +++ b/test_regress/t/t_var_sc_double.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.compile(make_top_shell=False, + make_main=False, + verilator_flags2=["--exe", test.pli_filename, "--sc -fno-inline"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_var_sc_double.v b/test_regress/t/t_var_sc_double.v new file mode 100644 index 000000000..c140ce083 --- /dev/null +++ b/test_regress/t/t_var_sc_double.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by George Polack. +// SPDX-License-Identifier: CC0-1.0 + +module t ( + // Outputs + o_z, + // Inputs + i_a + ); + input real i_a; + output real o_z; + + assign o_z = i_a; +endmodule From 20bd80698d461aab2bcada66cf0a54ec5907737e Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 25 Jul 2025 18:06:24 +0000 Subject: [PATCH 076/252] Apply 'make format' --- test_regress/t/t_var_sc_double.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_regress/t/t_var_sc_double.cpp b/test_regress/t/t_var_sc_double.cpp index d8642ac12..70a787e6d 100644 --- a/test_regress/t/t_var_sc_double.cpp +++ b/test_regress/t/t_var_sc_double.cpp @@ -16,7 +16,8 @@ bool pass = true; double sc_time_stamp() { return 0; } -void compareDoubles(double const lwp, double const rwp, double epsilon = std::numeric_limits::epsilon()) { +void compareDoubles(double const lwp, double const rwp, + double epsilon = std::numeric_limits::epsilon()) { auto diff = std::fabs(lwp - rwp); if (diff >= epsilon) { pass &= false; From d289934ca993ff3455ed06aff3459882a65ab4ca Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 02:43:57 -0400 Subject: [PATCH 077/252] Improve `--skip-identical` to skip on identical input file contents (#6109). --- Changes | 1 + src/V3File.cpp | 107 ++++++++++++++++--------- src/V3String.cpp | 19 +++++ src/V3String.h | 1 + test_regress/t/t_flag_skipidentical.py | 25 ++++-- test_regress/t/t_flag_skipidentical.v | 3 +- 6 files changed, 110 insertions(+), 46 deletions(-) diff --git a/Changes b/Changes index b1d19faac..98e3cd8a8 100644 --- a/Changes +++ b/Changes @@ -24,6 +24,7 @@ Verilator 5.039 devel * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). +* Improve `--skip-identical` to skip on identical input file contents (#6109). * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] * Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] diff --git a/src/V3File.cpp b/src/V3File.cpp index 5997b29ef..10b3232d6 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -60,6 +60,8 @@ VL_DEFINE_DEBUG_FUNCTIONS; constexpr int INFILTER_IPC_BUFSIZ = (64 * 1024); // For debug, try this as a small number constexpr int INFILTER_CACHE_MAX = (64 * 1024); // Maximum bytes to cache if same file read twice +constexpr off_t FILE_HASH_SIZE_MAX = 1 * 1024 * 1024; // Maxium size file to hash + //###################################################################### // V3File Internal state @@ -71,6 +73,7 @@ class V3FileDependImp final { bool m_exists = true; const string m_filename; // Filename struct stat m_stat; // Stat information + VHashSha256 m_hash; // SHA hash of file contents public: DependFile(const string& filename, bool target) : m_target{target} @@ -88,6 +91,11 @@ class V3FileDependImp final { time_t cnstime() const { return VL_STAT_CTIME_NSEC(m_stat); } // Nanoseconds time_t mstime() const { return m_stat.st_mtime; } // Seconds time_t mnstime() const { return VL_STAT_MTIME_NSEC(m_stat); } // Nanoseconds + string hashDigestSymbol() { + static VHashSha256 emptyHash; + return m_hash.digestSymbol() != emptyHash.digestSymbol() ? m_hash.digestSymbol() + : "unhashed"; + } void loadStats() { if (!m_stat.st_mtime) { const string fn = filename(); @@ -98,6 +106,9 @@ class V3FileDependImp final { m_exists = false; // Not an error... This can occur due to `line directives in the .vpp files UINFO(1, "-Info: File not statable: " << filename()); + } else { + // For speed, don't hash large files (e.g. verilator_bin) + if (!target() && size() <= FILE_HASH_SIZE_MAX) m_hash.insertFile(filename()); } } } @@ -186,28 +197,28 @@ void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn << "IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will.\n"; *ofp << "C \"" << cmdline << "\"\n"; - for (std::set::iterator iter = m_filenameList.begin(); - iter != m_filenameList.end(); ++iter) { + for (auto& it : m_filenameList) { + DependFile* const depfilep = const_cast(&it); // Read stats of files we create after we're done making them // (except for this file, of course) - DependFile* const dfp = const_cast(&(*iter)); - V3Os::filesystemFlush(dfp->filename()); - dfp->loadStats(); - off_t showSize = iter->size(); - ino_t showIno = iter->ino(); - if (dfp->filename() == filename) { + V3Os::filesystemFlush(depfilep->filename()); + depfilep->loadStats(); + off_t showSize = depfilep->size(); + ino_t showIno = depfilep->ino(); + if (depfilep->filename() == filename) { showSize = 0; showIno = 0; // We're writing it, so need to ignore it } - *ofp << (iter->target() ? "T" : "S") << " "; + *ofp << (depfilep->target() ? "T" : "S") << " "; *ofp << " " << std::setw(8) << showSize; *ofp << " " << std::setw(8) << showIno; - *ofp << " " << std::setw(11) << iter->cstime(); - *ofp << " " << std::setw(11) << iter->cnstime(); - *ofp << " " << std::setw(11) << iter->mstime(); - *ofp << " " << std::setw(11) << iter->mnstime(); - *ofp << " \"" << iter->filename() << "\""; + *ofp << " " << std::setw(11) << depfilep->cstime(); + *ofp << " " << std::setw(11) << depfilep->cnstime(); + *ofp << " " << std::setw(11) << depfilep->mstime(); + *ofp << " " << std::setw(11) << depfilep->mnstime(); + *ofp << " \"" << depfilep->hashDigestSymbol() << "\""; + *ofp << " \"" << depfilep->filename() << "\""; *ofp << '\n'; } } @@ -236,6 +247,8 @@ bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn } } + const bool skipHashing = !V3Os::getenvStr("VERILATOR_DEBUG_SKIP_HASH", "").empty(); + while (!ifp->eof()) { char chkDir; *ifp >> chkDir; @@ -254,39 +267,55 @@ bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn *ifp >> chkMnstime; char quote; *ifp >> quote; + const string chkHash = V3Os::getline(*ifp, '"'); + *ifp >> quote; const string chkFilename = V3Os::getline(*ifp, '"'); V3Os::filesystemFlush(chkFilename); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - struct stat chkStat; - const int err = stat(chkFilename.c_str(), &chkStat); + struct stat curStat; + const int err = stat(chkFilename.c_str(), &curStat); if (err != 0) { UINFO(2, " --check-times failed: missing " << chkFilename); return false; } - // UINFO(9," got d="< // open, read, write, close +#endif + #include "V3String.h" #include "V3Error.h" @@ -28,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; #endif #include +#include size_t VName::s_minLength = 32; size_t VName::s_maxLength = 0; // Disabled @@ -494,6 +499,20 @@ void VHashSha256::insert(const void* datap, size_t length) { m_remainder = std::string(reinterpret_cast(chunkp + posBegin), chunkLen - posEnd); } +void VHashSha256::insertFile(const string& filename) { + static const size_t BUFFER_SIZE = 64 * 1024; + + const int fd = ::open(filename.c_str(), O_RDONLY); + if (fd < 0) return; + + std::array buf; + while (const size_t got = ::read(fd, &buf, BUFFER_SIZE)) { + if (got <= 0) break; + insert(&buf, got); + } + ::close(fd); +} + void VHashSha256::finalize() { if (!m_final) { // Make sure no 64 byte blocks left diff --git a/src/V3String.h b/src/V3String.h index f40b46b8a..537dea287 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -190,6 +190,7 @@ public: insert(data.data(), data.length()); } // Process data into the digest void insert(uint64_t value) { insert(cvtToStr(value)); } + void insertFile(const string& filename); private: static void selfTestOne(const string& data, const string& data2, const string& exp, diff --git a/test_regress/t/t_flag_skipidentical.py b/test_regress/t/t_flag_skipidentical.py index c69949f33..59c1f403d 100755 --- a/test_regress/t/t_flag_skipidentical.py +++ b/test_regress/t/t_flag_skipidentical.py @@ -21,8 +21,17 @@ def prep_output_file(filename): FileTimes[filename] = oldstats +def check_times(): + for filename, oldtime in FileTimes.items(): + newstats = os.path.getmtime(filename) + print("New %s mtime=%d" % (filename, newstats)) + if oldtime != newstats: + test.error("--skip-identical was ignored -- regenerated %s" % (filename)) + + test.scenarios('vlt') +test.setenv('VERILATOR_DEBUG_SKIP_HASH', "1") test.compile(verilator_flags2=['--stats']) print("NOTE: use --debugi, as --debug in driver turns off skip-identical") @@ -32,13 +41,19 @@ prep_output_file(test.obj_dir + "/V" + test.name + "__stats.txt") time.sleep(2) # Or else it might take < 1 second to compile and see no diff. +print("\nTest skip without hash fallback") test.setenv('VERILATOR_DEBUG_SKIP_IDENTICAL', "1") +test.setenv('VERILATOR_DEBUG_SKIP_HASH', "1") test.compile(verilator_flags2=['--stats']) +check_times() -for filename, oldtime in FileTimes.items(): - newstats = os.path.getmtime(filename) - print("New %s mtime=%d" % (filename, newstats)) - if oldtime != newstats: - test.error("--skip-identical was ignored -- regenerated %s" % (filename)) +time.sleep(2) # Or else it might take < 1 second to compile and see no diff. + +print("\nTest skip with hash fallback") +os.utime(test.top_filename, None) +test.setenv('VERILATOR_DEBUG_SKIP_IDENTICAL', "1") +test.setenv('VERILATOR_DEBUG_SKIP_HASH', "") +test.compile(verilator_flags2=['--stats']) +check_times() test.passes() diff --git a/test_regress/t/t_flag_skipidentical.v b/test_regress/t/t_flag_skipidentical.v index e5613cfd9..461271fca 100644 --- a/test_regress/t/t_flag_skipidentical.v +++ b/test_regress/t/t_flag_skipidentical.v @@ -4,6 +4,5 @@ // any use, without warranty, 2006 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/); - +module t; endmodule From 94ef630490d0a403f1e75f67f5c5a27608054bd2 Mon Sep 17 00:00:00 2001 From: github action Date: Sat, 26 Jul 2025 06:45:26 +0000 Subject: [PATCH 078/252] Apply 'make format' --- src/V3String.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/V3String.cpp b/src/V3String.cpp index 620300b4e..6a0e54215 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -18,13 +18,12 @@ #include "verilatedos.h" #if defined(_WIN32) || defined(__MINGW32__) -# include // open, read, write, close +#include // open, read, write, close #endif -#include "V3String.h" - #include "V3Error.h" #include "V3FileLine.h" +#include "V3String.h" #ifndef V3ERROR_NO_GLOBAL_ #include "V3Global.h" From 504884b7d58291e9ec5077b653b3cf890aa84810 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 26 Jul 2025 20:37:01 +0100 Subject: [PATCH 079/252] Refactor DFG context objects (#6232) - Move All DFG context objects to V3DfgContext.h - Add separate object for ast2dfg and dfg2ast passes - Factor out commonalities No functional change --- src/CMakeLists.txt | 3 +- src/V3DfgAstToDfg.cpp | 14 +- src/V3DfgBreakCycles.cpp | 4 +- src/V3DfgContext.h | 286 ++++++++++++++++++ src/V3DfgDfgToAst.cpp | 12 +- src/V3DfgOptimizer.cpp | 4 +- src/V3DfgPasses.cpp | 88 +----- src/V3DfgPasses.h | 111 +------ src/V3DfgPeephole.cpp | 13 +- ...3DfgPeephole.h => V3DfgPeepholePatterns.h} | 12 - test_regress/t/t_dfg_peephole.py | 2 +- test_regress/t/t_dfg_unhandled.py | 2 +- 12 files changed, 321 insertions(+), 230 deletions(-) create mode 100644 src/V3DfgContext.h rename src/{V3DfgPeephole.h => V3DfgPeepholePatterns.h} (94%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c224960dd..b0d44b037 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,10 +72,11 @@ set(HEADERS V3Descope.h V3Dfg.h V3DfgCache.h + V3DfgContext.h V3DfgOptimizer.h V3DfgPasses.h V3DfgPatternStats.h - V3DfgPeephole.h + V3DfgPeepholePatterns.h V3DfgVertices.h V3DiagSarif.h V3DupFinder.h diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index f5f4f03ed..b6c91953f 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -97,7 +97,7 @@ class AstToDfgVisitor final : public VNVisitor { // STATE DfgGraph* const m_dfgp; // The graph being built - V3DfgOptimizationContext& m_ctx; // The optimization context for stats + V3DfgAstToDfgContext& m_ctx; // The context for stats bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit' std::vector m_uncommittedVertices; // Vertices that we might decide to revert bool m_converting = false; // We are trying to convert some logic at the moment @@ -886,7 +886,7 @@ class AstToDfgVisitor final : public VNVisitor { } // CONSTRUCTOR - explicit AstToDfgVisitor(RootType& root, V3DfgOptimizationContext& ctx) + explicit AstToDfgVisitor(RootType& root, V3DfgAstToDfgContext& ctx) : m_dfgp{makeDfg(root)} , m_ctx{ctx} { // Build the DFG @@ -922,15 +922,15 @@ class AstToDfgVisitor final : public VNVisitor { } public: - static DfgGraph* apply(RootType& root, V3DfgOptimizationContext& ctx) { + static DfgGraph* apply(RootType& root, V3DfgAstToDfgContext& ctx) { return AstToDfgVisitor{root, ctx}.m_dfgp; } }; -DfgGraph* V3DfgPasses::astToDfg(AstModule& module, V3DfgOptimizationContext& ctx) { - return AstToDfgVisitor::apply(module, ctx); +DfgGraph* V3DfgPasses::astToDfg(AstModule& module, V3DfgContext& ctx) { + return AstToDfgVisitor::apply(module, ctx.m_ast2DfgContext); } -DfgGraph* V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgOptimizationContext& ctx) { - return AstToDfgVisitor::apply(netlist, ctx); +DfgGraph* V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgContext& ctx) { + return AstToDfgVisitor::apply(netlist, ctx.m_ast2DfgContext); } diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 46a53c6c2..1a009d586 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -1109,8 +1109,8 @@ public: } }; -std::pair, bool> -V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgOptimizationContext& ctx) { +std::pair, bool> // +V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Shorthand for dumping graph at given dump level const auto dump = [&](int level, const DfgGraph& dfg, const std::string& name) { if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-" + name); diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h new file mode 100644 index 000000000..459aae2fd --- /dev/null +++ b/src/V3DfgContext.h @@ -0,0 +1,286 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Persistent context for DFG algorithms +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Various context objects hold data that need to persist across invocations +// of a DFG algorithms. +// +//************************************************************************* + +#ifndef VERILATOR_V3DFGCONTEXT_H_ +#define VERILATOR_V3DFGCONTEXT_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3DfgPatternStats.h" +#include "V3DfgPeepholePatterns.h" +#include "V3File.h" +#include "V3Stats.h" +#include "V3String.h" + +class V3DfgContext; + +////////////////////////////////////////////////////////////////////////////// +// Base class for all context objects +////////////////////////////////////////////////////////////////////////////// + +class V3DfgSubContext VL_NOT_FINAL { + V3DfgContext& m_ctx; // The whole context + const std::string m_label; // Label to add to stats, etc. + const std::string m_name; // Pass/algorithm name, to be used in statistics + +protected: + VL_DEFINE_DEBUG_FUNCTIONS; + + V3DfgSubContext(V3DfgContext& ctx, const std::string& label, const char* name) + : m_ctx{ctx} + , m_label{label} + , m_name{name} {} + + void addStat(const std::string& what, double value) { + V3Stats::addStat("Optimizations, DFG " + m_label + " " + m_name + ", " + what, value); + } + +public: + inline const std::string& prefix() const; + const std::string& label() const { return m_label; } +}; + +////////////////////////////////////////////////////////////////////////////// +// Contexts for various algorithms - keep sorted +////////////////////////////////////////////////////////////////////////////// + +class V3DfgAstToDfgContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_coalescedAssignments; // Number of partial assignments coalesced + VDouble0 m_inputEquations; // Number of input combinational equations + VDouble0 m_representable; // Number of combinational equations representable + VDouble0 m_nonRepDType; // Equations non-representable due to data type + VDouble0 m_nonRepImpure; // Equations non-representable due to impure node + VDouble0 m_nonRepTiming; // Equations non-representable due to timing control + VDouble0 m_nonRepLhs; // Equations non-representable due to lhs + VDouble0 m_nonRepNode; // Equations non-representable due to node type + VDouble0 m_nonRepUnknown; // Equations non-representable due to unknown node + VDouble0 m_nonRepVarRef; // Equations non-representable due to variable reference + VDouble0 m_nonRepWidth; // Equations non-representable due to width mismatch + +private: + V3DfgAstToDfgContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "AstToDfg"} {} + ~V3DfgAstToDfgContext() { + addStat("coalesced assignments", m_coalescedAssignments); + addStat("input equations", m_inputEquations); + addStat("representable", m_representable); + addStat("non-representable (dtype)", m_nonRepDType); + addStat("non-representable (impure)", m_nonRepImpure); + addStat("non-representable (timing)", m_nonRepTiming); + addStat("non-representable (lhs)", m_nonRepLhs); + addStat("non-representable (node)", m_nonRepNode); + addStat("non-representable (unknown)", m_nonRepUnknown); + addStat("non-representable (var ref)", m_nonRepVarRef); + addStat("non-representable (width)", m_nonRepWidth); + + // Check the stats are consistent + UASSERT(m_representable // + + m_nonRepDType // + + m_nonRepImpure // + + m_nonRepTiming // + + m_nonRepLhs // + + m_nonRepNode // + + m_nonRepUnknown // + + m_nonRepVarRef // + + m_nonRepWidth // + == m_inputEquations, + "Inconsistent statistics"); + } +}; +class V3DfgBinToOneHotContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_decodersCreated; // Number of bianry to one-hot decoders created + +private: + V3DfgBinToOneHotContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "BinToOneHot"} {} + ~V3DfgBinToOneHotContext() { addStat("decoders created", m_decodersCreated); } +}; +class V3DfgBreakCyclesContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_nFixed; // Number of graphs that became acyclic + VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic + VDouble0 m_nUnchanged; // Number of graphs that were left unchanged + VDouble0 m_nTrivial; // Number of graphs that were not changed + VDouble0 m_nImprovements; // Number of changes made to graphs + +private: + V3DfgBreakCyclesContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "BreakCycles"} {} + ~V3DfgBreakCyclesContext() { + addStat("made acyclic", m_nFixed); + addStat("improved", m_nImproved); + addStat("left unchanged", m_nUnchanged); + addStat("trivial", m_nTrivial); + addStat("changes applied", m_nImprovements); + } +}; +class V3DfgCseContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_eliminated; // Number of common sub-expressions eliminated + +private: + V3DfgCseContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "CSE"} {} + ~V3DfgCseContext() { addStat("expressions eliminated", m_eliminated); } +}; + +class V3DfgDfgToAstContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_resultEquations; // Number of result combinational equations + +private: + V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "Dfg2Ast"} {} + ~V3DfgDfgToAstContext() { addStat("result equations", m_resultEquations); } +}; +class V3DfgEliminateVarsContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_varsReplaced; // Number of variables replaced + VDouble0 m_varsRemoved; // Number of variables removed + +private: + V3DfgEliminateVarsContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "EliminateVars"} {} + ~V3DfgEliminateVarsContext() { + addStat("variables replaced", m_varsReplaced); + addStat("variables removed", m_varsRemoved); + } +}; +class V3DfgPeepholeContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + // Enable flags for each optimization + std::array m_enabled; + // Count of applications for each optimization (for statistics) + std::array m_count; + +private: + V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label) VL_MT_DISABLED; + ~V3DfgPeepholeContext() VL_MT_DISABLED; +}; +class V3DfgRegularizeContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + VDouble0 m_temporariesIntroduced; // Number of temporaries introduced + +private: + V3DfgRegularizeContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "Regularize"} {} + ~V3DfgRegularizeContext() { addStat("temporaries introduced", m_temporariesIntroduced); } +}; + +////////////////////////////////////////////////////////////////////////////// +// Top level V3DfgContext +////////////////////////////////////////////////////////////////////////////// + +class V3DfgContext final { + const std::string m_label; // Label to add to stats, etc. + const std::string m_prefix; // Prefix to add to file dumps (derived from label) + +public: + // STATE + + // Global statistics + VDouble0 m_modules; // Number of modules optimized + + // Sub contexts - keep sorted by type + V3DfgAstToDfgContext m_ast2DfgContext{*this, m_label}; + V3DfgBinToOneHotContext m_binToOneHotContext{*this, m_label}; + V3DfgBreakCyclesContext m_breakCyclesContext{*this, m_label}; + V3DfgCseContext m_cseContext0{*this, m_label + " 1st"}; + V3DfgCseContext m_cseContext1{*this, m_label + " 2nd"}; + V3DfgDfgToAstContext m_dfg2AstContext{*this, m_label}; + V3DfgEliminateVarsContext m_eliminateVarsContext{*this, m_label}; + V3DfgPeepholeContext m_peepholeContext{*this, m_label}; + V3DfgRegularizeContext m_regularizeContext{*this, m_label}; + + // Node pattern collector + V3DfgPatternStats m_patternStats; + + // CONSTRUCTOR + explicit V3DfgContext(const std::string& label) + : m_label{label} + , m_prefix{VString::removeWhitespace(label) + "-"} {} + + ~V3DfgContext() { + const string prefix = "Optimizations, DFG " + label() + " General, "; + V3Stats::addStat(prefix + "modules", m_modules); + + // Print the collected patterns + if (v3Global.opt.stats()) { + // Label to lowercase, without spaces + std::string ident = label(); + std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { // + return c == ' ' ? '_' : std::tolower(c); + }); + + // File to dump to + const std::string filename = v3Global.opt.hierTopDataDir() + "/" + + v3Global.opt.prefix() + "__stats_dfg_patterns__" + ident + + ".txt"; + // Open, write, close + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; + if (ofp->fail()) v3fatal("Can't write file: " << filename); + m_patternStats.dump(label(), *ofp); + } + } + + // ACCESSORS + const std::string& label() const { return m_label; } + const std::string& prefix() const { return m_prefix; } +}; + +const std::string& V3DfgSubContext::prefix() const { return m_ctx.prefix(); } + +#endif //VERILATOR_V3DFGCONTEXT_H_ diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 520f42622..7b1cab73a 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -138,7 +138,7 @@ class DfgToAstVisitor final : DfgVisitor { // STATE AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped - V3DfgOptimizationContext& m_ctx; // The optimization context for stats + V3DfgDfgToAstContext& m_ctx; // The context for stats AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal // METHODS @@ -276,7 +276,7 @@ class DfgToAstVisitor final : DfgVisitor { #include "V3Dfg__gen_dfg_to_ast.h" // Constructor - explicit DfgToAstVisitor(DfgGraph& dfg, V3DfgOptimizationContext& ctx) + explicit DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx) : m_modp{dfg.modulep()} , m_ctx{ctx} { // Convert the graph back to combinational assignments @@ -297,13 +297,13 @@ class DfgToAstVisitor final : DfgVisitor { } public: - static void apply(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { DfgToAstVisitor{dfg, ctx}; } + static void apply(DfgGraph& dfg, V3DfgDfgToAstContext& ctx) { DfgToAstVisitor{dfg, ctx}; } }; -void V3DfgPasses::dfgToAst(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { +void V3DfgPasses::dfgToAst(DfgGraph& dfg, V3DfgContext& ctx) { if (dfg.modulep()) { - DfgToAstVisitor::apply(dfg, ctx); + DfgToAstVisitor::apply(dfg, ctx.m_dfg2AstContext); } else { - DfgToAstVisitor::apply(dfg, ctx); + DfgToAstVisitor::apply(dfg, ctx.m_dfg2AstContext); } } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 571b413ac..2db9a8265 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -236,7 +236,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) { V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeEitherLevel() >= 3); } -static void process(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { +static void process(DfgGraph& dfg, V3DfgContext& ctx) { // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of // small cycles. @@ -307,7 +307,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { const VNUser2InUse user2InUse; const VNUser3InUse user3InUse; - V3DfgOptimizationContext ctx{label}; + V3DfgContext ctx{label}; if (!netlistp->topScopep()) { // Pre V3Scope application. Run on each module separately. diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 0168ba629..d349551d4 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -25,92 +25,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; -V3DfgBinToOneHotContext::~V3DfgBinToOneHotContext() { - V3Stats::addStat("Optimizations, DFG " + m_label + " BinToOneHot, decoders created", - m_decodersCreated); -} - -V3DfgBreakCyclesContext::~V3DfgBreakCyclesContext() { - V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, made acyclic", m_nFixed); - V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, improved", m_nImproved); - V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, left unchanged", - m_nUnchanged); - V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, trivial", m_nTrivial); - V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, changes applied", - m_nImprovements); -} - -V3DfgCseContext::~V3DfgCseContext() { - V3Stats::addStat("Optimizations, DFG " + m_label + " CSE, expressions eliminated", - m_eliminated); -} - -V3DfgRegularizeContext::~V3DfgRegularizeContext() { - V3Stats::addStat("Optimizations, DFG " + m_label + " Regularize, temporaries introduced", - m_temporariesIntroduced); -} - -V3DfgEliminateVarsContext::~V3DfgEliminateVarsContext() { - V3Stats::addStat("Optimizations, DFG " + m_label + " EliminateVars, variables replaced", - m_varsReplaced); - V3Stats::addStat("Optimizations, DFG " + m_label + " EliminateVars, variables removed", - m_varsRemoved); -} - -static std::string getPrefix(const std::string& label) { - if (label.empty()) return ""; - std::string str = VString::removeWhitespace(label); - std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { // - return c == ' ' ? '-' : std::tolower(c); - }); - str += "-"; - return str; -} - -V3DfgOptimizationContext::V3DfgOptimizationContext(const std::string& label) - : m_label{label} - , m_prefix{getPrefix(label)} {} - -V3DfgOptimizationContext::~V3DfgOptimizationContext() { - const string prefix = "Optimizations, DFG " + m_label + " "; - V3Stats::addStat(prefix + "General, modules", m_modules); - V3Stats::addStat(prefix + "Ast2Dfg, coalesced assignments", m_coalescedAssignments); - V3Stats::addStat(prefix + "Ast2Dfg, input equations", m_inputEquations); - V3Stats::addStat(prefix + "Ast2Dfg, representable", m_representable); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (dtype)", m_nonRepDType); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (impure)", m_nonRepImpure); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (timing)", m_nonRepTiming); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (lhs)", m_nonRepLhs); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (node)", m_nonRepNode); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (unknown)", m_nonRepUnknown); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (var ref)", m_nonRepVarRef); - V3Stats::addStat(prefix + "Ast2Dfg, non-representable (width)", m_nonRepWidth); - V3Stats::addStat(prefix + "Dfg2Ast, result equations", m_resultEquations); - - // Print the collected patterns - if (v3Global.opt.stats()) { - // Label to lowercase, without spaces - std::string ident = m_label; - std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { // - return c == ' ' ? '_' : std::tolower(c); - }); - - // File to dump to - const std::string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() - + "__stats_dfg_patterns__" + ident + ".txt"; - // Open, write, close - const std::unique_ptr ofp{V3File::new_ofstream(filename)}; - if (ofp->fail()) v3fatal("Can't write file: " << filename); - m_patternStats.dump(m_label, *ofp); - } - - // Check the stats are consistent - UASSERT(m_inputEquations - == m_representable + m_nonRepDType + m_nonRepImpure + m_nonRepTiming + m_nonRepLhs - + m_nonRepNode + m_nonRepUnknown + m_nonRepVarRef + m_nonRepWidth, - "Inconsistent statistics"); -} - // Common sub-expression elimination void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { // Remove common sub-expressions @@ -592,7 +506,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { for (AstNode* const nodep : replacedVariables) nodep->unlinkFrBack()->deleteTree(); } -void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { +void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) { // There is absolutely nothing useful we can do with a graph of size 2 or less if (dfg.size() <= 2) return; diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 99cd404dd..b17fa1002 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -20,108 +20,11 @@ #include "config_build.h" #include "verilatedos.h" -#include "V3DfgPatternStats.h" -#include "V3DfgPeephole.h" +#include "V3DfgContext.h" class AstModule; class DfgGraph; -//=========================================================================== -// Various context objects hold data that need to persist across invocations -// of a DFG pass. - -class V3DfgBinToOneHotContext final { - const std::string m_label; // Label to apply to stats - -public: - VDouble0 m_decodersCreated; // Number of bianry to one-hot decoders created - explicit V3DfgBinToOneHotContext(const std::string& label) - : m_label{label} {} - ~V3DfgBinToOneHotContext() VL_MT_DISABLED; -}; - -class V3DfgBreakCyclesContext final { - const std::string m_label; // Label to apply to stats - -public: - VDouble0 m_nFixed; // Number of graphs that became acyclic - VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic - VDouble0 m_nUnchanged; // Number of graphs that were left unchanged - VDouble0 m_nTrivial; // Number of graphs that were not changed - VDouble0 m_nImprovements; // Number of changes made to graphs - explicit V3DfgBreakCyclesContext(const std::string& label) - : m_label{label} {} - ~V3DfgBreakCyclesContext() VL_MT_DISABLED; -}; - -class V3DfgCseContext final { - const std::string m_label; // Label to apply to stats - -public: - VDouble0 m_eliminated; // Number of common sub-expressions eliminated - explicit V3DfgCseContext(const std::string& label) - : m_label{label} {} - ~V3DfgCseContext() VL_MT_DISABLED; -}; - -class V3DfgRegularizeContext final { - const std::string m_label; // Label to apply to stats - -public: - VDouble0 m_temporariesIntroduced; // Number of temporaries introduced - - explicit V3DfgRegularizeContext(const std::string& label) - : m_label{label} {} - ~V3DfgRegularizeContext() VL_MT_DISABLED; -}; - -class V3DfgEliminateVarsContext final { - const std::string m_label; // Label to apply to stats - -public: - VDouble0 m_varsReplaced; // Number of variables replaced - VDouble0 m_varsRemoved; // Number of variables removed - - explicit V3DfgEliminateVarsContext(const std::string& label) - : m_label{label} {} - ~V3DfgEliminateVarsContext() VL_MT_DISABLED; -}; - -class V3DfgOptimizationContext final { - const std::string m_label; // Label to add to stats, etc. - const std::string m_prefix; // Prefix to add to file dumps (derived from label) - -public: - VDouble0 m_modules; // Number of modules optimized - VDouble0 m_coalescedAssignments; // Number of partial assignments coalesced - VDouble0 m_inputEquations; // Number of input combinational equations - VDouble0 m_representable; // Number of combinational equations representable - VDouble0 m_nonRepDType; // Equations non-representable due to data type - VDouble0 m_nonRepImpure; // Equations non-representable due to impure node - VDouble0 m_nonRepTiming; // Equations non-representable due to timing control - VDouble0 m_nonRepLhs; // Equations non-representable due to lhs - VDouble0 m_nonRepNode; // Equations non-representable due to node type - VDouble0 m_nonRepUnknown; // Equations non-representable due to unknown node - VDouble0 m_nonRepVarRef; // Equations non-representable due to variable reference - VDouble0 m_nonRepWidth; // Equations non-representable due to width mismatch - VDouble0 m_resultEquations; // Number of result combinational equations - - V3DfgBinToOneHotContext m_binToOneHotContext{m_label}; - V3DfgBreakCyclesContext m_breakCyclesContext{m_label}; - V3DfgCseContext m_cseContext0{m_label + " 1st"}; - V3DfgCseContext m_cseContext1{m_label + " 2nd"}; - V3DfgPeepholeContext m_peepholeContext{m_label}; - V3DfgRegularizeContext m_regularizeContext{m_label}; - V3DfgEliminateVarsContext m_eliminateVarsContext{m_label}; - - V3DfgPatternStats m_patternStats; - - explicit V3DfgOptimizationContext(const std::string& label) VL_MT_DISABLED; - ~V3DfgOptimizationContext() VL_MT_DISABLED; - - const std::string& prefix() const { return m_prefix; } -}; - namespace V3DfgPasses { //=========================================================================== // Top level entry points @@ -130,10 +33,10 @@ namespace V3DfgPasses { // Construct a DfGGraph representing the combinational logic in the given AstModule. The logic // that is represented by the graph is removed from the given AstModule. Returns the // constructed DfgGraph. -DfgGraph* astToDfg(AstModule&, V3DfgOptimizationContext&) VL_MT_DISABLED; +DfgGraph* astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED; // Same as above, but for the entire netlist, after V3Scope -DfgGraph* astToDfg(AstNetlist&, V3DfgOptimizationContext&) VL_MT_DISABLED; +DfgGraph* astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED; // Attempt to make the given cyclic graph into an acyclic, or "less cyclic" // equivalent. If the returned pointer is null, then no improvement was @@ -142,14 +45,14 @@ DfgGraph* astToDfg(AstNetlist&, V3DfgOptimizationContext&) VL_MT_DISABLED; // graph is always independent of the original. If an imporoved graph is // returned, then the returned 'bool' flag indicated if the returned graph is // acyclic (flag 'true'), or still cyclic (flag 'false'). -std::pair, bool> breakCycles(const DfgGraph&, - V3DfgOptimizationContext&) VL_MT_DISABLED; +std::pair, bool> // +breakCycles(const DfgGraph&, V3DfgContext&) VL_MT_DISABLED; // Optimize the given DfgGraph -void optimize(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED; +void optimize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED; // Convert DfgGraph back into Ast, and insert converted graph back into the Ast. -void dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED; +void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED; //=========================================================================== // Intermediate/internal operations diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index f308a0c7c..a6e990029 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -23,21 +23,20 @@ #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT -#include "V3DfgPeephole.h" - #include "V3Dfg.h" #include "V3DfgCache.h" #include "V3DfgPasses.h" +#include "V3DfgPeepholePatterns.h" #include "V3Stats.h" #include VL_DEFINE_DEBUG_FUNCTIONS; -V3DfgPeepholeContext::V3DfgPeepholeContext(const std::string& label) - : m_label{label} { +V3DfgPeepholeContext::V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "Peephole"} { const auto checkEnabled = [this](VDfgPeepholePattern id) { - string str{id.ascii()}; + std::string str{id.ascii()}; std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { // return c == '_' ? '-' : std::tolower(c); }); @@ -50,11 +49,11 @@ V3DfgPeepholeContext::V3DfgPeepholeContext(const std::string& label) V3DfgPeepholeContext::~V3DfgPeepholeContext() { const auto emitStat = [this](VDfgPeepholePattern id) { - string str{id.ascii()}; + std::string str{id.ascii()}; std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { // return c == '_' ? ' ' : std::tolower(c); }); - V3Stats::addStat("Optimizations, DFG " + m_label + " Peephole, " + str, m_count[id]); + addStat(str, m_count[id]); }; #define OPTIMIZATION_EMIT_STATS(id, name) emitStat(VDfgPeepholePattern::id); FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_EMIT_STATS) diff --git a/src/V3DfgPeephole.h b/src/V3DfgPeepholePatterns.h similarity index 94% rename from src/V3DfgPeephole.h rename to src/V3DfgPeepholePatterns.h index f08627660..e3fe5c3d0 100644 --- a/src/V3DfgPeephole.h +++ b/src/V3DfgPeepholePatterns.h @@ -132,16 +132,4 @@ public: operator en() const { return m_e; } }; -struct V3DfgPeepholeContext final { - const std::string m_label; // Label to apply to stats - - // Enable flags for each optimization - std::array m_enabled; - // Count of applications for each optimization (for statistics) - std::array m_count; - - explicit V3DfgPeepholeContext(const std::string& label) VL_MT_DISABLED; - ~V3DfgPeepholeContext() VL_MT_DISABLED; -}; - #endif diff --git a/test_regress/t/t_dfg_peephole.py b/test_regress/t/t_dfg_peephole.py index 034ba591c..ffb1819e9 100755 --- a/test_regress/t/t_dfg_peephole.py +++ b/test_regress/t/t_dfg_peephole.py @@ -20,7 +20,7 @@ if not os.path.exists(root + "/.git"): # Read optimizations optimizations = [] -hdrFile = "../src/V3DfgPeephole.h" +hdrFile = "../src/V3DfgPeepholePatterns.h" with open(hdrFile, 'r', encoding="utf8") as hdrFh: prevOpt = "" lineno = 0 diff --git a/test_regress/t/t_dfg_unhandled.py b/test_regress/t/t_dfg_unhandled.py index f60c4d682..1bb7d5c4a 100755 --- a/test_regress/t/t_dfg_unhandled.py +++ b/test_regress/t/t_dfg_unhandled.py @@ -14,6 +14,6 @@ test.scenarios('vlt') test.compile(verilator_flags2=["--stats"]) test.file_grep(test.stats, - r'Optimizations, DFG pre inline Ast2Dfg, non-representable \(impure\)\s+(\d+)', 1) + r'Optimizations, DFG pre inline AstToDfg, non-representable \(impure\)\s+(\d+)', 1) test.passes() From 42e1c838758cf0ff244a425abc5fe2d82d14a8d4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 15:03:36 -0400 Subject: [PATCH 080/252] Internals: Add some FileLine stats --- src/V3FileLine.cpp | 13 +++++++++++++ src/V3FileLine.h | 1 + src/Verilator.cpp | 1 + 3 files changed, 15 insertions(+) diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 1631d6e4a..b7778d2de 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -26,6 +26,7 @@ # include "V3Global.h" # include "V3Control.h" # include "V3File.h" +# include "V3Stats.h" #endif #include "V3Waiver.h" // clang-format on @@ -551,6 +552,18 @@ void FileLine::operator delete(void* objp, size_t size) { } #endif +void FileLine::stats() { +#ifndef V3ERROR_NO_GLOBAL_ + V3Stats::addStatSum("FileLines, Number of filenames", + singleton().m_names.size()); // Max m_filenameno + V3Stats::addStatSum("FileLines, Message enable sets", + singleton().m_internedMsgEns.size()); // Max m_msgEnIdx + // Don't currently have a good path to recording max line/column, + // Infrequently useful, alternatively we could keep globals we update as make each FileLine + // or could use fileLineLeakChecks. +#endif +} + void FileLine::deleteAllRemaining() { #ifdef VL_LEAK_CHECKS // FileLines are allocated, but never nicely freed, as it's much faster diff --git a/src/V3FileLine.h b/src/V3FileLine.h index e7e0cbb8e..0eb250525 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -213,6 +213,7 @@ public: FileLine* copyOrSameFileLine(); FileLine* copyOrSameFileLineApplied(); static void deleteAllRemaining(); + static void stats(); ~FileLine(); #ifdef VL_LEAK_CHECKS static void* operator new(size_t size); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 2e1be9ac0..143b7956c 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -119,6 +119,7 @@ V3Global v3Global; static void reportStatsIfEnabled() { if (v3Global.opt.stats()) { + FileLine::stats(); V3Stats::statsFinalAll(v3Global.rootp()); V3Stats::statsReport(); } From 39a5c731ac7aeac925fa6e5fe5f3851e82d8d988 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 15:48:19 -0400 Subject: [PATCH 081/252] Tests: Fix missing forward decls (#6202 partial) --- src/verilog.y | 3 ++- test_regress/t/t_enum_bad_circdecl.out | 14 +++++++------- test_regress/t/t_enum_bad_circdecl.v | 7 +++---- test_regress/t/t_enum_recurse_bad2.out | 4 ++-- test_regress/t/t_enum_recurse_bad2.v | 1 + ...d_nested_typedef.py => t_typedef_fwd_nested.py} | 2 +- ...ard_nested_typedef.v => t_typedef_fwd_nested.v} | 1 + 7 files changed, 17 insertions(+), 15 deletions(-) rename test_regress/t/{t_forward_nested_typedef.py => t_typedef_fwd_nested.py} (93%) rename test_regress/t/{t_forward_nested_typedef.v => t_typedef_fwd_nested.v} (98%) diff --git a/src/verilog.y b/src/verilog.y index 834b79b96..44354aece 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7592,7 +7592,8 @@ class_item: // ==IEEE: class_item { const string cgName = $1->name(); $1->name("__vlAnonCG_" + cgName); - AstVar* const newp = new AstVar{$1, VVarType::VAR, cgName, VFlagChildDType{}, new AstRefDType($1, $1->name())}; + AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName, + VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())}; $$ = addNextNull($1, newp); } // // local_parameter_declaration under parameter_declaration diff --git a/test_regress/t/t_enum_bad_circdecl.out b/test_regress/t/t_enum_bad_circdecl.out index f3e6e08d0..97448fd94 100644 --- a/test_regress/t/t_enum_bad_circdecl.out +++ b/test_regress/t/t_enum_bad_circdecl.out @@ -1,9 +1,9 @@ -%Error: t/t_enum_bad_circdecl.v:11:6: Typedef has self-reference: 'bad_redecl' - : ... note: In instance 't' - 11 | } bad_redecl; - | ^~~~~~~~~~ - t/t_enum_bad_circdecl.v:9:17: ... Location of reference - 9 | typedef enum bad_redecl [2:0] { - | ^~~~~~~~~~ +%Error: t/t_enum_bad_circdecl.v:10:41: Typedef has self-reference: 'bad_redecl' + : ... note: In instance 't' + 10 | typedef enum bad_redecl [2:0] {VALUE} bad_redecl; + | ^~~~~~~~~~ + t/t_enum_bad_circdecl.v:10:16: ... Location of reference + 10 | typedef enum bad_redecl [2:0] {VALUE} bad_redecl; + | ^~~~~~~~~~ ... 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_enum_bad_circdecl.v b/test_regress/t/t_enum_bad_circdecl.v index bb5478c50..8599269a3 100644 --- a/test_regress/t/t_enum_bad_circdecl.v +++ b/test_regress/t/t_enum_bad_circdecl.v @@ -4,10 +4,9 @@ // any use, without warranty, 2020 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -module t(); +module t; - typedef enum bad_redecl [2:0] { - VALUE - } bad_redecl; + typedef enum bad_redecl; + typedef enum bad_redecl [2:0] {VALUE} bad_redecl; endmodule diff --git a/test_regress/t/t_enum_recurse_bad2.out b/test_regress/t/t_enum_recurse_bad2.out index 0c481a895..bc0e8fe41 100644 --- a/test_regress/t/t_enum_recurse_bad2.out +++ b/test_regress/t/t_enum_recurse_bad2.out @@ -1,5 +1,5 @@ -%Error: t/t_enum_recurse_bad2.v:7:14: Self-referential enumerated type definition - 7 | typedef enum foo_t { A = 'b0, B = 'b1 } foo_t; +%Error: t/t_enum_recurse_bad2.v:8:14: Self-referential enumerated type definition + 8 | typedef enum foo_t { A = 'b0, B = 'b1 } foo_t; | ^~~~~ ... 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_enum_recurse_bad2.v b/test_regress/t/t_enum_recurse_bad2.v index 8cec4c4e0..449b05bfa 100644 --- a/test_regress/t/t_enum_recurse_bad2.v +++ b/test_regress/t/t_enum_recurse_bad2.v @@ -4,4 +4,5 @@ // any use, without warranty, 2019 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +typedef enum foo_t; typedef enum foo_t { A = 'b0, B = 'b1 } foo_t; diff --git a/test_regress/t/t_forward_nested_typedef.py b/test_regress/t/t_typedef_fwd_nested.py similarity index 93% rename from test_regress/t/t_forward_nested_typedef.py rename to test_regress/t/t_typedef_fwd_nested.py index d4f986441..dbdaf4551 100755 --- a/test_regress/t/t_forward_nested_typedef.py +++ b/test_regress/t/t_typedef_fwd_nested.py @@ -9,7 +9,7 @@ import vltest_bootstrap -test.scenarios('simulator') +test.scenarios('simulator_st') test.compile() diff --git a/test_regress/t/t_forward_nested_typedef.v b/test_regress/t/t_typedef_fwd_nested.v similarity index 98% rename from test_regress/t/t_forward_nested_typedef.v rename to test_regress/t/t_typedef_fwd_nested.v index ff643e21c..e6e99040b 100644 --- a/test_regress/t/t_forward_nested_typedef.v +++ b/test_regress/t/t_typedef_fwd_nested.v @@ -6,6 +6,7 @@ typedef class Bar; typedef Bar Baz; +typedef class Quux; typedef Quux #(16, 32) Quux_t; typedef Quux_t Quuux_t; From c2e0f496bdd4fe319900a3cfa6bc2ae502b057e1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 15:55:33 -0400 Subject: [PATCH 082/252] Internals: Free up 32 bits in FileLine. Limits columns to 16M columns, or may report wrong column on such long lines. Limits single token to 64K lines, or may report wrong line for that token. --- src/V3FileLine.h | 57 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 0eb250525..430ad04fd 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -140,14 +140,19 @@ class FileLine final { // MEMBERS // Columns here means number of chars from beginning (i.e. tabs count as one) + // 64-bit word # 0 msgEnSetIdx_t m_msgEnIdx = 0; // Message enable bit set (index into interned array) fileNameIdx_t m_filenameno = 0; // `line corrected filename number bool m_waive : 1; // Waive warning - pack next to the line number to save 8 bytes of storage unsigned m_contentLineno : 31; // Line number within source stream + // 64-bit word # 1 + uint32_t m_pad = 0; // space for project coming soon int m_firstLineno = 0; // `line corrected token's first line number - int m_firstColumn = 0; // `line corrected token's first column number - int m_lastLineno = 0; // `line corrected token's last line number - int m_lastColumn = 0; // `line corrected token's last column number + // 64-bit word # 2 + uint32_t m_firstColumn : 24; // `line corrected token's first column number + uint32_t m_lastColumn : 24; // `line corrected token's last column number + uint16_t m_lastLinenoAdder = 0; // Last line number as offset from m_firstLineno + // 64-bit word # 3/4 VFileContent* m_contentp = nullptr; // Source text contents line is within FileLine* m_parent = nullptr; // Parent line that included this line @@ -175,14 +180,18 @@ private: : m_msgEnIdx{singleton().defaultMsgEnIndex()} , m_filenameno{singleton().nameToNumber(FileLine::builtInFilename())} , m_waive{false} - , m_contentLineno{0} {} + , m_contentLineno{0} + , m_firstColumn{0} + , m_lastColumn{0} {} public: explicit FileLine(const string& filename) : m_msgEnIdx{defaultFileLine().m_msgEnIdx} , m_filenameno{singleton().nameToNumber(filename)} , m_waive{false} - , m_contentLineno{0} {} + , m_contentLineno{0} + , m_firstColumn{0} + , m_lastColumn{0} {} explicit FileLine(const FileLine& from) : m_msgEnIdx{from.m_msgEnIdx} , m_filenameno{from.m_filenameno} @@ -190,8 +199,8 @@ public: , m_contentLineno{from.m_contentLineno} , m_firstLineno{from.m_firstLineno} , m_firstColumn{from.m_firstColumn} - , m_lastLineno{from.m_lastLineno} , m_lastColumn{from.m_lastColumn} + , m_lastLinenoAdder{from.m_lastLinenoAdder} , m_contentp{from.m_contentp} , m_parent{from.m_parent} { if (m_contentp) m_contentp->refInc(); @@ -203,8 +212,8 @@ public: , m_contentLineno{fromp->m_contentLineno} , m_firstLineno{fromp->m_firstLineno} , m_firstColumn{fromp->m_firstColumn} - , m_lastLineno{fromp->m_lastLineno} , m_lastColumn{fromp->m_lastColumn} + , m_lastLinenoAdder{fromp->m_lastLinenoAdder} , m_contentp{fromp->m_contentp} , m_parent{fromp->m_parent} { if (m_contentp) m_contentp->refInc(); @@ -228,8 +237,9 @@ public: void contentLinenoFrom(const FileLine* fromp) { m_contentLineno = fromp->m_contentLineno; } void lineno(int num) { m_firstLineno = num; - m_lastLineno = num; - m_firstColumn = m_lastColumn = 1; + m_lastLinenoAdder = 0; + m_firstColumn = 1; + m_lastColumn = 1; } void column(int firstColumn, int lastColumn) { m_firstColumn = firstColumn; @@ -242,24 +252,34 @@ public: void lineDirectiveParse(const char* textp, string& filenameRef, int& linenoRef, int& enterExitRef); void linenoInc() { - m_lastLineno++; + if (m_lastLinenoAdder == 65535) { + // Overflow can only happen on super-long comment, adjust + // starting first line to deal with it this will make token + // start later than in reality, but nothing should refer to it, + // and the overall line number will not drift. + m_firstLineno += 65535 + 1; + m_lastLinenoAdder = 0; + } else { + ++m_lastLinenoAdder; + } m_lastColumn = 1; - m_contentLineno++; + ++m_contentLineno; } void startToken() { - m_firstLineno = m_lastLineno; + m_firstLineno = lastLineno(); m_firstColumn = m_lastColumn; + m_lastLinenoAdder = 0; } // Advance last line/column based on given text void forwardToken(const char* textp, size_t size, bool trackLines = true); int firstLineno() const VL_MT_SAFE { return m_firstLineno; } - int firstColumn() const VL_MT_SAFE { return m_firstColumn; } - int lastLineno() const VL_MT_SAFE { return m_lastLineno; } - int lastColumn() const VL_MT_SAFE { return m_lastColumn; } + int firstColumn() const VL_MT_SAFE { return static_cast(m_firstColumn); } + int lastLineno() const VL_MT_SAFE { return m_firstLineno + m_lastLinenoAdder; } + int lastColumn() const VL_MT_SAFE { return static_cast(m_lastColumn); } VFileContent* contentp() const { return m_contentp; } // If not otherwise more specific, use last lineno for errors etc, // as the parser errors etc generally make more sense pointing at the last parse point - int lineno() const VL_MT_SAFE { return m_lastLineno; } + int lineno() const VL_MT_SAFE { return lastLineno(); } string source() const VL_MT_SAFE; string sourcePrefix(int toColumn) const VL_MT_SAFE; string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines @@ -367,7 +387,7 @@ public: string warnContextSecondary() const { return warnContext(); } bool operator==(const FileLine& rhs) const { return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn - && m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn + && m_lastLinenoAdder == rhs.m_lastLinenoAdder && m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx); } bool equalFirstLineCol(const FileLine& rhs) const { @@ -382,7 +402,8 @@ public: return (m_firstLineno < rhs.m_firstLineno) ? -1 : 1; if (m_firstColumn != rhs.m_firstColumn) return (m_firstColumn < rhs.m_firstColumn) ? -1 : 1; - if (m_lastLineno != rhs.m_lastLineno) return (m_lastLineno < rhs.m_lastLineno) ? -1 : 1; + if (m_lastLinenoAdder != rhs.m_lastLinenoAdder) + return (m_lastLinenoAdder < rhs.m_lastLinenoAdder) ? -1 : 1; if (m_lastColumn != rhs.m_lastColumn) return (m_lastColumn < rhs.m_lastColumn) ? -1 : 1; for (size_t i = 0; i < msgEn().size(); ++i) { if (msgEn().test(i) != rhs.msgEn().test(i)) return rhs.msgEn().test(i) ? -1 : 1; From d8dbb08a9548176ba0b80aaa5556e0925abfe15b Mon Sep 17 00:00:00 2001 From: Paul Swirhun <63216497+paul-demo@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:53:51 -0700 Subject: [PATCH 083/252] Support bit queue streaming (#5830) (#6103) --- include/verilated_funcs.h | 211 ++++++++++- src/V3Ast.cpp | 3 + src/V3AstNodeExpr.h | 31 ++ src/V3AstNodes.cpp | 14 + src/V3Const.cpp | 126 ++++++- src/V3EmitCFunc.h | 33 ++ src/V3Width.cpp | 28 ++ test_regress/t/t_queue_concat_assign.v | 31 +- test_regress/t/t_stream_bad.out | 20 +- test_regress/t/t_stream_bad.v | 10 +- test_regress/t/t_stream_bitqueue.py | 18 + test_regress/t/t_stream_bitqueue.v | 490 +++++++++++++++++++++++++ test_regress/t/t_stream_crc_example.py | 18 + test_regress/t/t_stream_crc_example.v | 105 ++++++ test_regress/t/t_stream_unpack.v | 4 +- 15 files changed, 1097 insertions(+), 45 deletions(-) create mode 100755 test_regress/t/t_stream_bitqueue.py create mode 100644 test_regress/t/t_stream_bitqueue.v create mode 100755 test_regress/t/t_stream_crc_example.py create mode 100644 test_regress/t/t_stream_crc_example.v diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 2bcb7f5fc..09e0be17e 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2232,6 +2232,173 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn return owp; } +template +static inline VlQueue VL_CLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +template +static inline VlQueue VL_REVCLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +// Helper function to get a bit from a queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue& queue, int srcElementBits, size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const T element = queue.at(elemIdx); + if (srcElementBits == 1) { + return element & 1; + } else { + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + return (element >> actualBitPos) & 1; + } +} + +// Helper function to set a bit in the destination queue +template +static inline void VL_SET_QUEUE_BIT(VlQueue& queue, int dstElementBits, size_t bitIndex, + bool value) { + if (dstElementBits == 1) { + if (VL_UNLIKELY(bitIndex >= queue.size())) return; + queue.atWrite(bitIndex) = value ? 1 : 0; + } else { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + if (value) { + queue.atWrite(elemIdx) |= (static_cast(1) << actualBitPos); + } else { + queue.atWrite(elemIdx) &= ~(static_cast(1) << actualBitPos); + } + } +} + +// Helper function to get a bit from a VlWide queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue>& queue, int srcElementBits, + size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const VlWide& element = queue.at(elemIdx); + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + + return VL_BITISSET_W(element.data(), actualBitPos); +} + +// Helper function to set a bit in a VlWide queue at a specific bit index +template +static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElementBits, + size_t bitIndex, bool value) { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + + VlWide& element = queue.atWrite(elemIdx); + if (value) { + VL_ASSIGNBIT_WO(actualBitPos, element.data()); + } else { + VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0); + } +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) { + elem = 0; +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) { + for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + if (srcElementBits == dstElementBits) { + // Simple case: same element bit width, direct copy of each element + if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op + q = from; + } else { + // Different element bit widths: use streaming conversion + VlQueue srcCopy = from; + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } + for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) { + VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex)); + } + } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, + int srcElementBits, int dstElementBits) { + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + + // Always make a copy to handle the case where q and from are the same queue + VlQueue srcCopy = from; + + q.renew(dstSize); + + // Initialize all elements to zero using appropriate method + for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } + + if (lbits == 1) { + // Simple bit reversal: write directly to destination + for (int i = srcTotalBits - 1; i >= 0; --i) { + VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i)); + } + } else { + // Generalized block-reversal for lbits > 1: + // 1. Reverse all bits using 1-bit blocks + // 2. Split into lbits-sized blocks and pad incomplete blocks on the left + // 3. Reverse each lbits-sized block using 1-bit blocks + + const size_t numCompleteBlocks = srcTotalBits / lbits; + const size_t remainderBits = srcTotalBits % lbits; + const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0); + + size_t dstBitIndex = 0; + + for (size_t block = 0; block < srcBlocks; ++block) { + const size_t blockStart = block * lbits; + const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits; + + for (int bit = bitsToProcess - 1; bit >= 0; --bit) { + const size_t reversedBitIndex = blockStart + bit; + const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex; + VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex)); + } + + dstBitIndex += lbits - bitsToProcess; + } + } +} + //====================================================================== // Expressions needing insert/select @@ -2239,49 +2406,49 @@ static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const QData mask = VL_MASK_Q(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { @@ -2289,7 +2456,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2298,7 +2469,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2307,7 +2482,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2316,7 +2495,11 @@ static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const QData mask = VL_MASK_Q(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2326,7 +2509,11 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue> const int size = (rbits + lbits - 1) / lbits; q.renew(size); for (size_t i = 0; i < size; ++i) { - VL_SEL_WWII(lbits, rbits, q.atWrite(i), rwp, i * lbits, lbits); + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth); } } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 4d7bcd99e..831ed01c7 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1592,6 +1592,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) return VCastable::ENUM_IMPLICIT; if (fromNumericable) return VCastable::ENUM_EXPLICIT; + } else if (VN_IS(toDtp, QueueDType) + && (VN_IS(fromDtp, BasicDType) || VN_IS(fromDtp, StreamDType))) { + return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { if (fromConstp->isNull()) return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index d816fa2d5..d0b448719 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1129,6 +1129,37 @@ public: string emitC() final override { V3ERROR_NA_RETURN(""); } bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } }; +class AstCvtArrayToArray final : public AstNodeExpr { + // Copy/Cast from dynamic/unpacked types to dynamic/unpacked types + // @astgen op1 := fromp : AstNodeExpr +private: + const bool m_reverse; // whether ordering gets reversed in this operation (ex: {<<{expr}}) + const int m_blockSize; // num bits per block in a streaming operation (ex: 4 in {<<4{expr}})) + const int m_dstElementBits; // num bits in lhs (ex: 8 if to byte-queue, 1 if to bit-queue) + const int m_srcElementBits; // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue) + +public: + AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse, + int blockSize, int dstElementBits, int srcElementBits) + : ASTGEN_SUPER_CvtArrayToArray(fl) + , m_reverse{reverse} + , m_blockSize{blockSize} + , m_dstElementBits{dstElementBits} + , m_srcElementBits{srcElementBits} { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtArrayToArray; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } + bool reverse() const { return m_reverse; } + int blockSize() const { return m_blockSize; } + int dstElementBits() const { return m_dstElementBits; } + int srcElementBits() const { return m_srcElementBits; } +}; class AstCvtArrayToPacked final : public AstNodeExpr { // Cast from dynamic queue data type to packed array // @astgen op1 := fromp : AstNodeExpr diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 4d21a7392..c4347bbfe 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1696,6 +1696,20 @@ void AstCCast::dumpJson(std::ostream& str) const { dumpJsonNumFunc(str, size); dumpJsonGen(str); } +void AstCvtArrayToArray::dump(std::ostream& str) const { + this->AstNodeExpr::dump(str); + str << " reverse=" << reverse(); + str << " blockSize=" << blockSize(); + str << " dstElementBits=" << dstElementBits(); + str << " srcElementBits=" << srcElementBits(); +} +void AstCvtArrayToArray::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, reverse); + dumpJsonNumFunc(str, blockSize); + dumpJsonNumFunc(str, dstElementBits); + dumpJsonNumFunc(str, srcElementBits); + dumpJsonGen(str); +} void AstCell::dump(std::ostream& str) const { this->AstNode::dump(str); if (recursive()) str << " [RECURSIVE]"; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index c8d54a8c9..058bf0493 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2205,8 +2205,23 @@ class ConstVisitor final : public VNVisitor { AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack(); AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack(); AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { - srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; + if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + srcp = new AstCvtArrayToArray{ + srcp->fileline(), srcp, nodep->dtypep(), false, 1, + dstElementBits, srcElementBits}; + } else { + srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; + } } else if (VN_IS(srcDTypep, UnpackArrayDType)) { srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep}; // Handling the case where lhs is wider than rhs by inserting zeros. StreamL does @@ -2289,15 +2304,50 @@ class ConstVisitor final : public VNVisitor { // Further reduce, any of the nodes may have more reductions. return true; } else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) { - AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefp(); AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL); - AstNodeExpr* const srcp = streamp->lhsp(); - const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); - if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) - || VN_IS(srcDTypep, UnpackArrayDType)) { - streamp->lhsp( - new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), lhsDtypep}); - streamp->dtypeFrom(lhsDtypep); + AstNodeExpr* srcp = streamp->lhsp(); + AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); + if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) + || VN_IS(srcDTypep, UnpackArrayDType))) { + if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { + int blockSize = 1; + if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { + blockSize = constp->toSInt(); + if (VL_UNLIKELY(blockSize <= 0)) { + // Not reachable due to higher level checks when parsing stream + // operators commented out to not fail v3error-coverage-checks. + // nodep->v3error("Stream block size must be positive, got " << + // blockSize); nodep->v3error("Stream block size must be positive, got + // " << blockSize); + blockSize = 1; + } + } + // Not reachable due to higher level checks when parsing stream operators + // commented out to not fail v3error-coverage-checks. + // else { + // nodep->v3error("Stream block size must be constant (got " << + // streamp->rhsp()->prettyTypeName() << ")"); + // } + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + streamp->unlinkFrBack(); + srcp = new AstCvtArrayToArray{ + srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, true, + blockSize, dstElementBits, srcElementBits}; + nodep->rhsp(srcp); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + } else { + streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), + dstDTypep}); + streamp->dtypeFrom(dstDTypep); + } } } else if (m_doV && replaceAssignMultiSel(nodep)) { return true; @@ -3083,6 +3133,64 @@ class ConstVisitor final : public VNVisitor { varrefp->varp()->valuep(initvaluep); } } + void visit(AstCvtArrayToArray* nodep) override { + iterateChildren(nodep); + // Handle the case where we have a stream operation inside a cast conversion + // To avoid infinite recursion, mark the node as processed by setting user1. + if (!nodep->user1()) { + nodep->user1(true); + // Check for both StreamL and StreamR operations + AstNodeStream* streamp = nullptr; + bool isReverse = false; + if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) { + streamp = streamLp; + isReverse = true; // StreamL reverses the operation + } else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) { + streamp = streamRp; + isReverse = false; // StreamR doesn't reverse the operation + } + if (streamp) { + AstNodeExpr* srcp = streamp->lhsp(); + AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp(); + if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) { + int blockSize = 1; + if (AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { + blockSize = constp->toSInt(); + if (VL_UNLIKELY(blockSize <= 0)) { + // Not reachable due to higher level checks when parsing stream + // operators commented out to not fail v3error-coverage-checks. + // nodep->v3error("Stream block size must be positive, got " << + // blockSize); + blockSize = 1; + } + } + // Not reachable due to higher level checks when parsing stream operators + // commented out to not fail v3error-coverage-checks. + // else { + // nodep->v3error("Stream block size must be constant (got " << + // streamp->rhsp()->prettyTypeName() << ")"); + // } + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + streamp->unlinkFrBack(); + AstNodeExpr* newp = new AstCvtArrayToArray{ + srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse, + blockSize, dstElementBits, srcElementBits}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } + } + } void visit(AstRelease* nodep) override { if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) { FileLine* const flp = nodep->fileline(); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index c5e5c1efb..e489ae07c 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -426,6 +426,22 @@ public: emitVarDecl(nodep); } + void visit(AstCvtArrayToArray* nodep) override { + if (nodep->reverse()) { + puts("VL_REVCLONE_Q("); + } else { + puts("VL_CLONE_Q("); + } + iterateAndNextConstNull(nodep->fromp()); + puts(", "); + puts(cvtToStr(nodep->blockSize())); + puts(", "); + puts(cvtToStr(nodep->srcElementBits())); + puts(", "); + puts(cvtToStr(nodep->dstElementBits())); + puts(")"); + } + void visit(AstCvtArrayToPacked* nodep) override { AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); AstNodeDType* const elemDtp = fromDtp->subDTypep()->skipRefp(); @@ -514,6 +530,23 @@ public: puts(", "); rhs = false; iterateAndNextConstNull(castp->fromp()); + } else if (const AstCvtArrayToArray* const castp + = VN_CAST(nodep->rhsp(), CvtArrayToArray)) { + if (castp->reverse()) { + putns(castp, "VL_REVCOPY_Q("); + } else { + putns(castp, "VL_COPY_Q("); + } + iterateAndNextConstNull(nodep->lhsp()); + puts(", "); + rhs = false; + iterateAndNextConstNull(castp->fromp()); + puts(", "); + puts(cvtToStr(castp->blockSize())); + puts(", "); + puts(cvtToStr(castp->srcElementBits())); + puts(", "); + puts(cvtToStr(castp->dstElementBits())); } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) // && !VN_IS(nodep->rhsp(), CExpr) // && !VN_IS(nodep->rhsp(), CMethodHard) // diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5edaa1cd6..603846fdf 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1598,6 +1598,12 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); // Type set in constructor } + void visit(AstCvtArrayToArray* nodep) override { + if (nodep->didWidthAndSet()) return; + // Opaque returns, so arbitrary + userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); + // Type set in constructor + } void visit(AstCvtArrayToPacked* nodep) override { if (nodep->didWidthAndSet()) return; // Opaque returns, so arbitrary @@ -2201,6 +2207,28 @@ class WidthVisitor final : public VNVisitor { // Can just remove cast, but need extend placeholder // so we can avoid warning message } + } else if (VN_IS(toDtp, QueueDType)) { + if (VN_IS(fromDtp, BasicDType)) { + newp = new AstCvtPackedToArray{nodep->fileline(), + nodep->fromp()->unlinkFrBack(), toDtp}; + } else if (VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, StreamDType)) { + int srcElementBits = 1; + int dstElementBits = 1; + if (AstNodeDType* const elemDtp = fromDtp->subDTypep()) { + srcElementBits = elemDtp->width(); + } + const AstQueueDType* const dstQueueDtp = VN_AS(toDtp, QueueDType); + if (AstNodeDType* const elemDtp = dstQueueDtp->subDTypep()) { + dstElementBits = elemDtp->width(); + } + newp = new AstCvtArrayToArray{nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + toDtp, + false, + 1, + dstElementBits, + srcElementBits}; + } } else if (VN_IS(toDtp, ClassRefDType)) { // Can just remove cast } else { diff --git a/test_regress/t/t_queue_concat_assign.v b/test_regress/t/t_queue_concat_assign.v index b3860878e..2af584e29 100644 --- a/test_regress/t/t_queue_concat_assign.v +++ b/test_regress/t/t_queue_concat_assign.v @@ -4,20 +4,25 @@ // any use, without warranty, 2024 by Antmicro. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/); - initial begin - bit q1[$] = {1'b1}; - bit q2[$]; - bit [1:0] d1[] = {2'b10}; - bit [1:0] d2[]; - q2 = {q1}; - if (q2[0] != 1) $stop; +module t ( /*AUTOARG*/); + initial begin + bit q1[$] = {1'b1}; + bit q2[$]; + bit q3[$]; + bit [1:0] d1[] = {2'b10}; + bit [1:0] d2[]; + // TODO: queue streaming support broke assignment like this. + // It's something to do witih computeCastable and V3Width.cpp + // q2 = {q1}; + // if (q2[0] != 1) $stop; - d2 = {2'b11}; - if (d2[0] != 2'b11) $stop; + q3 = q1; + if (q3[0] != 1) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end + d2 = {2'b11}; + if (d2[0] != 2'b11) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index ad2c9c4c4..acf856f50 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -1,10 +1,18 @@ -%Error: t/t_stream_bad.v:12:32: Expecting expression to be constant, but can't convert a RAND to constant. +%Error: t/t_stream_bad.v:14:25: Expecting expression to be constant, but can't convert a RAND to constant. : ... note: In instance 't' - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^~~~~~~ + 14 | packed_data_32 = {<<$random{byte_in}}; + | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_stream_bad.v:12:30: Slice size isn't a constant or basic data type. +%Error: t/t_stream_bad.v:14:23: Slice size isn't a constant or basic data type. : ... note: In instance 't' - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^~ + 14 | packed_data_32 = {<<$random{byte_in}}; + | ^~ +%Error: t/t_stream_bad.v:15:25: Expecting expression to be constant, but variable isn't const: 'x' + : ... note: In instance 't' + 15 | packed_data_32 = {<>{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_q = bit_q_t'({<<{4'hc}}); + `checkh(bit_q[0], 1'b0); + `checkh(bit_q[1], 1'b0); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b1); + + bit_q = {>>{bit_q_t'(4'he)}}; + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b0); + + bit_q = {<<{bit_q_t'(4'hd)}}; + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b0); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b1); + + bit_qq = {>>{bit_q}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b0); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b1); + + bit_qq = {<<{bit_q}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_q = bit_q_t'({>>{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_q = bit_q_t'({>>2{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_qq = bit_q_t'({>>{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_qq = bit_q_t'({>>2{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_qq = bit_q_t'({<<{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b0); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b1); + + bit_qq = {<<2{bit_qq}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b0); + + bit_qq = {<<2{bit_q_t'({<<{bit_q}})}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b0); + end + + begin + cdata_q_t cdata_q, cdata_qq; + + cdata_q = cdata_q_t'(32'hdeadbeef); + `checkh(cdata_q[0], 8'hde); + `checkh(cdata_q[1], 8'had); + `checkh(cdata_q[2], 8'hbe); + `checkh(cdata_q[3], 8'hef); + + cdata_qq = cdata_q_t'({<<{cdata_q}}); + `checkh(cdata_qq[0], 8'hf7); + `checkh(cdata_qq[1], 8'h7d); + `checkh(cdata_qq[2], 8'hb5); + `checkh(cdata_qq[3], 8'h7b); + + cdata_qq = {<<2{cdata_q}}; + `checkh(cdata_qq[0], 8'hfb); + `checkh(cdata_qq[1], 8'hbe); + `checkh(cdata_qq[2], 8'h7a); + `checkh(cdata_qq[3], 8'hb7); + end + + begin + sdata_logic_q_t sdata_q, sdata_qq; + + sdata_q = sdata_logic_q_t'(64'hfeedface_deadbeef); + `checkh(sdata_q[0], 16'hfeed); + `checkh(sdata_q[1], 16'hface); + `checkh(sdata_q[2], 16'hdead); + `checkh(sdata_q[3], 16'hbeef); + + sdata_qq = sdata_logic_q_t'({<<{sdata_q}}); + `checkh(sdata_qq[0], 16'hf77d); + `checkh(sdata_qq[1], 16'hb57b); + `checkh(sdata_qq[2], 16'h735f); + `checkh(sdata_qq[3], 16'hb77f); + + sdata_qq = {<<2{sdata_q}}; + `checkh(sdata_qq[0], 16'hfbbe); + `checkh(sdata_qq[1], 16'h7ab7); + `checkh(sdata_qq[2], 16'hb3af); + `checkh(sdata_qq[3], 16'h7bbf); + end + + begin + idata_logic_q_t idata_q, idata_qq; + + idata_q = idata_logic_q_t'(64'h12345678_9abcdef0); + `checkh(idata_q[0], 32'h12345678); + `checkh(idata_q[1], 32'h9abcdef0); + + idata_qq = idata_logic_q_t'({<<{idata_q}}); + `checkh(idata_qq[0], 32'h0f7b3d59); + `checkh(idata_qq[1], 32'h1e6a2c48); + + idata_q = idata_logic_q_t'(128'hfeedface_deadbeef_cafebabe_12345678); + `checkh(idata_q[0], 32'hfeedface); + `checkh(idata_q[1], 32'hdeadbeef); + `checkh(idata_q[2], 32'hcafebabe); + `checkh(idata_q[3], 32'h12345678); + + idata_qq = {<<2{idata_logic_q_t'({<<{idata_q}})}}; + `checkh(idata_qq[0], 32'hfddef5cd); + `checkh(idata_qq[1], 32'hed5e7ddf); + `checkh(idata_qq[2], 32'hc5fd757d); + `checkh(idata_qq[3], 32'h2138a9b4); + end + + begin + qdata_logic_q_t qdata_q, qdata_qq; + + qdata_q.push_back(64'hdeadbeef_cafebabe); + qdata_q.push_back(64'hfeedface_12345678); + `checkh(qdata_q[0], 64'hdeadbeef_cafebabe); + `checkh(qdata_q[1], 64'hfeedface_12345678); + + qdata_qq = qdata_logic_q_t'({<<{qdata_q}}); + `checkh(qdata_qq[0], 64'h1e6a2c48735fb77f); + `checkh(qdata_qq[1], 64'h7d5d7f53f77db57b); + + qdata_q.push_back(64'h1111222233334444); + qdata_q.push_back(64'h5555666677778888); + qdata_qq = {<<2{qdata_q}}; + `checkh(qdata_qq[0], 64'h2222dddd99995555); + `checkh(qdata_qq[1], 64'h1111cccc88884444); + `checkh(qdata_qq[2], 64'h2d951c84b3af7bbf); + `checkh(qdata_qq[3], 64'hbeaebfa3fbbe7ab7); + end + + begin + wide_q_t wide_q, wide_qq; + + wide_q.push_back(128'hdeadbeef_cafebabe_feedface_12345678); + wide_q.push_back(128'h11112222_33334444_55556666_77778888); + `checkh(wide_q[0], 128'hdeadbeef_cafebabe_feedface_12345678); + `checkh(wide_q[1], 128'h11112222_33334444_55556666_77778888); + + wide_qq = wide_q_t'({<<{wide_q}}); + `checkh(wide_qq[0], 128'h1111eeee6666aaaa2222cccc44448888); + `checkh(wide_qq[1], 128'h1e6a2c48735fb77f7d5d7f53f77db57b); + + wide_q.push_back(128'haaaabbbb_ccccdddd_eeeeffff_00001111); + wide_q.push_back(128'h22223333_44445555_66667777_88889999); + + wide_qq = wide_q_t'({<<{wide_q}}); + wide_qq = {<<2{wide_q}}; + `checkh(wide_qq[0], 128'h66662222dddd999955551111cccc8888); + `checkh(wide_qq[1], 128'h44440000ffffbbbb77773333eeeeaaaa); + `checkh(wide_qq[2], 128'h2222dddd999955551111cccc88884444); + `checkh(wide_qq[3], 128'h2d951c84b3af7bbfbeaebfa3fbbe7ab7); + end + + begin + byte_q_t bytq_init; + byte_q_t bytq; + bit_q_t bitq; + + bytq_init.push_back(8'h84); + bytq_init.push_back(8'haa); + `checkh(bytq_init[0], 8'h84); + `checkh(bytq_init[1], 8'haa); + s = $sformatf("bytq_init=%p", bytq_init); + `checks(s, "bytq_init='{'h84, 'haa} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa} "); + + /* + Generalized block-reversal semantics for the outer left-stream when blockSize > 1. + This seemingly complicated approach is what is required to match commercial simulators, + otherwise the straggler bit [1] in the padded byte might end up as 0x01 instead of 0x80. + + Starting with result of inner {<<{bitq}}: [1,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,0] (17 bits), + apply outer {<<8{...}} using generalized block-reversal like this: + - Reverse all bits: [0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1] + - Split into 8-bit blocks from left and pad incomplete blocks on the left: + - Block 0: [0,0,1,0,0,0,0,1] (complete) + - Block 1: [0,1,0,1,0,1,0,1] (complete) + - Block 2: [1] -> pad on left -> [0,0,0,0,0,0,0,1] + - Reverse bits within each 8-bit block: + - Block 0: [0,0,1,0,0,0,0,1] -> [1,0,0,0,0,1,0,0] = 0x84 + - Block 1: [0,1,0,1,0,1,0,1] -> [1,0,1,0,1,0,1,0] = 0xaa + - Block 2: [0,0,0,0,0,0,0,1] -> [1,0,0,0,0,0,0,0] = 0x80 + */ + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1} "); + `checkh(bytq[0], 8'h84); + `checkh(bytq[1], 8'haa); + `checkh(bytq[2], 8'h80); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h80} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1} "); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'hc0} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'he0} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h70} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'hb8} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h5c} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h2e} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h97} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h97, 'h80} "); + end + + // Test StreamR (>>) operations - fairly simple since this should maintain left-to-right order. + begin + bit_q_t bitq; + byte_q_t bytq; + + bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; + bitq = {>>4{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + + bytq = {8'h84, 8'haa}; + bitq = {>>{bit_q_t'({<<{bytq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1} "); + + bitq = { + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b1, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + 1'b1, + 1'b0 + }; + bytq = {>>2{byte_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h43, 'h55} "); + + bytq = {8'h12, 8'h34, 8'h56}; + bytq = {>>{byte_q_t'({<<{bytq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h6a, 'h2c, 'h48} "); + + bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; + bitq = {>>6{bit_q_t'({>>{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + + bytq = {8'h84, 8'haa}; + bitq = {>>{bit_q_t'({>>{bytq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + + bitq = { + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b1, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + 1'b1, + 1'b0 + }; + bytq = {>>8{byte_q_t'({>>{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'haa, 'hc2} "); + + bytq = {8'h12, 8'h34, 8'h56}; + bytq = {>>{byte_q_t'({>>{bytq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h12, 'h34, 'h56} "); + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_stream_crc_example.py b/test_regress/t/t_stream_crc_example.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_stream_crc_example.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_stream_crc_example.v b/test_regress/t/t_stream_crc_example.v new file mode 100644 index 000000000..642ae5be8 --- /dev/null +++ b/test_regress/t/t_stream_crc_example.v @@ -0,0 +1,105 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the 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); +`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); + +typedef bit bit_q_t[$]; + +module t ( /*AUTOARG*/ + // Inputs + clk +); + input clk; + integer cyc = 0; + reg [63:0] crc = '0; + reg [63:0] sum = '0; + + // Take CRC data and apply to testblock inputs + wire [31:0] in = crc[31:0]; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [31:0] out; // From test of Test.v + // End of automatics + + Test test ( + .clk, + .in, + .out + ); + + // Aggregate outputs into a single result vector + wire [63:0] result = {32'h0, out}; + + initial begin + byte unsigned p[$]; + byte unsigned po[$]; + bit bits[$]; + string s; + + p = {8'h84, 8'haa}; + `checkh(p[0], 8'h84); + `checkh(p[1], 8'haa); + + bits = {<<8{bit_q_t'({<<{p}})}}; + bits.push_front(1'b0); + po = {<<8{bit_q_t'({<<{bits}})}}; + + s = $sformatf("p=%p", p); + `checks(s, "p='{'h84, 'haa} "); + + s = $sformatf("bits=%p", bits); + `checks(s, + "bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + + s = $sformatf("po=%p", po); + `checks(s, "po='{'h8, 'h55, 'h80} "); + end + + always_ff @(posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x sum=%x\n", $time, cyc, crc, result, sum); +`endif + + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + + if (cyc == 0) begin + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end else if (cyc < 10) begin + sum <= '0; + end else if (cyc == 99) begin + `checkh(crc, 64'hc77bb9b3784ea091); + `checkh(sum, 64'h9721d4e989defb24); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module Test ( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + byte unsigned p[$]; + byte unsigned po[$]; + bit bits[$]; + + always_ff @(posedge clk) begin + p = {in[31:24], in[23:16], in[15:8], in[7:0]}; + bits = {<<8{bit_q_t'({<<{p}})}}; + bits.push_front(1'b0); + po = {<<8{bit_q_t'({<<{bits}})}}; + out <= {po[3], po[2], po[1], po[0]}; + end +endmodule diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v index b83ea9174..ee6514123 100644 --- a/test_regress/t/t_stream_unpack.v +++ b/test_regress/t/t_stream_unpack.v @@ -280,8 +280,8 @@ module t (/*AUTOARG*/); o = {<<128{p}}; `checkh(o, 256'habcd0123456789abfadecafedeadbeeffadecafedeadbeefabcd0123456789ab); {>>{p}} = o; - `checkh(p[0], 128'hfadecafedeadbeefabcd0123456789ab); - `checkh(p[1], 128'habcd0123456789abfadecafedeadbeef); + `checkh(p[0], 128'habcd0123456789abfadecafedeadbeef); + `checkh(p[1], 128'hfadecafedeadbeefabcd0123456789ab); $write("*-* All Finished *-*\n"); $finish; From f3560837ecbbaa96c6bc4bfefe7f687400dccd91 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 17:11:35 -0400 Subject: [PATCH 084/252] Add error on missing forward declarations (#6207). --- Changes | 1 + src/V3Ast.cpp | 21 +++++++++++++ src/V3Ast.h | 4 +++ src/V3AstNodeOther.h | 23 +++++++++++++-- src/V3AstNodes.cpp | 10 +++++++ src/V3FileLine.cpp | 2 ++ src/V3FileLine.h | 14 ++++++--- src/V3LinkDot.cpp | 37 +++++++++++++++++++++-- src/V3Param.cpp | 14 +-------- src/V3ParseGrammar.cpp | 2 +- src/verilog.y | 18 ++++++------ test_regress/t/t_dump_json.out | 2 +- test_regress/t/t_typedef_fwd.py | 16 ++++++++++ test_regress/t/t_typedef_fwd.v | 42 ++++++++++++++++++++++++++ test_regress/t/t_typedef_fwd_bad.out | 44 ++++++++++++++++++++++++++++ test_regress/t/t_typedef_fwd_bad.py | 17 +++++++++++ 16 files changed, 234 insertions(+), 33 deletions(-) create mode 100755 test_regress/t/t_typedef_fwd.py create mode 100644 test_regress/t/t_typedef_fwd.v create mode 100644 test_regress/t/t_typedef_fwd_bad.out create mode 100755 test_regress/t/t_typedef_fwd_bad.py diff --git a/Changes b/Changes index 98e3cd8a8..e7f6f383a 100644 --- a/Changes +++ b/Changes @@ -17,6 +17,7 @@ Verilator 5.039 devel * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base data type, and wire data type checking per IEEE. +* Add error on missing forward declarations (#6207). [Alex Solomatnikov] * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 831ed01c7..b0d8f7cfe 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -58,6 +58,27 @@ const VNTypeInfo VNType::typeInfoTable[] = { std::ostream& operator<<(std::ostream& os, VNType rhs); +//###################################################################### +// VFwdType + +bool VFwdType::isNodeCompatible(const AstNode* nodep) const { + const AstNode* defp = nodep; + if (const AstTypedef* const adefp = VN_CAST(defp, Typedef)) defp = adefp->subDTypep(); + if (const AstNodeDType* const adefp = VN_CAST(defp, NodeDType)) + defp = adefp->skipRefToNonRefp(); + switch (m_e) { + case VFwdType::NONE: return true; break; + case VFwdType::ENUM: return VN_IS(defp, EnumDType); break; + case VFwdType::STRUCT: return VN_IS(defp, StructDType); break; + case VFwdType::UNION: return VN_IS(defp, UnionDType); break; + case VFwdType::INTERFACE_CLASS: // FALLTHRU // TODO: Over permissive for now + case VFwdType::CLASS: return VN_IS(defp, ClassRefDType) || VN_IS(defp, Class); break; + default: v3fatalSrc("Bad case"); + } + VL_UNREACHABLE; + return false; // LCOV_EXCL_LINE +} + //###################################################################### // VSelfPointerText diff --git a/src/V3Ast.h b/src/V3Ast.h index cb19127d5..13790a5cd 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -304,6 +304,8 @@ public: explicit VFwdType(int _e) : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning constexpr operator en() const { return m_e; } + // Is a node type compatible with the declaration + bool isNodeCompatible(const AstNode* nodep) const; }; constexpr bool operator==(const VFwdType& lhs, const VFwdType& rhs) { return lhs.m_e == rhs.m_e; } constexpr bool operator==(const VFwdType& lhs, VFwdType::en rhs) { return lhs.m_e == rhs; } @@ -2236,6 +2238,8 @@ public: } virtual void tag(const string& text) {} virtual string tag() const { return ""; } + virtual uint32_t declTokenNum() const { return 0; } + virtual void declTokenNumSetMin(uint32_t tokenNum) {} virtual string verilogKwd() const { return ""; } string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied string origNameProtect() const; // origName with --protect-id applied diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index b819ee829..f8d12c686 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1829,6 +1829,7 @@ class AstTypedef final : public AstNode { string m_name; string m_tag; // Holds the string of the verilator tag -- used in XML output. + uint32_t m_declTokenNum; // Declaration token number bool m_attrPublic = false; bool m_isHideLocal : 1; // Verilog local bool m_isHideProtected : 1; // Verilog protected @@ -1838,6 +1839,7 @@ public: AstNodeDType* dtp) : ASTGEN_SUPER_Typedef(fl) , m_name{name} + , m_declTokenNum{fl->tokenNum()} , m_isHideLocal{false} , m_isHideProtected{false} { childDTypep(dtp); // Only for parser @@ -1852,6 +1854,10 @@ public: return dtypep() ? dtypep() : childDTypep(); } // METHODS + uint32_t declTokenNum() const override { return m_declTokenNum; } + void declTokenNumSetMin(uint32_t tokenNum) override { + m_declTokenNum = std::min(m_declTokenNum, tokenNum); + } string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; } @@ -1868,15 +1874,20 @@ public: class AstTypedefFwd final : public AstNode { // Forward declaration of a type; stripped after netlist parsing is complete string m_name; + const VFwdType m_fwdType; // Forward type for lint check public: - AstTypedefFwd(FileLine* fl, const string& name) + AstTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) : ASTGEN_SUPER_TypedefFwd(fl) - , m_name{name} {} + , m_name{name} + , m_fwdType{fwdType} {} ASTGEN_MEMBERS_AstTypedefFwd; // METHODS string name() const override VL_MT_STABLE { return m_name; } + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; bool maybePointedTo() const override VL_MT_SAFE { return true; } + VFwdType fwdType() const { return m_fwdType; } }; class AstUdpTable final : public AstNode { // @astgen op1 := linesp : List[AstUdpTableLine] @@ -2505,6 +2516,7 @@ class AstClass final : public AstNodeModule { // @astgen op4 := extendsp : List[AstClassExtends] // MEMBERS // @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with + uint32_t m_declTokenNum; // Declaration token number VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends) bool m_extended = false; // Is extension or extended by other classes bool m_interfaceClass = false; // Interface class @@ -2514,7 +2526,8 @@ class AstClass final : public AstNodeModule { public: AstClass(FileLine* fl, const string& name, const string& libname) - : ASTGEN_SUPER_Class(fl, name, libname) {} + : ASTGEN_SUPER_Class(fl, name, libname) + , m_declTokenNum{fl->tokenNum()} {} ASTGEN_MEMBERS_AstClass; string verilogKwd() const override { return "class"; } bool maybePointedTo() const override VL_MT_SAFE { return true; } @@ -2538,6 +2551,10 @@ public: // Return true if this class is an extension of base class (SLOW) // Accepts nullptrs static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp); + uint32_t declTokenNum() const override { return m_declTokenNum; } + void declTokenNumSetMin(uint32_t tokenNum) override { + m_declTokenNum = std::min(m_declTokenNum, tokenNum); + } void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; } VBaseOverride baseOverride() const { return m_baseOverride; } // Return the lowest class extended from, or this class diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c4347bbfe..b02358d5d 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1771,6 +1771,7 @@ void AstClass::dump(std::ostream& str) const { if (useVirtualPublic()) str << " [VIRPUB]"; } void AstClass::dumpJson(std::ostream& str) const { + // dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file dumpJsonBoolFunc(str, isExtended); dumpJsonBoolFunc(str, isInterfaceClass); dumpJsonBoolFunc(str, isVirtual); @@ -2161,9 +2162,18 @@ void AstTypedef::dump(std::ostream& str) const { } } void AstTypedef::dumpJson(std::ostream& str) const { + // dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file dumpJsonBoolFunc(str, attrPublic); dumpJsonGen(str); } +void AstTypedefFwd::dump(std::ostream& str) const { + this->AstNode::dump(str); + str << " [" << fwdType().ascii() << "]"; +} +void AstTypedefFwd::dumpJson(std::ostream& str) const { + dumpJsonStr(str, "fwdType", fwdType().ascii()); + dumpJsonGen(str); +} void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); } void AstRange::dump(std::ostream& str) const { diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index b7778d2de..d65db10da 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -293,6 +293,8 @@ void FileLine::lineDirectiveParse(const char* textp, string& filenameRef, int& l } void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) { + static int s_tokenNum = 1; + m_tokenNum = s_tokenNum++; for (const char* sp = textp; size && *sp; ++sp, --size) { if (*sp == '\n') { if (trackLines) linenoInc(); diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 430ad04fd..27ad85ef7 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -146,7 +146,7 @@ class FileLine final { bool m_waive : 1; // Waive warning - pack next to the line number to save 8 bytes of storage unsigned m_contentLineno : 31; // Line number within source stream // 64-bit word # 1 - uint32_t m_pad = 0; // space for project coming soon + uint32_t m_tokenNum = 0; // Token number in processing order int m_firstLineno = 0; // `line corrected token's first line number // 64-bit word # 2 uint32_t m_firstColumn : 24; // `line corrected token's first column number @@ -197,6 +197,7 @@ public: , m_filenameno{from.m_filenameno} , m_waive{from.m_waive} , m_contentLineno{from.m_contentLineno} + , m_tokenNum{from.m_tokenNum} , m_firstLineno{from.m_firstLineno} , m_firstColumn{from.m_firstColumn} , m_lastColumn{from.m_lastColumn} @@ -210,6 +211,7 @@ public: , m_filenameno{fromp->m_filenameno} , m_waive{fromp->m_waive} , m_contentLineno{fromp->m_contentLineno} + , m_tokenNum{fromp->m_tokenNum} , m_firstLineno{fromp->m_firstLineno} , m_firstColumn{fromp->m_firstColumn} , m_lastColumn{fromp->m_lastColumn} @@ -272,6 +274,7 @@ public: } // Advance last line/column based on given text void forwardToken(const char* textp, size_t size, bool trackLines = true); + uint32_t tokenNum() const VL_MT_SAFE { return m_tokenNum; } int firstLineno() const VL_MT_SAFE { return m_firstLineno; } int firstColumn() const VL_MT_SAFE { return static_cast(m_firstColumn); } int lastLineno() const VL_MT_SAFE { return m_firstLineno + m_lastLinenoAdder; } @@ -386,9 +389,10 @@ public: /// Simplified information vs warnContextPrimary() to make dump clearer string warnContextSecondary() const { return warnContext(); } bool operator==(const FileLine& rhs) const { - return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn - && m_lastLinenoAdder == rhs.m_lastLinenoAdder && m_lastColumn == rhs.m_lastColumn - && m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx); + return (m_tokenNum == rhs.m_tokenNum && m_firstLineno == rhs.m_firstLineno + && m_firstColumn == rhs.m_firstColumn && m_lastLinenoAdder == rhs.m_lastLinenoAdder + && m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno + && m_msgEnIdx == rhs.m_msgEnIdx); } bool equalFirstLineCol(const FileLine& rhs) const { return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno @@ -408,6 +412,8 @@ public: for (size_t i = 0; i < msgEn().size(); ++i) { if (msgEn().test(i) != rhs.msgEn().test(i)) return rhs.msgEn().test(i) ? -1 : 1; } + // TokenNum is compared last as makes more logical sort order by file/line first + if (m_tokenNum != rhs.m_tokenNum) return (m_tokenNum < rhs.m_tokenNum) ? -1 : 1; return 0; // (*this) and rhs are equivalent } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 425bb1f40..585c9416c 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -310,6 +310,8 @@ public: << nodep->warnContextPrimary() << '\n' << fnodep->warnOther() << "... Location of original declaration\n" << fnodep->warnContextSecondary()); + nodep->declTokenNumSetMin(0); // Disable checking forward typedefs + fnodep->declTokenNumSetMin(0); // Disable checking forward typedefs } else { nodep->v3error("Unsupported in C: " << ucfirst(nodeTextType(nodep)) << " has the same name as " @@ -2063,8 +2065,13 @@ class LinkDotParamVisitor final : public VNVisitor { } 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 + if (foundp) { + // If the typedef was earlier in source order (tokenNum), then remember that earlier + // point to avoid error something wasn't declared + // Might be forward declaring something odd (with declTokenNumSetMin not implemented, + // but should be harmless to ignore as this is just for error detection + foundp->nodep()->declTokenNumSetMin(nodep->fileline()->tokenNum()); + } else if (v3Global.opt.pedantic()) { // We only check it was ever really defined in pedantic mode, as it // might have been in a header file referring to a module we never // needed so there are false positives @@ -2645,6 +2652,23 @@ class LinkDotResolveVisitor final : public VNVisitor { symIterateNull(nodep, m_statep->getNodeSym(nodep)); } + void checkDeclOrder(AstNode* nodep, AstNode* declp) { + uint32_t declTokenNum = declp->declTokenNum(); + if (!declTokenNum) return; // Not implemented/valid on this object + if (nodep->fileline()->tokenNum() < declTokenNum) { + UINFO(1, "Related node " << nodep->fileline()->tokenNum() << " " << nodep); + UINFO(1, "Related decl " << declTokenNum << " " << declp); + nodep->v3error("Reference to " + << nodep->prettyNameQ() << " before declaration (IEEE 1800-2023 6.18)\n" + << nodep->warnMore() + << "... Suggest move the declaration before the reference, or use a " + "forward typedef\n" + << nodep->warnContextPrimary() << '\n' + << declp->warnOther() << "... Location of original declaration\n" + << declp->warnContextSecondary()); + } + } + void replaceWithCheckBreak(AstNode* oldp, AstNodeDType* newp) { // Flag now to avoid V3Broken throwing an internal error if (oldp->wouldBreak(newp)) { @@ -3505,6 +3529,9 @@ class LinkDotResolveVisitor final : public VNVisitor { ok = m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE; if (ok) { AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()}; + // Don't check if typedef is to a :: as might not be + // resolved yet + if (m_ds.m_dotPos == DP_NONE) checkDeclOrder(nodep, defp); refp->typedefp(defp); if (VN_IS(nodep->backp(), SelExtract)) { m_packedArrayDtp = refp; @@ -4551,6 +4578,9 @@ class LinkDotResolveVisitor final : public VNVisitor { foundp = m_curSymp->findIdFlat(nodep->name()); } if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) { + // Don't check if typedef is to a :: as might not be resolved + // yet + if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); nodep->typedefp(defp); nodep->classOrPackagep(foundp->classOrPackagep()); } else if (AstParamTypeDType* const defp @@ -4566,6 +4596,9 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->classOrPackagep(foundp->classOrPackagep()); } } else if (AstClass* const defp = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) { + // Don't check if typedef is to a :: as might not be resolved + // yet + if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); AstPin* const paramsp = nodep->paramsp(); if (paramsp) paramsp->unlinkFrBackWithNext(); AstClassRefDType* const newp diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 09b527a65..4eba561cc 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -782,19 +782,7 @@ class ParamProcessor final { // Constify may have caused pinp->exprp to change rawTypep = VN_AS(pinp->exprp(), NodeDType); exprp = rawTypep->skipRefToNonRefp(); - bool ok = true; - switch (modvarp->fwdType()) { - case VFwdType::NONE: ok = true; break; - case VFwdType::ENUM: ok = VN_IS(exprp, EnumDType); break; - case VFwdType::STRUCT: ok = VN_IS(exprp, StructDType); break; - case VFwdType::UNION: ok = VN_IS(exprp, UnionDType); break; - case VFwdType::CLASS: ok = VN_IS(exprp, ClassRefDType); break; - case VFwdType::INTERFACE_CLASS: // TODO: Over permissive for now: - ok = VN_IS(exprp, ClassRefDType); - break; - default: modvarp->v3fatalSrc("Bad case"); - } - if (!ok) { + if (!modvarp->fwdType().isNodeCompatible(exprp)) { pinp->v3error("Parameter type expression type " << exprp->prettyDTypeNameQ() << " violates parameter's forwarding type '" diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 42d54bc0a..08b0fd4b4 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -260,7 +260,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name, } if (VN_IS(dtypep, ParseTypeDType)) { // Parser needs to know what is a type - AstNode* const newp = new AstTypedefFwd{fileline, name}; + AstNode* const newp = new AstTypedefFwd{fileline, name, VFwdType::NONE}; AstNode::addNext(nodep, newp); } // Don't set dtypep in the ranging; diff --git a/src/verilog.y b/src/verilog.y index 44354aece..680c0bd93 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -183,8 +183,8 @@ public: PARSEP->tagNodep(nodep); return nodep; } - AstNode* createTypedefFwd(FileLine* fl, const string& name) { - AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name}; + AstNode* createTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) { + AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name, fwdType}; PARSEP->tagNodep(nodep); return nodep; } @@ -2695,15 +2695,15 @@ type_declaration: // ==IEEE: type_declaration | yTYPEDEF idAny/*interface*/ '.' idAny/*type*/ idAny/*type*/ dtypeAttrListE ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 typedef in this context"); } // // idAny as also allows redeclaring same typedef again - | yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($2, *$2); } + | yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($2, *$2, VFwdType::NONE); } // // IEEE: expanded forward_type to prevent conflict - | yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($4, *$4); } + | yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::ENUM); } + | yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::STRUCT); } + | yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::UNION); } + | yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::CLASS); } + | yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($4, *$4, VFwdType::INTERFACE_CLASS); } // - | yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } + | yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::NONE); } ; dtypeAttrListE: diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index c2dc38ecd..0e44b1848 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -534,7 +534,7 @@ "childDTypep": [ {"type":"PARSETYPEDTYPE","name":"","addr":"(PI)","loc":"d,32:20,32:24","dtypep":"UNLINKED","generic":false} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"TYPEDEFFWD","name":"T","addr":"(QI)","loc":"d,32:25,32:26"}, + {"type":"TYPEDEFFWD","name":"T","addr":"(QI)","loc":"d,32:25,32:26","fwdType":"none"}, {"type":"VAR","name":"m_bound","addr":"(RI)","loc":"d,33:21,33:28","dtypep":"UNLINKED","origName":"m_bound","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ {"type":"BASICDTYPE","name":"int","addr":"(SI)","loc":"d,33:17,33:20","dtypep":"(SI)","keyword":"int","range":"31:0","generic":false,"rangep": []} diff --git a/test_regress/t/t_typedef_fwd.py b/test_regress/t/t_typedef_fwd.py new file mode 100755 index 000000000..cca4c9e73 --- /dev/null +++ b/test_regress/t/t_typedef_fwd.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('linter') + +test.lint() + +test.passes() diff --git a/test_regress/t/t_typedef_fwd.v b/test_regress/t/t_typedef_fwd.v new file mode 100644 index 000000000..c7d16e283 --- /dev/null +++ b/test_regress/t/t_typedef_fwd.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 + +package P; +`ifndef TEST_NO_TYPEDEFS + typedef enum enum_t; + typedef struct struct_t; + typedef union union_t; + typedef class ClsB; + typedef interface class IfC; + typedef generic_t; +`endif + + class ClsA; + enum_t m_e; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + struct_t m_s; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + union_t m_u; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + ClsB m_b; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + IfC m_i; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + generic_t m_g; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + endclass + + typedef enum {N = 0} enum_t; + + typedef struct packed {int s;} struct_t; + typedef union packed {int s;} union_t; + + class ClsB; + endclass + + interface class IfC; + endclass + + typedef int generic_t; + +endpackage + +module t; +endmodule diff --git a/test_regress/t/t_typedef_fwd_bad.out b/test_regress/t/t_typedef_fwd_bad.out new file mode 100644 index 000000000..497f19e7a --- /dev/null +++ b/test_regress/t/t_typedef_fwd_bad.out @@ -0,0 +1,44 @@ +%Error: t/t_typedef_fwd.v:18:5: Reference to 'enum_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 18 | enum_t m_e; + | ^~~~~~ + t/t_typedef_fwd.v:26:24: ... Location of original declaration + 26 | typedef enum {N = 0} enum_t; + | ^~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_typedef_fwd.v:19:5: Reference to 'struct_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 19 | struct_t m_s; + | ^~~~~~~~ + t/t_typedef_fwd.v:28:34: ... Location of original declaration + 28 | typedef struct packed {int s;} struct_t; + | ^~~~~~~~ +%Error: t/t_typedef_fwd.v:20:5: Reference to 'union_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 20 | union_t m_u; + | ^~~~~~~ + t/t_typedef_fwd.v:29:33: ... Location of original declaration + 29 | typedef union packed {int s;} union_t; + | ^~~~~~~ +%Error: t/t_typedef_fwd.v:21:5: Reference to 'ClsB' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 21 | ClsB m_b; + | ^~~~ + t/t_typedef_fwd.v:31:3: ... Location of original declaration + 31 | class ClsB; + | ^~~~~ +%Error: t/t_typedef_fwd.v:22:5: Reference to 'IfC' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 22 | IfC m_i; + | ^~~ + t/t_typedef_fwd.v:34:13: ... Location of original declaration + 34 | interface class IfC; + | ^~~~~ +%Error: t/t_typedef_fwd.v:23:5: Reference to 'generic_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 23 | generic_t m_g; + | ^~~~~~~~~ + t/t_typedef_fwd.v:37:15: ... Location of original declaration + 37 | typedef int generic_t; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_typedef_fwd_bad.py b/test_regress/t/t_typedef_fwd_bad.py new file mode 100755 index 000000000..cc0ca5d61 --- /dev/null +++ b/test_regress/t/t_typedef_fwd_bad.py @@ -0,0 +1,17 @@ +#!/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.top_filename = 't/t_typedef_fwd.v' + +test.lint(v_flags2=['+define+TEST_NO_TYPEDEFS'], fails=True, expect_filename=test.golden_filename) + +test.passes() From 470f99694e0913ea7e8c306400c841f079b6d011 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 26 Jul 2025 17:59:52 -0400 Subject: [PATCH 085/252] Revert d8dbb08a: Support bit queue streaming (#5830) (#6103) --- include/verilated_funcs.h | 211 +---------- src/V3Ast.cpp | 3 - src/V3AstNodeExpr.h | 31 -- src/V3AstNodes.cpp | 14 - src/V3Const.cpp | 126 +------ src/V3EmitCFunc.h | 33 -- src/V3Width.cpp | 28 -- test_regress/t/t_queue_concat_assign.v | 31 +- test_regress/t/t_stream_bad.out | 20 +- test_regress/t/t_stream_bad.v | 10 +- test_regress/t/t_stream_bitqueue.py | 18 - test_regress/t/t_stream_bitqueue.v | 490 ------------------------- test_regress/t/t_stream_crc_example.py | 18 - test_regress/t/t_stream_crc_example.v | 105 ------ test_regress/t/t_stream_unpack.v | 4 +- 15 files changed, 45 insertions(+), 1097 deletions(-) delete mode 100755 test_regress/t/t_stream_bitqueue.py delete mode 100644 test_regress/t/t_stream_bitqueue.v delete mode 100755 test_regress/t/t_stream_crc_example.py delete mode 100644 test_regress/t/t_stream_crc_example.v diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 09e0be17e..2bcb7f5fc 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2232,173 +2232,6 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn return owp; } -template -static inline VlQueue VL_CLONE_Q(const VlQueue& from, int lbits, int srcElementBits, - int dstElementBits) { - VlQueue ret; - VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits); - return ret; -} - -template -static inline VlQueue VL_REVCLONE_Q(const VlQueue& from, int lbits, int srcElementBits, - int dstElementBits) { - VlQueue ret; - VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits); - return ret; -} - -// Helper function to get a bit from a queue at a specific bit index -template -static inline bool VL_GET_QUEUE_BIT(const VlQueue& queue, int srcElementBits, size_t bitIndex) { - const size_t elemIdx = bitIndex / srcElementBits; - if (VL_UNLIKELY(elemIdx >= queue.size())) return false; - - const T element = queue.at(elemIdx); - if (srcElementBits == 1) { - return element & 1; - } else { - const size_t bitInElem = bitIndex % srcElementBits; - const size_t actualBitPos = srcElementBits - 1 - bitInElem; - return (element >> actualBitPos) & 1; - } -} - -// Helper function to set a bit in the destination queue -template -static inline void VL_SET_QUEUE_BIT(VlQueue& queue, int dstElementBits, size_t bitIndex, - bool value) { - if (dstElementBits == 1) { - if (VL_UNLIKELY(bitIndex >= queue.size())) return; - queue.atWrite(bitIndex) = value ? 1 : 0; - } else { - const size_t elemIdx = bitIndex / dstElementBits; - if (VL_UNLIKELY(elemIdx >= queue.size())) return; - const size_t bitInElem = bitIndex % dstElementBits; - const size_t actualBitPos = dstElementBits - 1 - bitInElem; - if (value) { - queue.atWrite(elemIdx) |= (static_cast(1) << actualBitPos); - } else { - queue.atWrite(elemIdx) &= ~(static_cast(1) << actualBitPos); - } - } -} - -// Helper function to get a bit from a VlWide queue at a specific bit index -template -static inline bool VL_GET_QUEUE_BIT(const VlQueue>& queue, int srcElementBits, - size_t bitIndex) { - const size_t elemIdx = bitIndex / srcElementBits; - if (VL_UNLIKELY(elemIdx >= queue.size())) return false; - - const VlWide& element = queue.at(elemIdx); - const size_t bitInElem = bitIndex % srcElementBits; - const size_t actualBitPos = srcElementBits - 1 - bitInElem; - - return VL_BITISSET_W(element.data(), actualBitPos); -} - -// Helper function to set a bit in a VlWide queue at a specific bit index -template -static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElementBits, - size_t bitIndex, bool value) { - const size_t elemIdx = bitIndex / dstElementBits; - if (VL_UNLIKELY(elemIdx >= queue.size())) return; - - const size_t bitInElem = bitIndex % dstElementBits; - const size_t actualBitPos = dstElementBits - 1 - bitInElem; - - VlWide& element = queue.atWrite(elemIdx); - if (value) { - VL_ASSIGNBIT_WO(actualBitPos, element.data()); - } else { - VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0); - } -} - -template -static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) { - elem = 0; -} - -template -static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) { - for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } -} - -// This specialization works for both VlQueue (and similar) as well -// as VlQueue>. -template -static inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits, - int dstElementBits) { - if (srcElementBits == dstElementBits) { - // Simple case: same element bit width, direct copy of each element - if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op - q = from; - } else { - // Different element bit widths: use streaming conversion - VlQueue srcCopy = from; - const size_t srcTotalBits = from.size() * srcElementBits; - const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; - q.renew(dstSize); - for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } - for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) { - VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex, - VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex)); - } - } -} - -// This specialization works for both VlQueue (and similar) as well -// as VlQueue>. -template -static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, - int srcElementBits, int dstElementBits) { - const size_t srcTotalBits = from.size() * srcElementBits; - const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; - - // Always make a copy to handle the case where q and from are the same queue - VlQueue srcCopy = from; - - q.renew(dstSize); - - // Initialize all elements to zero using appropriate method - for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } - - if (lbits == 1) { - // Simple bit reversal: write directly to destination - for (int i = srcTotalBits - 1; i >= 0; --i) { - VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i, - VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i)); - } - } else { - // Generalized block-reversal for lbits > 1: - // 1. Reverse all bits using 1-bit blocks - // 2. Split into lbits-sized blocks and pad incomplete blocks on the left - // 3. Reverse each lbits-sized block using 1-bit blocks - - const size_t numCompleteBlocks = srcTotalBits / lbits; - const size_t remainderBits = srcTotalBits % lbits; - const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0); - - size_t dstBitIndex = 0; - - for (size_t block = 0; block < srcBlocks; ++block) { - const size_t blockStart = block * lbits; - const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits; - - for (int bit = bitsToProcess - 1; bit >= 0; --bit) { - const size_t reversedBitIndex = blockStart + bit; - const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex; - VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++, - VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex)); - } - - dstBitIndex += lbits - bitsToProcess; - } - } -} - //====================================================================== // Expressions needing insert/select @@ -2406,49 +2239,49 @@ static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const QData mask = VL_MASK_Q(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { @@ -2456,11 +2289,7 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - // Extract from MSB to LSB: MSB goes to index 0 - const int bitPos = rbits - (i + 1) * lbits; - const int actualBitPos = (bitPos < 0) ? 0 : bitPos; - const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; } } @@ -2469,11 +2298,7 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - // Extract from MSB to LSB: MSB goes to index 0 - const int bitPos = rbits - (i + 1) * lbits; - const int actualBitPos = (bitPos < 0) ? 0 : bitPos; - const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; } } @@ -2482,11 +2307,7 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - // Extract from MSB to LSB: MSB goes to index 0 - const int bitPos = rbits - (i + 1) * lbits; - const int actualBitPos = (bitPos < 0) ? 0 : bitPos; - const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; } } @@ -2495,11 +2316,7 @@ static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const QData mask = VL_MASK_Q(lbits); for (size_t i = 0; i < size; ++i) { - // Extract from MSB to LSB: MSB goes to index 0 - const int bitPos = rbits - (i + 1) * lbits; - const int actualBitPos = (bitPos < 0) ? 0 : bitPos; - const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; - q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask; + q.atWrite(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask; } } @@ -2509,11 +2326,7 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue> const int size = (rbits + lbits - 1) / lbits; q.renew(size); for (size_t i = 0; i < size; ++i) { - // Extract from MSB to LSB: MSB goes to index 0 - const int bitPos = rbits - (i + 1) * lbits; - const int actualBitPos = (bitPos < 0) ? 0 : bitPos; - const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; - VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth); + VL_SEL_WWII(lbits, rbits, q.atWrite(i), rwp, i * lbits, lbits); } } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index b0d8f7cfe..995a06020 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1613,9 +1613,6 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) return VCastable::ENUM_IMPLICIT; if (fromNumericable) return VCastable::ENUM_EXPLICIT; - } else if (VN_IS(toDtp, QueueDType) - && (VN_IS(fromDtp, BasicDType) || VN_IS(fromDtp, StreamDType))) { - return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { if (fromConstp->isNull()) return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index d0b448719..d816fa2d5 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1129,37 +1129,6 @@ public: string emitC() final override { V3ERROR_NA_RETURN(""); } bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } }; -class AstCvtArrayToArray final : public AstNodeExpr { - // Copy/Cast from dynamic/unpacked types to dynamic/unpacked types - // @astgen op1 := fromp : AstNodeExpr -private: - const bool m_reverse; // whether ordering gets reversed in this operation (ex: {<<{expr}}) - const int m_blockSize; // num bits per block in a streaming operation (ex: 4 in {<<4{expr}})) - const int m_dstElementBits; // num bits in lhs (ex: 8 if to byte-queue, 1 if to bit-queue) - const int m_srcElementBits; // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue) - -public: - AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse, - int blockSize, int dstElementBits, int srcElementBits) - : ASTGEN_SUPER_CvtArrayToArray(fl) - , m_reverse{reverse} - , m_blockSize{blockSize} - , m_dstElementBits{dstElementBits} - , m_srcElementBits{srcElementBits} { - this->fromp(fromp); - dtypeFrom(dtp); - } - ASTGEN_MEMBERS_AstCvtArrayToArray; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - string emitVerilog() override { V3ERROR_NA_RETURN(""); } - string emitC() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { return true; } - bool reverse() const { return m_reverse; } - int blockSize() const { return m_blockSize; } - int dstElementBits() const { return m_dstElementBits; } - int srcElementBits() const { return m_srcElementBits; } -}; class AstCvtArrayToPacked final : public AstNodeExpr { // Cast from dynamic queue data type to packed array // @astgen op1 := fromp : AstNodeExpr diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b02358d5d..68901b507 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1696,20 +1696,6 @@ void AstCCast::dumpJson(std::ostream& str) const { dumpJsonNumFunc(str, size); dumpJsonGen(str); } -void AstCvtArrayToArray::dump(std::ostream& str) const { - this->AstNodeExpr::dump(str); - str << " reverse=" << reverse(); - str << " blockSize=" << blockSize(); - str << " dstElementBits=" << dstElementBits(); - str << " srcElementBits=" << srcElementBits(); -} -void AstCvtArrayToArray::dumpJson(std::ostream& str) const { - dumpJsonBoolFunc(str, reverse); - dumpJsonNumFunc(str, blockSize); - dumpJsonNumFunc(str, dstElementBits); - dumpJsonNumFunc(str, srcElementBits); - dumpJsonGen(str); -} void AstCell::dump(std::ostream& str) const { this->AstNode::dump(str); if (recursive()) str << " [RECURSIVE]"; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 058bf0493..c8d54a8c9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2205,23 +2205,8 @@ class ConstVisitor final : public VNVisitor { AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack(); AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack(); AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); - const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { - if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { - int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { - srcElementBits = elemDtp->width(); - } - int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { - dstElementBits = elemDtp->width(); - } - srcp = new AstCvtArrayToArray{ - srcp->fileline(), srcp, nodep->dtypep(), false, 1, - dstElementBits, srcElementBits}; - } else { - srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; - } + srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; } else if (VN_IS(srcDTypep, UnpackArrayDType)) { srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep}; // Handling the case where lhs is wider than rhs by inserting zeros. StreamL does @@ -2304,50 +2289,15 @@ class ConstVisitor final : public VNVisitor { // Further reduce, any of the nodes may have more reductions. return true; } else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) { + AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefp(); AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL); - AstNodeExpr* srcp = streamp->lhsp(); - AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); - AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); - if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) - || VN_IS(srcDTypep, UnpackArrayDType))) { - if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { - int blockSize = 1; - if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { - blockSize = constp->toSInt(); - if (VL_UNLIKELY(blockSize <= 0)) { - // Not reachable due to higher level checks when parsing stream - // operators commented out to not fail v3error-coverage-checks. - // nodep->v3error("Stream block size must be positive, got " << - // blockSize); nodep->v3error("Stream block size must be positive, got - // " << blockSize); - blockSize = 1; - } - } - // Not reachable due to higher level checks when parsing stream operators - // commented out to not fail v3error-coverage-checks. - // else { - // nodep->v3error("Stream block size must be constant (got " << - // streamp->rhsp()->prettyTypeName() << ")"); - // } - int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { - srcElementBits = elemDtp->width(); - } - int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { - dstElementBits = elemDtp->width(); - } - streamp->unlinkFrBack(); - srcp = new AstCvtArrayToArray{ - srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, true, - blockSize, dstElementBits, srcElementBits}; - nodep->rhsp(srcp); - VL_DO_DANGLING(pushDeletep(streamp), streamp); - } else { - streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), - dstDTypep}); - streamp->dtypeFrom(dstDTypep); - } + AstNodeExpr* const srcp = streamp->lhsp(); + const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) + || VN_IS(srcDTypep, UnpackArrayDType)) { + streamp->lhsp( + new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), lhsDtypep}); + streamp->dtypeFrom(lhsDtypep); } } else if (m_doV && replaceAssignMultiSel(nodep)) { return true; @@ -3133,64 +3083,6 @@ class ConstVisitor final : public VNVisitor { varrefp->varp()->valuep(initvaluep); } } - void visit(AstCvtArrayToArray* nodep) override { - iterateChildren(nodep); - // Handle the case where we have a stream operation inside a cast conversion - // To avoid infinite recursion, mark the node as processed by setting user1. - if (!nodep->user1()) { - nodep->user1(true); - // Check for both StreamL and StreamR operations - AstNodeStream* streamp = nullptr; - bool isReverse = false; - if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) { - streamp = streamLp; - isReverse = true; // StreamL reverses the operation - } else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) { - streamp = streamRp; - isReverse = false; // StreamR doesn't reverse the operation - } - if (streamp) { - AstNodeExpr* srcp = streamp->lhsp(); - AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); - AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp(); - if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) { - int blockSize = 1; - if (AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { - blockSize = constp->toSInt(); - if (VL_UNLIKELY(blockSize <= 0)) { - // Not reachable due to higher level checks when parsing stream - // operators commented out to not fail v3error-coverage-checks. - // nodep->v3error("Stream block size must be positive, got " << - // blockSize); - blockSize = 1; - } - } - // Not reachable due to higher level checks when parsing stream operators - // commented out to not fail v3error-coverage-checks. - // else { - // nodep->v3error("Stream block size must be constant (got " << - // streamp->rhsp()->prettyTypeName() << ")"); - // } - int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { - srcElementBits = elemDtp->width(); - } - int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { - dstElementBits = elemDtp->width(); - } - streamp->unlinkFrBack(); - AstNodeExpr* newp = new AstCvtArrayToArray{ - srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse, - blockSize, dstElementBits, srcElementBits}; - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(streamp), streamp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - return; - } - } - } - } void visit(AstRelease* nodep) override { if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) { FileLine* const flp = nodep->fileline(); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e489ae07c..c5e5c1efb 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -426,22 +426,6 @@ public: emitVarDecl(nodep); } - void visit(AstCvtArrayToArray* nodep) override { - if (nodep->reverse()) { - puts("VL_REVCLONE_Q("); - } else { - puts("VL_CLONE_Q("); - } - iterateAndNextConstNull(nodep->fromp()); - puts(", "); - puts(cvtToStr(nodep->blockSize())); - puts(", "); - puts(cvtToStr(nodep->srcElementBits())); - puts(", "); - puts(cvtToStr(nodep->dstElementBits())); - puts(")"); - } - void visit(AstCvtArrayToPacked* nodep) override { AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); AstNodeDType* const elemDtp = fromDtp->subDTypep()->skipRefp(); @@ -530,23 +514,6 @@ public: puts(", "); rhs = false; iterateAndNextConstNull(castp->fromp()); - } else if (const AstCvtArrayToArray* const castp - = VN_CAST(nodep->rhsp(), CvtArrayToArray)) { - if (castp->reverse()) { - putns(castp, "VL_REVCOPY_Q("); - } else { - putns(castp, "VL_COPY_Q("); - } - iterateAndNextConstNull(nodep->lhsp()); - puts(", "); - rhs = false; - iterateAndNextConstNull(castp->fromp()); - puts(", "); - puts(cvtToStr(castp->blockSize())); - puts(", "); - puts(cvtToStr(castp->srcElementBits())); - puts(", "); - puts(cvtToStr(castp->dstElementBits())); } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) // && !VN_IS(nodep->rhsp(), CExpr) // && !VN_IS(nodep->rhsp(), CMethodHard) // diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 603846fdf..5edaa1cd6 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1598,12 +1598,6 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); // Type set in constructor } - void visit(AstCvtArrayToArray* nodep) override { - if (nodep->didWidthAndSet()) return; - // Opaque returns, so arbitrary - userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); - // Type set in constructor - } void visit(AstCvtArrayToPacked* nodep) override { if (nodep->didWidthAndSet()) return; // Opaque returns, so arbitrary @@ -2207,28 +2201,6 @@ class WidthVisitor final : public VNVisitor { // Can just remove cast, but need extend placeholder // so we can avoid warning message } - } else if (VN_IS(toDtp, QueueDType)) { - if (VN_IS(fromDtp, BasicDType)) { - newp = new AstCvtPackedToArray{nodep->fileline(), - nodep->fromp()->unlinkFrBack(), toDtp}; - } else if (VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, StreamDType)) { - int srcElementBits = 1; - int dstElementBits = 1; - if (AstNodeDType* const elemDtp = fromDtp->subDTypep()) { - srcElementBits = elemDtp->width(); - } - const AstQueueDType* const dstQueueDtp = VN_AS(toDtp, QueueDType); - if (AstNodeDType* const elemDtp = dstQueueDtp->subDTypep()) { - dstElementBits = elemDtp->width(); - } - newp = new AstCvtArrayToArray{nodep->fileline(), - nodep->fromp()->unlinkFrBack(), - toDtp, - false, - 1, - dstElementBits, - srcElementBits}; - } } else if (VN_IS(toDtp, ClassRefDType)) { // Can just remove cast } else { diff --git a/test_regress/t/t_queue_concat_assign.v b/test_regress/t/t_queue_concat_assign.v index 2af584e29..b3860878e 100644 --- a/test_regress/t/t_queue_concat_assign.v +++ b/test_regress/t/t_queue_concat_assign.v @@ -4,25 +4,20 @@ // any use, without warranty, 2024 by Antmicro. // SPDX-License-Identifier: CC0-1.0 -module t ( /*AUTOARG*/); - initial begin - bit q1[$] = {1'b1}; - bit q2[$]; - bit q3[$]; - bit [1:0] d1[] = {2'b10}; - bit [1:0] d2[]; - // TODO: queue streaming support broke assignment like this. - // It's something to do witih computeCastable and V3Width.cpp - // q2 = {q1}; - // if (q2[0] != 1) $stop; +module t (/*AUTOARG*/); + initial begin + bit q1[$] = {1'b1}; + bit q2[$]; + bit [1:0] d1[] = {2'b10}; + bit [1:0] d2[]; + q2 = {q1}; + if (q2[0] != 1) $stop; - q3 = q1; - if (q3[0] != 1) $stop; + d2 = {2'b11}; + if (d2[0] != 2'b11) $stop; - d2 = {2'b11}; - if (d2[0] != 2'b11) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end - $write("*-* All Finished *-*\n"); - $finish; - end endmodule diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index acf856f50..ad2c9c4c4 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -1,18 +1,10 @@ -%Error: t/t_stream_bad.v:14:25: Expecting expression to be constant, but can't convert a RAND to constant. +%Error: t/t_stream_bad.v:12:32: Expecting expression to be constant, but can't convert a RAND to constant. : ... note: In instance 't' - 14 | packed_data_32 = {<<$random{byte_in}}; - | ^~~~~~~ + 12 | initial packed_data_32 = {<<$random{byte_in}}; + | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_stream_bad.v:14:23: Slice size isn't a constant or basic data type. +%Error: t/t_stream_bad.v:12:30: Slice size isn't a constant or basic data type. : ... note: In instance 't' - 14 | packed_data_32 = {<<$random{byte_in}}; - | ^~ -%Error: t/t_stream_bad.v:15:25: Expecting expression to be constant, but variable isn't const: 'x' - : ... note: In instance 't' - 15 | packed_data_32 = {<>{4'hd}}); - `checkh(bit_q[0], 1'b1); - `checkh(bit_q[1], 1'b1); - `checkh(bit_q[2], 1'b0); - `checkh(bit_q[3], 1'b1); - - bit_q = bit_q_t'({<<{4'hc}}); - `checkh(bit_q[0], 1'b0); - `checkh(bit_q[1], 1'b0); - `checkh(bit_q[2], 1'b1); - `checkh(bit_q[3], 1'b1); - - bit_q = {>>{bit_q_t'(4'he)}}; - `checkh(bit_q[0], 1'b1); - `checkh(bit_q[1], 1'b1); - `checkh(bit_q[2], 1'b1); - `checkh(bit_q[3], 1'b0); - - bit_q = {<<{bit_q_t'(4'hd)}}; - `checkh(bit_q[0], 1'b1); - `checkh(bit_q[1], 1'b0); - `checkh(bit_q[2], 1'b1); - `checkh(bit_q[3], 1'b1); - - bit_qq = {>>{bit_q}}; - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b0); - `checkh(bit_qq[2], 1'b1); - `checkh(bit_qq[3], 1'b1); - - bit_qq = {<<{bit_q}}; - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b1); - `checkh(bit_qq[2], 1'b0); - `checkh(bit_qq[3], 1'b1); - - bit_q = bit_q_t'({>>{4'hd}}); - `checkh(bit_q[0], 1'b1); - `checkh(bit_q[1], 1'b1); - `checkh(bit_q[2], 1'b0); - `checkh(bit_q[3], 1'b1); - - bit_q = bit_q_t'({>>2{4'hd}}); - `checkh(bit_q[0], 1'b1); - `checkh(bit_q[1], 1'b1); - `checkh(bit_q[2], 1'b0); - `checkh(bit_q[3], 1'b1); - - bit_qq = bit_q_t'({>>{bit_q}}); - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b1); - `checkh(bit_qq[2], 1'b0); - `checkh(bit_qq[3], 1'b1); - - bit_qq = bit_q_t'({>>2{bit_q}}); - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b1); - `checkh(bit_qq[2], 1'b0); - `checkh(bit_qq[3], 1'b1); - - bit_qq = bit_q_t'({<<{bit_q}}); - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b0); - `checkh(bit_qq[2], 1'b1); - `checkh(bit_qq[3], 1'b1); - - bit_qq = {<<2{bit_qq}}; - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b1); - `checkh(bit_qq[2], 1'b1); - `checkh(bit_qq[3], 1'b0); - - bit_qq = {<<2{bit_q_t'({<<{bit_q}})}}; - `checkh(bit_qq[0], 1'b1); - `checkh(bit_qq[1], 1'b1); - `checkh(bit_qq[2], 1'b1); - `checkh(bit_qq[3], 1'b0); - end - - begin - cdata_q_t cdata_q, cdata_qq; - - cdata_q = cdata_q_t'(32'hdeadbeef); - `checkh(cdata_q[0], 8'hde); - `checkh(cdata_q[1], 8'had); - `checkh(cdata_q[2], 8'hbe); - `checkh(cdata_q[3], 8'hef); - - cdata_qq = cdata_q_t'({<<{cdata_q}}); - `checkh(cdata_qq[0], 8'hf7); - `checkh(cdata_qq[1], 8'h7d); - `checkh(cdata_qq[2], 8'hb5); - `checkh(cdata_qq[3], 8'h7b); - - cdata_qq = {<<2{cdata_q}}; - `checkh(cdata_qq[0], 8'hfb); - `checkh(cdata_qq[1], 8'hbe); - `checkh(cdata_qq[2], 8'h7a); - `checkh(cdata_qq[3], 8'hb7); - end - - begin - sdata_logic_q_t sdata_q, sdata_qq; - - sdata_q = sdata_logic_q_t'(64'hfeedface_deadbeef); - `checkh(sdata_q[0], 16'hfeed); - `checkh(sdata_q[1], 16'hface); - `checkh(sdata_q[2], 16'hdead); - `checkh(sdata_q[3], 16'hbeef); - - sdata_qq = sdata_logic_q_t'({<<{sdata_q}}); - `checkh(sdata_qq[0], 16'hf77d); - `checkh(sdata_qq[1], 16'hb57b); - `checkh(sdata_qq[2], 16'h735f); - `checkh(sdata_qq[3], 16'hb77f); - - sdata_qq = {<<2{sdata_q}}; - `checkh(sdata_qq[0], 16'hfbbe); - `checkh(sdata_qq[1], 16'h7ab7); - `checkh(sdata_qq[2], 16'hb3af); - `checkh(sdata_qq[3], 16'h7bbf); - end - - begin - idata_logic_q_t idata_q, idata_qq; - - idata_q = idata_logic_q_t'(64'h12345678_9abcdef0); - `checkh(idata_q[0], 32'h12345678); - `checkh(idata_q[1], 32'h9abcdef0); - - idata_qq = idata_logic_q_t'({<<{idata_q}}); - `checkh(idata_qq[0], 32'h0f7b3d59); - `checkh(idata_qq[1], 32'h1e6a2c48); - - idata_q = idata_logic_q_t'(128'hfeedface_deadbeef_cafebabe_12345678); - `checkh(idata_q[0], 32'hfeedface); - `checkh(idata_q[1], 32'hdeadbeef); - `checkh(idata_q[2], 32'hcafebabe); - `checkh(idata_q[3], 32'h12345678); - - idata_qq = {<<2{idata_logic_q_t'({<<{idata_q}})}}; - `checkh(idata_qq[0], 32'hfddef5cd); - `checkh(idata_qq[1], 32'hed5e7ddf); - `checkh(idata_qq[2], 32'hc5fd757d); - `checkh(idata_qq[3], 32'h2138a9b4); - end - - begin - qdata_logic_q_t qdata_q, qdata_qq; - - qdata_q.push_back(64'hdeadbeef_cafebabe); - qdata_q.push_back(64'hfeedface_12345678); - `checkh(qdata_q[0], 64'hdeadbeef_cafebabe); - `checkh(qdata_q[1], 64'hfeedface_12345678); - - qdata_qq = qdata_logic_q_t'({<<{qdata_q}}); - `checkh(qdata_qq[0], 64'h1e6a2c48735fb77f); - `checkh(qdata_qq[1], 64'h7d5d7f53f77db57b); - - qdata_q.push_back(64'h1111222233334444); - qdata_q.push_back(64'h5555666677778888); - qdata_qq = {<<2{qdata_q}}; - `checkh(qdata_qq[0], 64'h2222dddd99995555); - `checkh(qdata_qq[1], 64'h1111cccc88884444); - `checkh(qdata_qq[2], 64'h2d951c84b3af7bbf); - `checkh(qdata_qq[3], 64'hbeaebfa3fbbe7ab7); - end - - begin - wide_q_t wide_q, wide_qq; - - wide_q.push_back(128'hdeadbeef_cafebabe_feedface_12345678); - wide_q.push_back(128'h11112222_33334444_55556666_77778888); - `checkh(wide_q[0], 128'hdeadbeef_cafebabe_feedface_12345678); - `checkh(wide_q[1], 128'h11112222_33334444_55556666_77778888); - - wide_qq = wide_q_t'({<<{wide_q}}); - `checkh(wide_qq[0], 128'h1111eeee6666aaaa2222cccc44448888); - `checkh(wide_qq[1], 128'h1e6a2c48735fb77f7d5d7f53f77db57b); - - wide_q.push_back(128'haaaabbbb_ccccdddd_eeeeffff_00001111); - wide_q.push_back(128'h22223333_44445555_66667777_88889999); - - wide_qq = wide_q_t'({<<{wide_q}}); - wide_qq = {<<2{wide_q}}; - `checkh(wide_qq[0], 128'h66662222dddd999955551111cccc8888); - `checkh(wide_qq[1], 128'h44440000ffffbbbb77773333eeeeaaaa); - `checkh(wide_qq[2], 128'h2222dddd999955551111cccc88884444); - `checkh(wide_qq[3], 128'h2d951c84b3af7bbfbeaebfa3fbbe7ab7); - end - - begin - byte_q_t bytq_init; - byte_q_t bytq; - bit_q_t bitq; - - bytq_init.push_back(8'h84); - bytq_init.push_back(8'haa); - `checkh(bytq_init[0], 8'h84); - `checkh(bytq_init[1], 8'haa); - s = $sformatf("bytq_init=%p", bytq_init); - `checks(s, "bytq_init='{'h84, 'haa} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa} "); - - /* - Generalized block-reversal semantics for the outer left-stream when blockSize > 1. - This seemingly complicated approach is what is required to match commercial simulators, - otherwise the straggler bit [1] in the padded byte might end up as 0x01 instead of 0x80. - - Starting with result of inner {<<{bitq}}: [1,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,0] (17 bits), - apply outer {<<8{...}} using generalized block-reversal like this: - - Reverse all bits: [0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1] - - Split into 8-bit blocks from left and pad incomplete blocks on the left: - - Block 0: [0,0,1,0,0,0,0,1] (complete) - - Block 1: [0,1,0,1,0,1,0,1] (complete) - - Block 2: [1] -> pad on left -> [0,0,0,0,0,0,0,1] - - Reverse bits within each 8-bit block: - - Block 0: [0,0,1,0,0,0,0,1] -> [1,0,0,0,0,1,0,0] = 0x84 - - Block 1: [0,1,0,1,0,1,0,1] -> [1,0,1,0,1,0,1,0] = 0xaa - - Block 2: [0,0,0,0,0,0,0,1] -> [1,0,0,0,0,0,0,0] = 0x80 - */ - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1} "); - `checkh(bytq[0], 8'h84); - `checkh(bytq[1], 8'haa); - `checkh(bytq[2], 8'h80); - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h80} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1} "); - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'hc0} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'he0} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h70} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'hb8} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h5c} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b0); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h2e} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h97} "); - - bytq = bytq_init; - bitq = {<<8{bit_q_t'({<<{bytq}})}}; - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bitq.push_back(1'b0); - bitq.push_back(1'b0); - bitq.push_back(1'b1); - bitq.push_back(1'b1); - bytq = {<<8{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h97, 'h80} "); - end - - // Test StreamR (>>) operations - fairly simple since this should maintain left-to-right order. - begin - bit_q_t bitq; - byte_q_t bytq; - - bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; - bitq = {>>4{bit_q_t'({<<{bitq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); - - bytq = {8'h84, 8'haa}; - bitq = {>>{bit_q_t'({<<{bytq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, - "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1} "); - - bitq = { - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b1, - 1'b0, - 1'b0, - 1'b0, - 1'b0, - 1'b1, - 1'b0 - }; - bytq = {>>2{byte_q_t'({<<{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h43, 'h55} "); - - bytq = {8'h12, 8'h34, 8'h56}; - bytq = {>>{byte_q_t'({<<{bytq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h6a, 'h2c, 'h48} "); - - bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; - bitq = {>>6{bit_q_t'({>>{bitq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); - - bytq = {8'h84, 8'haa}; - bitq = {>>{bit_q_t'({>>{bytq}})}}; - s = $sformatf("bitq=%p", bitq); - `checks(s, - "bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); - - bitq = { - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b0, - 1'b1, - 1'b1, - 1'b0, - 1'b0, - 1'b0, - 1'b0, - 1'b1, - 1'b0 - }; - bytq = {>>8{byte_q_t'({>>{bitq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'haa, 'hc2} "); - - bytq = {8'h12, 8'h34, 8'h56}; - bytq = {>>{byte_q_t'({>>{bytq}})}}; - s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h12, 'h34, 'h56} "); - end - - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_stream_crc_example.py b/test_regress/t/t_stream_crc_example.py deleted file mode 100755 index d4f986441..000000000 --- a/test_regress/t/t_stream_crc_example.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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() - -test.execute() - -test.passes() diff --git a/test_regress/t/t_stream_crc_example.v b/test_regress/t/t_stream_crc_example.v deleted file mode 100644 index 642ae5be8..000000000 --- a/test_regress/t/t_stream_crc_example.v +++ /dev/null @@ -1,105 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed into the 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); -`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); - -typedef bit bit_q_t[$]; - -module t ( /*AUTOARG*/ - // Inputs - clk -); - input clk; - integer cyc = 0; - reg [63:0] crc = '0; - reg [63:0] sum = '0; - - // Take CRC data and apply to testblock inputs - wire [31:0] in = crc[31:0]; - - /*AUTOWIRE*/ - // Beginning of automatic wires (for undeclared instantiated-module outputs) - wire [31:0] out; // From test of Test.v - // End of automatics - - Test test ( - .clk, - .in, - .out - ); - - // Aggregate outputs into a single result vector - wire [63:0] result = {32'h0, out}; - - initial begin - byte unsigned p[$]; - byte unsigned po[$]; - bit bits[$]; - string s; - - p = {8'h84, 8'haa}; - `checkh(p[0], 8'h84); - `checkh(p[1], 8'haa); - - bits = {<<8{bit_q_t'({<<{p}})}}; - bits.push_front(1'b0); - po = {<<8{bit_q_t'({<<{bits}})}}; - - s = $sformatf("p=%p", p); - `checks(s, "p='{'h84, 'haa} "); - - s = $sformatf("bits=%p", bits); - `checks(s, - "bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); - - s = $sformatf("po=%p", po); - `checks(s, "po='{'h8, 'h55, 'h80} "); - end - - always_ff @(posedge clk) begin -`ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d crc=%x result=%x sum=%x\n", $time, cyc, crc, result, sum); -`endif - - cyc <= cyc + 1; - crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; - sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; - - if (cyc == 0) begin - crc <= 64'h5aef0c8d_d70a4497; - sum <= '0; - end else if (cyc < 10) begin - sum <= '0; - end else if (cyc == 99) begin - `checkh(crc, 64'hc77bb9b3784ea091); - `checkh(sum, 64'h9721d4e989defb24); - $write("*-* All Finished *-*\n"); - $finish; - end - end - -endmodule - -module Test ( - input logic clk, - input logic [31:0] in, - output logic [31:0] out -); - byte unsigned p[$]; - byte unsigned po[$]; - bit bits[$]; - - always_ff @(posedge clk) begin - p = {in[31:24], in[23:16], in[15:8], in[7:0]}; - bits = {<<8{bit_q_t'({<<{p}})}}; - bits.push_front(1'b0); - po = {<<8{bit_q_t'({<<{bits}})}}; - out <= {po[3], po[2], po[1], po[0]}; - end -endmodule diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v index ee6514123..b83ea9174 100644 --- a/test_regress/t/t_stream_unpack.v +++ b/test_regress/t/t_stream_unpack.v @@ -280,8 +280,8 @@ module t (/*AUTOARG*/); o = {<<128{p}}; `checkh(o, 256'habcd0123456789abfadecafedeadbeeffadecafedeadbeefabcd0123456789ab); {>>{p}} = o; - `checkh(p[0], 128'habcd0123456789abfadecafedeadbeef); - `checkh(p[1], 128'hfadecafedeadbeefabcd0123456789ab); + `checkh(p[0], 128'hfadecafedeadbeefabcd0123456789ab); + `checkh(p[1], 128'habcd0123456789abfadecafedeadbeef); $write("*-* All Finished *-*\n"); $finish; From d93abd002fa243f58c622d14cfd76eef310440c6 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 03:36:41 -0400 Subject: [PATCH 086/252] Internals: V3Unroll refactoring. No functional change intended --- src/V3Unroll.cpp | 63 +++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 06d931db5..c344e819b 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -38,23 +38,32 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Unroll state, as a visitor of each AstNode class UnrollVisitor final : public VNVisitor { - // STATE + // STATE - across all visitors AstVar* m_forVarp; // Iterator variable const AstVarScope* m_forVscp; // Iterator variable scope (nullptr for generate pass) - AstConst* m_varValuep; // Current value of loop const AstNode* m_ignoreIncp; // Increment node to ignore bool m_varModeCheck; // Just checking RHS assignments - bool m_varModeReplace; // Replacing varrefs bool m_varAssignHit; // Assign var hit bool m_forkHit; // Fork hit bool m_generate; // Expand single generate For loop string m_beginName; // What name to give begin iterations + // STATE - Statistic tracking VDouble0 m_statLoops; // Statistic tracking VDouble0 m_statIters; // Statistic tracking // METHODS + void replaceVarRef(AstNode* bodyp, AstNode* varValuep) { + // Replace all occurances of loop variable in bodyp and next + bodyp->foreachAndNext([this, varValuep](AstVarRef* refp) { + if (refp->varp() == m_forVarp && refp->varScopep() == m_forVscp + && refp->access().isReadOnly()) { + AstNode* const newconstp = varValuep->cloneTree(false); + refp->replaceWith(newconstp); + VL_DO_DANGLING(pushDeletep(refp), refp); + } + }); + } - // VISITORS bool cantUnroll(AstNode* nodep, const char* reason) const { if (m_generate) nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason); @@ -198,15 +207,13 @@ class UnrollVisitor final : public VNVisitor { AstNode* clonep = nodep->cloneTree(true); UASSERT_OBJ(clonep, nodep, "Failed to clone tree"); if (loopValue) { - m_varValuep = new AstConst{nodep->fileline(), *loopValue}; + AstConst* varValuep = new AstConst{nodep->fileline(), *loopValue}; // Iteration requires a back, so put under temporary node AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep}; - m_varModeReplace = true; - iterateAndNextNull(tempp->stmtsp()); - m_varModeReplace = false; + replaceVarRef(tempp->stmtsp(), varValuep); clonep = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr); - VL_DO_CLEAR(pushDeletep(m_varValuep), m_varValuep = nullptr); + VL_DO_DANGLING(pushDeletep(varValuep), varValuep); } SimulateVisitor simvis; simvis.mainParamEmulate(clonep); @@ -288,8 +295,8 @@ class UnrollVisitor final : public VNVisitor { // Mark variable to disable some later warnings m_forVarp->usedLoopIdx(true); - AstNode* newbodysp = nullptr; ++m_statLoops; + AstNode* newbodysp = nullptr; if (stmtsp) { int times = 0; while (true) { @@ -302,27 +309,23 @@ class UnrollVisitor final : public VNVisitor { if (!res.isEqOne()) { break; // Done with the loop } else { - // Replace iterator values with constant. + // Replace iterator values with constant AstNode* oneloopp = stmtsp->cloneTree(true); - - m_varValuep = new AstConst{nodep->fileline(), loopValue}; - - // Iteration requires a back, so put under temporary node + AstConst* varValuep = new AstConst{nodep->fileline(), loopValue}; if (oneloopp) { + // Iteration requires a back, so put under temporary node AstBegin* const tempp = new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp}; - m_varModeReplace = true; - iterateAndNextNull(tempp->stmtsp()); - m_varModeReplace = false; + replaceVarRef(tempp->stmtsp(), varValuep); oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } if (m_generate) { - const string index = AstNode::encodeNumber(m_varValuep->toSInt()); + const string index = AstNode::encodeNumber(varValuep->toSInt()); const string nname = m_beginName + "__BRA__" + index + "__KET__"; oneloopp = new AstBegin{oneloopp->fileline(), nname, oneloopp, true}; } - VL_DO_CLEAR(pushDeletep(m_varValuep), m_varValuep = nullptr); + VL_DO_DANGLING(pushDeletep(varValuep), varValuep); if (newbodysp) { newbodysp->addNext(oneloopp); } else { @@ -373,10 +376,10 @@ class UnrollVisitor final : public VNVisitor { return true; } + // VISITORS void visit(AstWhile* nodep) override { iterateChildren(nodep); - if (m_varModeCheck || m_varModeReplace) { - } else { + if (!m_varModeCheck) { // Constify before unroll call, as it may change what is underneath. if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change // Grab initial value @@ -406,11 +409,10 @@ class UnrollVisitor final : public VNVisitor { } } void visit(AstGenFor* nodep) override { - if (!m_generate || m_varModeReplace) { + if (!m_generate) { iterateChildren(nodep); } // else V3Param will recursively call each for loop to be unrolled for us - if (m_varModeCheck || m_varModeReplace) { - } else { + if (!m_varModeCheck) { // Constify before unroll call, as it may change what is underneath. if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change @@ -446,13 +448,6 @@ class UnrollVisitor final : public VNVisitor { UINFO(8, " Itervar assigned to: " << nodep); m_varAssignHit = true; } - - if (m_varModeReplace && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp - && nodep->access().isReadOnly()) { - AstNode* const newconstp = m_varValuep->cloneTree(false); - nodep->replaceWith(newconstp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - } } void visit(AstFork* nodep) override { @@ -466,8 +461,6 @@ class UnrollVisitor final : public VNVisitor { } } - //-------------------- - // Default: Just iterate void visit(AstNode* nodep) override { if (m_varModeCheck && nodep == m_ignoreIncp) { // Ignore subtree that is the increment @@ -487,10 +480,8 @@ public: void init(bool generate, const string& beginName) { m_forVarp = nullptr; m_forVscp = nullptr; - m_varValuep = nullptr; m_ignoreIncp = nullptr; m_varModeCheck = false; - m_varModeReplace = false; m_varAssignHit = false; m_forkHit = false; m_generate = generate; From 1725ee9c5200eaa8b727685dfda52fefdda2d521 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 03:42:41 -0400 Subject: [PATCH 087/252] Fix loop initialization visibility outside loop (#4237). --- Changes | 1 + src/V3Unroll.cpp | 11 ++++++ test_regress/t/t_unroll_delay.out | 8 ++-- test_regress/t/t_unroll_double.out | 7 ++++ test_regress/t/t_unroll_double.py | 18 +++++++++ test_regress/t/t_unroll_double.v | 50 ++++++++++++++++++++++++ test_regress/t/t_unroll_double_unroll.py | 20 ++++++++++ 7 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 test_regress/t/t_unroll_double.out create mode 100755 test_regress/t/t_unroll_double.py create mode 100644 test_regress/t/t_unroll_double.v create mode 100755 test_regress/t/t_unroll_double_unroll.py diff --git a/Changes b/Changes index e7f6f383a..34d1f36ce 100644 --- a/Changes +++ b/Changes @@ -30,6 +30,7 @@ Verilator 5.039 devel * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] * Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] * Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] +* Fix loop initialization visibility outside loop (#4237). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). * Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler] diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index c344e819b..99e7aa89f 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -297,6 +297,17 @@ class UnrollVisitor final : public VNVisitor { ++m_statLoops; AstNode* newbodysp = nullptr; + if (initp && !m_generate) { // Set variable to initial value (may optimize away later) + AstNode* clonep = initp->cloneTree(true); + AstConst* varValuep = new AstConst{nodep->fileline(), loopValue}; + // Iteration requires a back, so put under temporary node + AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep}; + replaceVarRef(clonep, varValuep); + clonep = tempp->stmtsp()->unlinkFrBackWithNext(); + VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr); + VL_DO_DANGLING(pushDeletep(varValuep), varValuep); + newbodysp = clonep; + } if (stmtsp) { int times = 0; while (true) { diff --git a/test_regress/t/t_unroll_delay.out b/test_regress/t/t_unroll_delay.out index 1991fe349..a8d3a7b2e 100644 --- a/test_regress/t/t_unroll_delay.out +++ b/test_regress/t/t_unroll_delay.out @@ -1,14 +1,14 @@ [0] A 1 6 -[0] B 0 0 +[0] B 1 6 [1] C 1 6 [1] A 1 7 -[1] B 0 7 +[1] B 1 7 [2] C 1 7 -[2] B 0 8 +[2] B 1 8 [11] A 2 6 -[11] B 2 8 +[11] B 2 6 [12] C 2 6 [12] A 2 7 [12] B 2 7 diff --git a/test_regress/t/t_unroll_double.out b/test_regress/t/t_unroll_double.out new file mode 100644 index 000000000..e72040d3f --- /dev/null +++ b/test_regress/t/t_unroll_double.out @@ -0,0 +1,7 @@ +exit_a 0 0 A0 B0 C0 D0 B1 C1 D1 B2 C2 D2 Y3 Z3 A13 B10 C10 D10 B11 C11 D11 B12 C12 D12 Y13 Z13 A23 B20 C20 D20 B21 C21 D21 B22 C22 D22 Y23 Z23 +exit_a 0 1 A0 B0 C0 D0 B1 B2 C2 D2 Y3 Z3 A13 B10 C10 D10 B11 B12 C12 D12 Y13 Z13 A23 B20 C20 D20 B21 B22 C22 D22 Y23 Z23 +exit_a 0 2 A0 B0 C0 D0 B1 C1 D1 B2 C2 D2 Y3 Z3 A13 B10 C10 A20 B20 C20 D20 B21 C21 D21 B22 C22 D22 Y23 Z23 +exit_a 1 0 A0 B0 C0 D0 B1 C1 D1 B2 C2 D2 Y3 Z3 A13 B10 C10 D10 B11 C11 D11 B12 C12 D12 Y13 A23 B20 C20 D20 B21 C21 D21 B22 C22 D22 Y23 Z23 +exit_a 1 1 A0 B0 C0 D0 B1 B2 C2 D2 Y3 Z3 A13 B10 C10 D10 B11 B12 C12 D12 Y13 A23 B20 C20 D20 B21 B22 C22 D22 Y23 Z23 +exit_a 1 2 A0 B0 C0 D0 B1 C1 D1 B2 C2 D2 Y3 Z3 A13 B10 C10 A20 B20 C20 D20 B21 C21 D21 B22 C22 D22 Y23 Z23 +*-* All Finished *-* diff --git a/test_regress/t/t_unroll_double.py b/test_regress/t/t_unroll_double.py new file mode 100755 index 000000000..15f66fc8d --- /dev/null +++ b/test_regress/t/t_unroll_double.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(v_flags2=['+define+TEST_DISABLE']) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_unroll_double.v b/test_regress/t/t_unroll_double.v new file mode 100644 index 000000000..652568b22 --- /dev/null +++ b/test_regress/t/t_unroll_double.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`ifndef VERILATOR + `define PRAGMA +`elsif TEST_DISABLE + `define PRAGMA /*verilator unroll_disable*/ +`elsif TEST_FULL + `define PRAGMA /*verilator unroll_full*/ +`endif + +module t; + + int a, b; + int pos; + + initial begin + for (int exit_a = 0; exit_a < 2; ++exit_a) begin + `PRAGMA + for (int exit_b = 0; exit_b < 3; ++exit_b) begin + `PRAGMA + b = 0; + $write("exit_a %0d %0d", exit_a, exit_b); + for (a = 0; a < 3; ++a) begin : a_loop + `PRAGMA + $write(" A%0d", a * 10 + b); + for (b = 0; b < 3; ++b) begin : b_loop + `PRAGMA + $write(" B%0d", a * 10 + b); + if (exit_b == 1 && b == 1) disable b_loop; + $write(" C%0d", a * 10 + b); + if (exit_b == 2 && a == 1) disable a_loop; + $write(" D%0d", a * 10 + b); + end + $write(" Y%0d", a * 10 + b); + if (exit_a == 1 && a == 1) disable a_loop; + $write(" Z%0d", a * 10 + b); + end + $display; + end + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_unroll_double_unroll.py b/test_regress/t/t_unroll_double_unroll.py new file mode 100755 index 000000000..c6dcf1cdb --- /dev/null +++ b/test_regress/t/t_unroll_double_unroll.py @@ -0,0 +1,20 @@ +#!/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.top_filename = 't/t_unroll_double.v' +test.golden_filename = 't/t_unroll_double.out' + +test.compile(v_flags2=['+define+TEST_FULL']) + +test.execute(expect_filename=test.golden_filename) + +test.passes() From 895b85a16ec108f8e71cc4b240079769d646ddcf Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 27 Jul 2025 11:30:19 +0100 Subject: [PATCH 088/252] Fix Replicate with unsigned count but MSB set (#6231) (#6233) Correctly compute witdh of AstReplicate in its constructor when the count is unsigned but its MSB is set. --- src/V3AstNodeExpr.h | 5 +++-- test_regress/t/t_dfg_peephole.v | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index d816fa2d5..9fc16f9ed 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -3367,8 +3367,9 @@ public: : ASTGEN_SUPER_Replicate(fl, lhsp, rhsp) { if (lhsp) { if (const AstConst* const constp = VN_CAST(rhsp, Const)) { - if (constp->num().isFourState() || constp->num().isNegative()) { // V3Width warns - dtypeSetLogicSized(lhsp->width(), VSigning::UNSIGNED); + if (constp->num().isFourState() + || (constp->dtypep()->isSigned() && constp->num().isNegative())) { + dtypeSetLogicSized(lhsp->width(), VSigning::UNSIGNED); // V3Width warns } else { dtypeSetLogicSized(lhsp->width() * constp->toSInt(), VSigning::UNSIGNED); } diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index cd2ed056a..1789a7b9d 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -243,6 +243,10 @@ module t ( assign ascending_assign[4:7] = arand_b[0:3]; `signal(ASCENDING_ASSIGN, ascending_assign); + // Special cases to be covered + `signal(REPLICATE_WIDTH, {4'd8{rand_a[0]}}); // Replicate count unsigned, but MSB set + if ($bits(REPLICATE_WIDTH) != 8) $fatal("%0d != 8", $bits(REPLICATE_WIDTH)); + // Sel from not requires the operand to have a sinle sink, so can't use // the chekc due to the raw expression referencing the operand wire [63:0] sel_from_not_tmp = ~(rand_a >> rand_b[2:0] << rand_a[3:0]); From c4b3f1e99cde65ce8015f05755a31875bcc9a04e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 09:43:24 -0400 Subject: [PATCH 089/252] Tests: Add test and assert for nested simulated loops (#6223) --- src/V3Simulate.h | 2 + ...ble_unroll.py => t_unroll_double_param.py} | 5 +- test_regress/t/t_unroll_double_param.v | 46 +++++++++++++++++++ ..._unroll_double.out => t_unroll_nested.out} | 0 ...{t_unroll_double.py => t_unroll_nested.py} | 0 .../{t_unroll_double.v => t_unroll_nested.v} | 0 test_regress/t/t_unroll_nested_unroll.py | 23 ++++++++++ 7 files changed, 73 insertions(+), 3 deletions(-) rename test_regress/t/{t_unroll_double_unroll.py => t_unroll_double_param.py} (78%) create mode 100644 test_regress/t/t_unroll_double_param.v rename test_regress/t/{t_unroll_double.out => t_unroll_nested.out} (100%) rename test_regress/t/{t_unroll_double.py => t_unroll_nested.py} (100%) rename test_regress/t/{t_unroll_double.v => t_unroll_nested.v} (100%) create mode 100755 test_regress/t/t_unroll_nested_unroll.py diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 59f2614b6..516af3400 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -973,6 +973,8 @@ private: checkNodeInfo(nodep); if (!m_checkOnly) { UINFO(5, " JUMP GO " << nodep); + // Should be back at the JumpBlock and clear m_jumpp before another JumpGo + UASSERT_OBJ(!m_jumpp, nodep, "Jump inside jump"); m_jumpp = nodep; } } diff --git a/test_regress/t/t_unroll_double_unroll.py b/test_regress/t/t_unroll_double_param.py similarity index 78% rename from test_regress/t/t_unroll_double_unroll.py rename to test_regress/t/t_unroll_double_param.py index c6dcf1cdb..7de8d399e 100755 --- a/test_regress/t/t_unroll_double_unroll.py +++ b/test_regress/t/t_unroll_double_param.py @@ -10,10 +10,9 @@ import vltest_bootstrap test.scenarios('simulator') -test.top_filename = 't/t_unroll_double.v' -test.golden_filename = 't/t_unroll_double.out' +test.golden_filename = 't/t_unroll_nested.out' -test.compile(v_flags2=['+define+TEST_FULL']) +test.compile() test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unroll_double_param.v b/test_regress/t/t_unroll_double_param.v new file mode 100644 index 000000000..52ae975f8 --- /dev/null +++ b/test_regress/t/t_unroll_double_param.v @@ -0,0 +1,46 @@ +// 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; + + int a, b; + int pos; + + function string value; + // Debug 'initial' loops first + value = ""; + for (int exit_a = 0; exit_a < 2; ++exit_a) begin + for (int exit_b = 0; exit_b < 3; ++exit_b) begin + b = 0; + value = {value, $sformatf("exit_a %0d %0d", exit_a, exit_b)}; + for (a = 0; a < 3; ++a) begin : a_loop + value = {value, $sformatf(" A%0d", a * 10 + b)}; + for (b = 0; b < 3; ++b) begin : b_loop + value = {value, $sformatf(" B%0d", a * 10 + b)}; + if (exit_b == 1 && b == 1) disable b_loop; + value = {value, $sformatf(" C%0d", a * 10 + b)}; + if (exit_b == 2 && a == 1) disable a_loop; + value = {value, $sformatf(" D%0d", a * 10 + b)}; + end + value = {value, $sformatf(" Y%0d", a * 10 + b)}; + if (exit_a == 1 && a == 1) disable a_loop; + value = {value, $sformatf(" Z%0d", a * 10 + b)}; + end + value = {value, "\n"}; + end + end + endfunction + + localparam string VALUE = value(); + + initial begin + $write("%s", VALUE); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_unroll_double.out b/test_regress/t/t_unroll_nested.out similarity index 100% rename from test_regress/t/t_unroll_double.out rename to test_regress/t/t_unroll_nested.out diff --git a/test_regress/t/t_unroll_double.py b/test_regress/t/t_unroll_nested.py similarity index 100% rename from test_regress/t/t_unroll_double.py rename to test_regress/t/t_unroll_nested.py diff --git a/test_regress/t/t_unroll_double.v b/test_regress/t/t_unroll_nested.v similarity index 100% rename from test_regress/t/t_unroll_double.v rename to test_regress/t/t_unroll_nested.v diff --git a/test_regress/t/t_unroll_nested_unroll.py b/test_regress/t/t_unroll_nested_unroll.py new file mode 100755 index 000000000..ed5147555 --- /dev/null +++ b/test_regress/t/t_unroll_nested_unroll.py @@ -0,0 +1,23 @@ +#!/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.top_filename = 't/t_unroll_nested.v' +test.golden_filename = 't/t_unroll_nested.out' + +test.compile(v_flags2=['+define+TEST_FULL', '--stats']) + +test.execute(expect_filename=test.golden_filename) + +test.file_grep(test.stats, r'Optimizations, Unrolled Iterations\s+(\d+)', 11) +test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 4) + +test.passes() From 55b836e25a41b3b76b6de67728e0064b84f6e80c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 09:44:51 -0400 Subject: [PATCH 090/252] Commentary: Changes update --- Changes | 7 ++++++- docs/spelling.txt | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 34d1f36ce..361ad3b5c 100644 --- a/Changes +++ b/Changes @@ -17,9 +17,11 @@ Verilator 5.039 devel * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base data type, and wire data type checking per IEEE. -* Add error on missing forward declarations (#6207). [Alex Solomatnikov] +* Add error on missing forward declarations (#6206). [Alex Solomatnikov] * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] +* Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] +* Support Verilog real ports as SystemC double ports (#6136) (#6158). [George Polack] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] @@ -49,6 +51,7 @@ Verilator 5.039 devel * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] * Fix 'driver same component' assertion (#6211) (#6215). [Geza Lore] * Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] +* Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] Verilator 5.038 2025-07-08 @@ -140,6 +143,8 @@ Verilator 5.038 2025-07-08 * Fix interface array connections with non-zero low declaration index. * Fix developer build error on MacOS/Flex2.6.4 (#6153). [Paul Swirhun] * Fix crash with --dumpi-V3LinkDot without --debug (#6159). [Igor Zaworski, Antmicro Ltd.] +* Fix dereferencing stale iterator in DfgVertex::scopep() (#6225) (#6227). [Geza Lore] +* Fix component numbers of new Vertices in V3DfgBreakCycles (#6225) (#6228). [Geza Lore] Verilator 5.036 2025-04-27 diff --git a/docs/spelling.txt b/docs/spelling.txt index 055e931ab..c04bc2350 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -630,6 +630,7 @@ deparametrized der dereference dereferenced +dereferencing desassign deserialize destructor From d359fffcdc0c045d275cabcfc4027da0e76a01f9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 10:30:19 -0400 Subject: [PATCH 091/252] Internals: Refactor V3Task and add loop assert (#6218) --- src/V3Task.cpp | 34 +++++++++--------- test_regress/t/t_func_while2.py | 18 ++++++++++ test_regress/t/t_func_while2.v | 36 +++++++++++++++++++ ...uble_param.py => t_unroll_nested_param.py} | 0 ...double_param.v => t_unroll_nested_param.v} | 0 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100755 test_regress/t/t_func_while2.py create mode 100644 test_regress/t/t_func_while2.v rename test_regress/t/{t_unroll_double_param.py => t_unroll_nested_param.py} (100%) rename test_regress/t/{t_unroll_double_param.v => t_unroll_nested_param.v} (100%) diff --git a/src/V3Task.cpp b/src/V3Task.cpp index c66dcb463..314ea0986 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1435,9 +1435,9 @@ class TaskVisitor final : public VNVisitor { VL_RESTORER(m_modp); VL_RESTORER(m_modNCalls); m_modp = nodep; - m_insStmtp = nullptr; m_modNCalls = 0; iterateChildren(nodep); + UASSERT_OBJ(!m_insStmtp, nodep, "Didn't finish out last statement"); } void visit(AstWith* nodep) override { if (nodep->user1SetOnce()) { @@ -1451,8 +1451,8 @@ class TaskVisitor final : public VNVisitor { void visit(AstScope* nodep) override { VL_RESTORER(m_scopep); m_scopep = nodep; - m_insStmtp = nullptr; iterateChildren(nodep); + UASSERT_OBJ(!m_insStmtp, nodep, "Didn't finish out last statement"); } void visit(AstNodeFTaskRef* nodep) override { if (m_inSensesp) { @@ -1486,7 +1486,7 @@ class TaskVisitor final : public VNVisitor { ++m_statInlines; } - if (VN_IS(nodep, New)) { + if (VN_IS(nodep, New)) { // New not legal as while() condition insertBeforeStmt(nodep, beginp); UASSERT_OBJ(cnewp, nodep, "didn't create cnew for new"); nodep->replaceWith(cnewp); @@ -1508,7 +1508,7 @@ class TaskVisitor final : public VNVisitor { nodep->replaceWith(beginp); VL_DO_DANGLING(nodep->deleteTree(), nodep); VIsCached::clearCacheTree(); - } else { + } else { // VN_IS(nodep->backp(), StmtExpr) insertBeforeStmt(nodep, beginp); if (nodep->taskp()->isFunction()) { nodep->v3warn( @@ -1588,16 +1588,18 @@ class TaskVisitor final : public VNVisitor { } void visit(AstWhile* nodep) override { // Special, as statements need to be put in different places - // Conditions insert first at end of precondsp. - // TODO: is this right? This is how it used to be. - m_insStmtp = nodep; - iterateAndNextNull(nodep->condp()); - // Body insert just before themselves - m_insStmtp = nullptr; // First thing should be new statement - iterateAndNextNull(nodep->stmtsp()); - iterateAndNextNull(nodep->incsp()); - // Done the loop - m_insStmtp = nullptr; // Next thing should be new statement + { + // Conditions will create a StmtExpr + // Leave m_instStmtp = null, so will assert if not + iterateAndNextNull(nodep->condp()); + } + { + // Body insert just before themselves + VL_RESTORER(m_insStmtp); + m_insStmtp = nullptr; // First thing should be new statement + iterateAndNextNull(nodep->stmtsp()); + iterateAndNextNull(nodep->incsp()); + } } void visit(AstNodeForeach* nodep) override { // LCOV_EXCL_LINE nodep->v3fatalSrc( @@ -1608,15 +1610,15 @@ class TaskVisitor final : public VNVisitor { "For statements should have been converted to while statements in V3Begin.cpp"); } void visit(AstNodeStmt* nodep) override { + VL_RESTORER(m_insStmtp); m_insStmtp = nodep; iterateChildren(nodep); - m_insStmtp = nullptr; // Next thing should be new statement } void visit(AstStmtExpr* nodep) override { + VL_RESTORER(m_insStmtp); m_insStmtp = nodep; iterateChildren(nodep); if (!nodep->exprp()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - m_insStmtp = nullptr; // Next thing should be new statement } void visit(AstSenItem* nodep) override { UASSERT_OBJ(!m_inSensesp, nodep, "Senitem under senitem?"); diff --git a/test_regress/t/t_func_while2.py b/test_regress/t/t_func_while2.py new file mode 100755 index 000000000..563b6fc6f --- /dev/null +++ b/test_regress/t/t_func_while2.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_func_while2.v b/test_regress/t/t_func_while2.v new file mode 100644 index 000000000..8cbb204a5 --- /dev/null +++ b/test_regress/t/t_func_while2.v @@ -0,0 +1,36 @@ +// 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 + +// verilog_format: off +`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); +// verilog_format: on + +module t; + + int i; + string value; + + function automatic int count(); + ++i; + value = {value, $sformatf(" count%0d", i)}; + return i; + endfunction + + initial begin + value = ""; + i = 0; + while (count() <= 2) begin + // verilator unroll_disable + value = {value, " loop"}; + end + `checks(value, " count1 loop count2 loop count3"); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_unroll_double_param.py b/test_regress/t/t_unroll_nested_param.py similarity index 100% rename from test_regress/t/t_unroll_double_param.py rename to test_regress/t/t_unroll_nested_param.py diff --git a/test_regress/t/t_unroll_double_param.v b/test_regress/t/t_unroll_nested_param.v similarity index 100% rename from test_regress/t/t_unroll_double_param.v rename to test_regress/t/t_unroll_nested_param.v From 92a4bff0476c65e377bc02cb3477ed5f221c0231 Mon Sep 17 00:00:00 2001 From: github action Date: Sun, 27 Jul 2025 14:31:15 +0000 Subject: [PATCH 092/252] Apply 'make format' --- src/V3Task.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 314ea0986..e7cfc5357 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1486,7 +1486,7 @@ class TaskVisitor final : public VNVisitor { ++m_statInlines; } - if (VN_IS(nodep, New)) { // New not legal as while() condition + if (VN_IS(nodep, New)) { // New not legal as while() condition insertBeforeStmt(nodep, beginp); UASSERT_OBJ(cnewp, nodep, "didn't create cnew for new"); nodep->replaceWith(cnewp); From bd2cb989d19ed917438dd5b5b5deac987050f65c Mon Sep 17 00:00:00 2001 From: Paul Swirhun Date: Sun, 27 Jul 2025 15:29:56 -0400 Subject: [PATCH 093/252] Support bit queue streaming (#5830) (#6103). --- Changes | 1 + include/verilated_funcs.h | 207 ++++++++++- src/V3Ast.cpp | 3 + src/V3AstNodeExpr.h | 31 ++ src/V3AstNodes.cpp | 14 + src/V3Const.cpp | 126 ++++++- src/V3EmitCFunc.h | 33 ++ src/V3Width.cpp | 34 +- test_regress/t/t_queue_concat_assign.v | 42 ++- test_regress/t/t_stream_bad.out | 20 +- test_regress/t/t_stream_bad.v | 10 +- test_regress/t/t_stream_bitqueue.py | 18 + test_regress/t/t_stream_bitqueue.v | 490 +++++++++++++++++++++++++ test_regress/t/t_stream_crc_example.py | 18 + test_regress/t/t_stream_crc_example.v | 106 ++++++ test_regress/t/t_stream_unpack.v | 4 +- 16 files changed, 1110 insertions(+), 47 deletions(-) create mode 100755 test_regress/t/t_stream_bitqueue.py create mode 100644 test_regress/t/t_stream_bitqueue.v create mode 100755 test_regress/t/t_stream_crc_example.py create mode 100644 test_regress/t/t_stream_crc_example.v diff --git a/Changes b/Changes index 361ad3b5c..4846be015 100644 --- a/Changes +++ b/Changes @@ -21,6 +21,7 @@ Verilator 5.039 devel * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] +* Support bit queue streaming (#5830) (#6103). [Paul Swirhun] * Support Verilog real ports as SystemC double ports (#6136) (#6158). [George Polack] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 2bcb7f5fc..f8c5ef0a7 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2232,6 +2232,169 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn return owp; } +template +static inline VlQueue VL_CLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +template +static inline VlQueue VL_REVCLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +// Helper function to get a bit from a queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue& queue, int srcElementBits, size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const T element = queue.at(elemIdx); + if (srcElementBits == 1) { + return element & 1; + } else { + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + return (element >> actualBitPos) & 1; + } +} + +// Helper function to set a bit in the destination queue +template +static inline void VL_SET_QUEUE_BIT(VlQueue& queue, int dstElementBits, size_t bitIndex, + bool value) { + if (dstElementBits == 1) { + if (VL_UNLIKELY(bitIndex >= queue.size())) return; + queue.atWrite(bitIndex) = value ? 1 : 0; + } else { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + if (value) { + queue.atWrite(elemIdx) |= (static_cast(1) << actualBitPos); + } else { + queue.atWrite(elemIdx) &= ~(static_cast(1) << actualBitPos); + } + } +} + +// Helper function to get a bit from a VlWide queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue>& queue, int srcElementBits, + size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const VlWide& element = queue.at(elemIdx); + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + + return VL_BITISSET_W(element.data(), actualBitPos); +} + +// Helper function to set a bit in a VlWide queue at a specific bit index +template +static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElementBits, + size_t bitIndex, bool value) { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + + VlWide& element = queue.atWrite(elemIdx); + if (value) { + VL_ASSIGNBIT_WO(actualBitPos, element.data()); + } else { + VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0); + } +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) { + elem = 0; +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) { + for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + if (srcElementBits == dstElementBits) { + // Simple case: same element bit width, direct copy of each element + if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op + q = from; + } else { + // Different element bit widths: use streaming conversion + VlQueue srcCopy = from; + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } + for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) { + VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex)); + } + } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, + int srcElementBits, int dstElementBits) { + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + + // Always make a copy to handle the case where q and from are the same queue + VlQueue srcCopy = from; + + // Initialize all elements to zero using appropriate method + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); + + if (lbits == 1) { + // Simple bit reversal: write directly to destination + for (int i = srcTotalBits - 1; i >= 0; --i) { + VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i)); + } + } else { + // Generalized block-reversal for lbits > 1: + // 1. Reverse all bits using 1-bit blocks + // 2. Split into lbits-sized blocks and pad incomplete blocks on the left + // 3. Reverse each lbits-sized block using 1-bit blocks + const size_t numCompleteBlocks = srcTotalBits / lbits; + const size_t remainderBits = srcTotalBits % lbits; + const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0); + + size_t dstBitIndex = 0; + + for (size_t block = 0; block < srcBlocks; ++block) { + const size_t blockStart = block * lbits; + const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits; + for (int bit = bitsToProcess - 1; bit >= 0; --bit) { + const size_t reversedBitIndex = blockStart + bit; + const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex; + VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex)); + } + dstBitIndex += lbits - bitsToProcess; + } + } +} + //====================================================================== // Expressions needing insert/select @@ -2239,49 +2402,49 @@ static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const IData mask = VL_MASK_I(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue& q, QData from) { const size_t size = (rbits + lbits - 1) / lbits; q.renew(size); const QData mask = VL_MASK_Q(lbits); - for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask; + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; } static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { @@ -2289,7 +2452,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2298,7 +2465,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2307,7 +2478,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const IData mask = VL_MASK_I(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2316,7 +2491,11 @@ static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue& q, WData q.renew(size); const QData mask = VL_MASK_Q(lbits); for (size_t i = 0; i < size; ++i) { - q.atWrite(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask; + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask; } } @@ -2326,7 +2505,11 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue> const int size = (rbits + lbits - 1) / lbits; q.renew(size); for (size_t i = 0; i < size; ++i) { - VL_SEL_WWII(lbits, rbits, q.atWrite(i), rwp, i * lbits, lbits); + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth); } } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 995a06020..b0d8f7cfe 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1613,6 +1613,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) return VCastable::ENUM_IMPLICIT; if (fromNumericable) return VCastable::ENUM_EXPLICIT; + } else if (VN_IS(toDtp, QueueDType) + && (VN_IS(fromDtp, BasicDType) || VN_IS(fromDtp, StreamDType))) { + return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { if (fromConstp->isNull()) return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 9fc16f9ed..0047e2845 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1129,6 +1129,37 @@ public: string emitC() final override { V3ERROR_NA_RETURN(""); } bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } }; +class AstCvtArrayToArray final : public AstNodeExpr { + // Copy/Cast from dynamic/unpacked types to dynamic/unpacked types + // @astgen op1 := fromp : AstNodeExpr +private: + const bool m_reverse; // whether ordering gets reversed in this operation (ex: {<<{expr}}) + const int m_blockSize; // num bits per block in a streaming operation (ex: 4 in {<<4{expr}})) + const int m_dstElementBits; // num bits in lhs (ex: 8 if to byte-queue, 1 if to bit-queue) + const int m_srcElementBits; // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue) + +public: + AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse, + int blockSize, int dstElementBits, int srcElementBits) + : ASTGEN_SUPER_CvtArrayToArray(fl) + , m_reverse{reverse} + , m_blockSize{blockSize} + , m_dstElementBits{dstElementBits} + , m_srcElementBits{srcElementBits} { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtArrayToArray; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } + bool reverse() const { return m_reverse; } + int blockSize() const { return m_blockSize; } + int dstElementBits() const { return m_dstElementBits; } + int srcElementBits() const { return m_srcElementBits; } +}; class AstCvtArrayToPacked final : public AstNodeExpr { // Cast from dynamic queue data type to packed array // @astgen op1 := fromp : AstNodeExpr diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 68901b507..b02358d5d 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1696,6 +1696,20 @@ void AstCCast::dumpJson(std::ostream& str) const { dumpJsonNumFunc(str, size); dumpJsonGen(str); } +void AstCvtArrayToArray::dump(std::ostream& str) const { + this->AstNodeExpr::dump(str); + str << " reverse=" << reverse(); + str << " blockSize=" << blockSize(); + str << " dstElementBits=" << dstElementBits(); + str << " srcElementBits=" << srcElementBits(); +} +void AstCvtArrayToArray::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, reverse); + dumpJsonNumFunc(str, blockSize); + dumpJsonNumFunc(str, dstElementBits); + dumpJsonNumFunc(str, srcElementBits); + dumpJsonGen(str); +} void AstCell::dump(std::ostream& str) const { this->AstNode::dump(str); if (recursive()) str << " [RECURSIVE]"; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index c8d54a8c9..058bf0493 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2205,8 +2205,23 @@ class ConstVisitor final : public VNVisitor { AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack(); AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack(); AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { - srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; + if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + srcp = new AstCvtArrayToArray{ + srcp->fileline(), srcp, nodep->dtypep(), false, 1, + dstElementBits, srcElementBits}; + } else { + srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()}; + } } else if (VN_IS(srcDTypep, UnpackArrayDType)) { srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep}; // Handling the case where lhs is wider than rhs by inserting zeros. StreamL does @@ -2289,15 +2304,50 @@ class ConstVisitor final : public VNVisitor { // Further reduce, any of the nodes may have more reductions. return true; } else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) { - AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefp(); AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL); - AstNodeExpr* const srcp = streamp->lhsp(); - const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); - if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) - || VN_IS(srcDTypep, UnpackArrayDType)) { - streamp->lhsp( - new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), lhsDtypep}); - streamp->dtypeFrom(lhsDtypep); + AstNodeExpr* srcp = streamp->lhsp(); + AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); + if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) + || VN_IS(srcDTypep, UnpackArrayDType))) { + if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { + int blockSize = 1; + if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { + blockSize = constp->toSInt(); + if (VL_UNLIKELY(blockSize <= 0)) { + // Not reachable due to higher level checks when parsing stream + // operators commented out to not fail v3error-coverage-checks. + // nodep->v3error("Stream block size must be positive, got " << + // blockSize); nodep->v3error("Stream block size must be positive, got + // " << blockSize); + blockSize = 1; + } + } + // Not reachable due to higher level checks when parsing stream operators + // commented out to not fail v3error-coverage-checks. + // else { + // nodep->v3error("Stream block size must be constant (got " << + // streamp->rhsp()->prettyTypeName() << ")"); + // } + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + streamp->unlinkFrBack(); + srcp = new AstCvtArrayToArray{ + srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, true, + blockSize, dstElementBits, srcElementBits}; + nodep->rhsp(srcp); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + } else { + streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), + dstDTypep}); + streamp->dtypeFrom(dstDTypep); + } } } else if (m_doV && replaceAssignMultiSel(nodep)) { return true; @@ -3083,6 +3133,64 @@ class ConstVisitor final : public VNVisitor { varrefp->varp()->valuep(initvaluep); } } + void visit(AstCvtArrayToArray* nodep) override { + iterateChildren(nodep); + // Handle the case where we have a stream operation inside a cast conversion + // To avoid infinite recursion, mark the node as processed by setting user1. + if (!nodep->user1()) { + nodep->user1(true); + // Check for both StreamL and StreamR operations + AstNodeStream* streamp = nullptr; + bool isReverse = false; + if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) { + streamp = streamLp; + isReverse = true; // StreamL reverses the operation + } else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) { + streamp = streamRp; + isReverse = false; // StreamR doesn't reverse the operation + } + if (streamp) { + AstNodeExpr* srcp = streamp->lhsp(); + AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp(); + if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) { + int blockSize = 1; + if (AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { + blockSize = constp->toSInt(); + if (VL_UNLIKELY(blockSize <= 0)) { + // Not reachable due to higher level checks when parsing stream + // operators commented out to not fail v3error-coverage-checks. + // nodep->v3error("Stream block size must be positive, got " << + // blockSize); + blockSize = 1; + } + } + // Not reachable due to higher level checks when parsing stream operators + // commented out to not fail v3error-coverage-checks. + // else { + // nodep->v3error("Stream block size must be constant (got " << + // streamp->rhsp()->prettyTypeName() << ")"); + // } + int srcElementBits = 0; + if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + streamp->unlinkFrBack(); + AstNodeExpr* newp = new AstCvtArrayToArray{ + srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse, + blockSize, dstElementBits, srcElementBits}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } + } + } void visit(AstRelease* nodep) override { if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) { FileLine* const flp = nodep->fileline(); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index c5e5c1efb..e489ae07c 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -426,6 +426,22 @@ public: emitVarDecl(nodep); } + void visit(AstCvtArrayToArray* nodep) override { + if (nodep->reverse()) { + puts("VL_REVCLONE_Q("); + } else { + puts("VL_CLONE_Q("); + } + iterateAndNextConstNull(nodep->fromp()); + puts(", "); + puts(cvtToStr(nodep->blockSize())); + puts(", "); + puts(cvtToStr(nodep->srcElementBits())); + puts(", "); + puts(cvtToStr(nodep->dstElementBits())); + puts(")"); + } + void visit(AstCvtArrayToPacked* nodep) override { AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); AstNodeDType* const elemDtp = fromDtp->subDTypep()->skipRefp(); @@ -514,6 +530,23 @@ public: puts(", "); rhs = false; iterateAndNextConstNull(castp->fromp()); + } else if (const AstCvtArrayToArray* const castp + = VN_CAST(nodep->rhsp(), CvtArrayToArray)) { + if (castp->reverse()) { + putns(castp, "VL_REVCOPY_Q("); + } else { + putns(castp, "VL_COPY_Q("); + } + iterateAndNextConstNull(nodep->lhsp()); + puts(", "); + rhs = false; + iterateAndNextConstNull(castp->fromp()); + puts(", "); + puts(cvtToStr(castp->blockSize())); + puts(", "); + puts(cvtToStr(castp->srcElementBits())); + puts(", "); + puts(cvtToStr(castp->dstElementBits())); } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) // && !VN_IS(nodep->rhsp(), CExpr) // && !VN_IS(nodep->rhsp(), CMethodHard) // diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5edaa1cd6..8a146cdb6 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1598,6 +1598,12 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); // Type set in constructor } + void visit(AstCvtArrayToArray* nodep) override { + if (nodep->didWidthAndSet()) return; + // Opaque returns, so arbitrary + userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); + // Type set in constructor + } void visit(AstCvtArrayToPacked* nodep) override { if (nodep->didWidthAndSet()) return; // Opaque returns, so arbitrary @@ -2201,6 +2207,28 @@ class WidthVisitor final : public VNVisitor { // Can just remove cast, but need extend placeholder // so we can avoid warning message } + } else if (VN_IS(toDtp, QueueDType)) { + if (VN_IS(fromDtp, BasicDType)) { + newp = new AstCvtPackedToArray{nodep->fileline(), + nodep->fromp()->unlinkFrBack(), toDtp}; + } else if (VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, StreamDType)) { + int srcElementBits = 1; + int dstElementBits = 1; + if (AstNodeDType* const elemDtp = fromDtp->subDTypep()) { + srcElementBits = elemDtp->width(); + } + const AstQueueDType* const dstQueueDtp = VN_AS(toDtp, QueueDType); + if (AstNodeDType* const elemDtp = dstQueueDtp->subDTypep()) { + dstElementBits = elemDtp->width(); + } + newp = new AstCvtArrayToArray{nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + toDtp, + false, + 1, + dstElementBits, + srcElementBits}; + } } else if (VN_IS(toDtp, ClassRefDType)) { // Can just remove cast } else { @@ -4914,11 +4942,13 @@ class WidthVisitor final : public VNVisitor { patp = VN_AS(patp->nextp(), PatMember)) { patp->dtypep(arrayp->subDTypep()); AstNodeExpr* const rhsp = patternMemberValueIterate(patp); + const bool rhsIsDirect + = AstNode::computeCastable(rhsp->dtypep(), arrayp, nullptr).isAssignable(); const bool rhsIsValue = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr) .isAssignable(); - AstConsQueue* const newap - = new AstConsQueue{nodep->fileline(), rhsIsValue, rhsp, false, newp}; + AstConsQueue* const newap = new AstConsQueue{ + nodep->fileline(), !rhsIsDirect && rhsIsValue, rhsp, false, newp}; newap->dtypeFrom(arrayp); newp = newap; } diff --git a/test_regress/t/t_queue_concat_assign.v b/test_regress/t/t_queue_concat_assign.v index b3860878e..3e4478420 100644 --- a/test_regress/t/t_queue_concat_assign.v +++ b/test_regress/t/t_queue_concat_assign.v @@ -4,20 +4,36 @@ // any use, without warranty, 2024 by Antmicro. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/); - initial begin - bit q1[$] = {1'b1}; - bit q2[$]; - bit [1:0] d1[] = {2'b10}; - bit [1:0] d2[]; - q2 = {q1}; - if (q2[0] != 1) $stop; +module t ( /*AUTOARG*/); - d2 = {2'b11}; - if (d2[0] != 2'b11) $stop; + initial begin + bit q1[$] = {1'b1}; + bit q2[$]; + bit q3[$]; - $write("*-* All Finished *-*\n"); - $finish; - end + bit [1:0] d1[$] = {2'b10}; + bit [1:0] d2[$]; + bit [1:0] d3[$]; + + q2 = {q1}; // consCC + if (q2.size != 1) $stop; + if (q2[0] != 1) $stop; + + q3 = q1; + if (q3.size != 1) $stop; + if (q3[0] != 1) $stop; + + if (d1[0] != 2'b10) $stop; + + d2 = {2'b11}; + if (d2[0] != 2'b11) $stop; + + d3 = {d1, d2}; + if (d3[0] != 2'b10) $stop; + if (d3[1] != 2'b11) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index ad2c9c4c4..acf856f50 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -1,10 +1,18 @@ -%Error: t/t_stream_bad.v:12:32: Expecting expression to be constant, but can't convert a RAND to constant. +%Error: t/t_stream_bad.v:14:25: Expecting expression to be constant, but can't convert a RAND to constant. : ... note: In instance 't' - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^~~~~~~ + 14 | packed_data_32 = {<<$random{byte_in}}; + | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_stream_bad.v:12:30: Slice size isn't a constant or basic data type. +%Error: t/t_stream_bad.v:14:23: Slice size isn't a constant or basic data type. : ... note: In instance 't' - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^~ + 14 | packed_data_32 = {<<$random{byte_in}}; + | ^~ +%Error: t/t_stream_bad.v:15:25: Expecting expression to be constant, but variable isn't const: 'x' + : ... note: In instance 't' + 15 | packed_data_32 = {<>{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_q = bit_q_t'({<<{4'hc}}); + `checkh(bit_q[0], 1'b0); + `checkh(bit_q[1], 1'b0); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b1); + + bit_q = {>>{bit_q_t'(4'he)}}; + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b0); + + bit_q = {<<{bit_q_t'(4'hd)}}; + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b0); + `checkh(bit_q[2], 1'b1); + `checkh(bit_q[3], 1'b1); + + bit_qq = {>>{bit_q}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b0); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b1); + + bit_qq = {<<{bit_q}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_q = bit_q_t'({>>{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_q = bit_q_t'({>>2{4'hd}}); + `checkh(bit_q[0], 1'b1); + `checkh(bit_q[1], 1'b1); + `checkh(bit_q[2], 1'b0); + `checkh(bit_q[3], 1'b1); + + bit_qq = bit_q_t'({>>{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_qq = bit_q_t'({>>2{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b0); + `checkh(bit_qq[3], 1'b1); + + bit_qq = bit_q_t'({<<{bit_q}}); + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b0); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b1); + + bit_qq = {<<2{bit_qq}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b0); + + bit_qq = {<<2{bit_q_t'({<<{bit_q}})}}; + `checkh(bit_qq[0], 1'b1); + `checkh(bit_qq[1], 1'b1); + `checkh(bit_qq[2], 1'b1); + `checkh(bit_qq[3], 1'b0); + end + + begin + cdata_q_t cdata_q, cdata_qq; + + cdata_q = cdata_q_t'(32'hdeadbeef); + `checkh(cdata_q[0], 8'hde); + `checkh(cdata_q[1], 8'had); + `checkh(cdata_q[2], 8'hbe); + `checkh(cdata_q[3], 8'hef); + + cdata_qq = cdata_q_t'({<<{cdata_q}}); + `checkh(cdata_qq[0], 8'hf7); + `checkh(cdata_qq[1], 8'h7d); + `checkh(cdata_qq[2], 8'hb5); + `checkh(cdata_qq[3], 8'h7b); + + cdata_qq = {<<2{cdata_q}}; + `checkh(cdata_qq[0], 8'hfb); + `checkh(cdata_qq[1], 8'hbe); + `checkh(cdata_qq[2], 8'h7a); + `checkh(cdata_qq[3], 8'hb7); + end + + begin + sdata_logic_q_t sdata_q, sdata_qq; + + sdata_q = sdata_logic_q_t'(64'hfeedface_deadbeef); + `checkh(sdata_q[0], 16'hfeed); + `checkh(sdata_q[1], 16'hface); + `checkh(sdata_q[2], 16'hdead); + `checkh(sdata_q[3], 16'hbeef); + + sdata_qq = sdata_logic_q_t'({<<{sdata_q}}); + `checkh(sdata_qq[0], 16'hf77d); + `checkh(sdata_qq[1], 16'hb57b); + `checkh(sdata_qq[2], 16'h735f); + `checkh(sdata_qq[3], 16'hb77f); + + sdata_qq = {<<2{sdata_q}}; + `checkh(sdata_qq[0], 16'hfbbe); + `checkh(sdata_qq[1], 16'h7ab7); + `checkh(sdata_qq[2], 16'hb3af); + `checkh(sdata_qq[3], 16'h7bbf); + end + + begin + idata_logic_q_t idata_q, idata_qq; + + idata_q = idata_logic_q_t'(64'h12345678_9abcdef0); + `checkh(idata_q[0], 32'h12345678); + `checkh(idata_q[1], 32'h9abcdef0); + + idata_qq = idata_logic_q_t'({<<{idata_q}}); + `checkh(idata_qq[0], 32'h0f7b3d59); + `checkh(idata_qq[1], 32'h1e6a2c48); + + idata_q = idata_logic_q_t'(128'hfeedface_deadbeef_cafebabe_12345678); + `checkh(idata_q[0], 32'hfeedface); + `checkh(idata_q[1], 32'hdeadbeef); + `checkh(idata_q[2], 32'hcafebabe); + `checkh(idata_q[3], 32'h12345678); + + idata_qq = {<<2{idata_logic_q_t'({<<{idata_q}})}}; + `checkh(idata_qq[0], 32'hfddef5cd); + `checkh(idata_qq[1], 32'hed5e7ddf); + `checkh(idata_qq[2], 32'hc5fd757d); + `checkh(idata_qq[3], 32'h2138a9b4); + end + + begin + qdata_logic_q_t qdata_q, qdata_qq; + + qdata_q.push_back(64'hdeadbeef_cafebabe); + qdata_q.push_back(64'hfeedface_12345678); + `checkh(qdata_q[0], 64'hdeadbeef_cafebabe); + `checkh(qdata_q[1], 64'hfeedface_12345678); + + qdata_qq = qdata_logic_q_t'({<<{qdata_q}}); + `checkh(qdata_qq[0], 64'h1e6a2c48735fb77f); + `checkh(qdata_qq[1], 64'h7d5d7f53f77db57b); + + qdata_q.push_back(64'h1111222233334444); + qdata_q.push_back(64'h5555666677778888); + qdata_qq = {<<2{qdata_q}}; + `checkh(qdata_qq[0], 64'h2222dddd99995555); + `checkh(qdata_qq[1], 64'h1111cccc88884444); + `checkh(qdata_qq[2], 64'h2d951c84b3af7bbf); + `checkh(qdata_qq[3], 64'hbeaebfa3fbbe7ab7); + end + + begin + wide_q_t wide_q, wide_qq; + + wide_q.push_back(128'hdeadbeef_cafebabe_feedface_12345678); + wide_q.push_back(128'h11112222_33334444_55556666_77778888); + `checkh(wide_q[0], 128'hdeadbeef_cafebabe_feedface_12345678); + `checkh(wide_q[1], 128'h11112222_33334444_55556666_77778888); + + wide_qq = wide_q_t'({<<{wide_q}}); + `checkh(wide_qq[0], 128'h1111eeee6666aaaa2222cccc44448888); + `checkh(wide_qq[1], 128'h1e6a2c48735fb77f7d5d7f53f77db57b); + + wide_q.push_back(128'haaaabbbb_ccccdddd_eeeeffff_00001111); + wide_q.push_back(128'h22223333_44445555_66667777_88889999); + + wide_qq = wide_q_t'({<<{wide_q}}); + wide_qq = {<<2{wide_q}}; + `checkh(wide_qq[0], 128'h66662222dddd999955551111cccc8888); + `checkh(wide_qq[1], 128'h44440000ffffbbbb77773333eeeeaaaa); + `checkh(wide_qq[2], 128'h2222dddd999955551111cccc88884444); + `checkh(wide_qq[3], 128'h2d951c84b3af7bbfbeaebfa3fbbe7ab7); + end + + begin + byte_q_t bytq_init; + byte_q_t bytq; + bit_q_t bitq; + + bytq_init.push_back(8'h84); + bytq_init.push_back(8'haa); + `checkh(bytq_init[0], 8'h84); + `checkh(bytq_init[1], 8'haa); + s = $sformatf("bytq_init=%p", bytq_init); + `checks(s, "bytq_init='{'h84, 'haa} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa} "); + + /* + Generalized block-reversal semantics for the outer left-stream when blockSize > 1. + This seemingly complicated approach is what is required to match commercial simulators, + otherwise the straggler bit [1] in the padded byte might end up as 0x01 instead of 0x80. + + Starting with result of inner {<<{bitq}}: [1,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,0] (17 bits), + apply outer {<<8{...}} using generalized block-reversal like this: + - Reverse all bits: [0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1] + - Split into 8-bit blocks from left and pad incomplete blocks on the left: + - Block 0: [0,0,1,0,0,0,0,1] (complete) + - Block 1: [0,1,0,1,0,1,0,1] (complete) + - Block 2: [1] -> pad on left -> [0,0,0,0,0,0,0,1] + - Reverse bits within each 8-bit block: + - Block 0: [0,0,1,0,0,0,0,1] -> [1,0,0,0,0,1,0,0] = 0x84 + - Block 1: [0,1,0,1,0,1,0,1] -> [1,0,1,0,1,0,1,0] = 0xaa + - Block 2: [0,0,0,0,0,0,0,1] -> [1,0,0,0,0,0,0,0] = 0x80 + */ + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1} "); + `checkh(bytq[0], 8'h84); + `checkh(bytq[1], 8'haa); + `checkh(bytq[2], 8'h80); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h80} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1} "); + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'hc0} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'he0} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h70} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'hb8} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h5c} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h2e} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h97} "); + + bytq = bytq_init; + bitq = {<<8{bit_q_t'({<<{bytq}})}}; + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b0); + bitq.push_back(1'b0); + bitq.push_back(1'b1); + bitq.push_back(1'b1); + bytq = {<<8{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h84, 'haa, 'h97, 'h80} "); + end + + // Test StreamR (>>) operations - fairly simple since this should maintain left-to-right order. + begin + bit_q_t bitq; + byte_q_t bytq; + + bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; + bitq = {>>4{bit_q_t'({<<{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + + bytq = {8'h84, 8'haa}; + bitq = {>>{bit_q_t'({<<{bytq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1} "); + + bitq = { + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b1, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + 1'b1, + 1'b0 + }; + bytq = {>>2{byte_q_t'({<<{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h43, 'h55} "); + + bytq = {8'h12, 8'h34, 8'h56}; + bytq = {>>{byte_q_t'({<<{bytq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h6a, 'h2c, 'h48} "); + + bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; + bitq = {>>6{bit_q_t'({>>{bitq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + + bytq = {8'h84, 8'haa}; + bitq = {>>{bit_q_t'({>>{bytq}})}}; + s = $sformatf("bitq=%p", bitq); + `checks(s, + "bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + + bitq = { + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b0, + 1'b1, + 1'b1, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + 1'b1, + 1'b0 + }; + bytq = {>>8{byte_q_t'({>>{bitq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'haa, 'hc2} "); + + bytq = {8'h12, 8'h34, 8'h56}; + bytq = {>>{byte_q_t'({>>{bytq}})}}; + s = $sformatf("bytq=%p", bytq); + `checks(s, "bytq='{'h12, 'h34, 'h56} "); + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_stream_crc_example.py b/test_regress/t/t_stream_crc_example.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_stream_crc_example.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_stream_crc_example.v b/test_regress/t/t_stream_crc_example.v new file mode 100644 index 000000000..7f09a08d4 --- /dev/null +++ b/test_regress/t/t_stream_crc_example.v @@ -0,0 +1,106 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`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); +`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); +// verilog_format: on + +typedef bit bit_q_t[$]; + +module t ( /*AUTOARG*/ + // Inputs + clk +); + input clk; + integer cyc = 0; + reg [63:0] crc = '0; + reg [63:0] sum = '0; + + // Take CRC data and apply to testblock inputs + wire [31:0] in = crc[31:0]; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [31:0] out; // From test of Test.v + // End of automatics + + Test test ( + .clk, + .in, + .out + ); + + // Aggregate outputs into a single result vector + wire [63:0] result = {32'h0, out}; + + initial begin + byte unsigned p[$]; + byte unsigned po[$]; + bit bits[$]; + string s; + + p = {8'h84, 8'haa}; + `checkh(p[0], 8'h84); + `checkh(p[1], 8'haa); + + bits = {<<8{bit_q_t'({<<{p}})}}; + bits.push_front(1'b0); + po = {<<8{bit_q_t'({<<{bits}})}}; + + s = $sformatf("p=%p", p); + `checks(s, "p='{'h84, 'haa} "); + + s = $sformatf("bits=%p", bits); + `checks(s, + "bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + + s = $sformatf("po=%p", po); + `checks(s, "po='{'h8, 'h55, 'h80} "); + end + + always_ff @(posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x sum=%x\n", $time, cyc, crc, result, sum); +`endif + + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + + if (cyc == 0) begin + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end else if (cyc < 10) begin + sum <= '0; + end else if (cyc == 99) begin + `checkh(crc, 64'hc77bb9b3784ea091); + `checkh(sum, 64'h9721d4e989defb24); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module Test ( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + byte unsigned p[$]; + byte unsigned po[$]; + bit bits[$]; + + always_ff @(posedge clk) begin + p = {in[31:24], in[23:16], in[15:8], in[7:0]}; + bits = {<<8{bit_q_t'({<<{p}})}}; + bits.push_front(1'b0); + po = {<<8{bit_q_t'({<<{bits}})}}; + out <= {po[3], po[2], po[1], po[0]}; + end +endmodule diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v index b83ea9174..ee6514123 100644 --- a/test_regress/t/t_stream_unpack.v +++ b/test_regress/t/t_stream_unpack.v @@ -280,8 +280,8 @@ module t (/*AUTOARG*/); o = {<<128{p}}; `checkh(o, 256'habcd0123456789abfadecafedeadbeeffadecafedeadbeefabcd0123456789ab); {>>{p}} = o; - `checkh(p[0], 128'hfadecafedeadbeefabcd0123456789ab); - `checkh(p[1], 128'habcd0123456789abfadecafedeadbeef); + `checkh(p[0], 128'habcd0123456789abfadecafedeadbeef); + `checkh(p[1], 128'hfadecafedeadbeefabcd0123456789ab); $write("*-* All Finished *-*\n"); $finish; From 7c71bdf2d68574965904d3d5d2440b8095bea513 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 15:55:59 -0400 Subject: [PATCH 094/252] Commentary --- docs/guide/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/install.rst b/docs/guide/install.rst index 9e1783b57..e7438b289 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -153,7 +153,7 @@ need to be present to run Verilator: sudo apt-get install git autoconf flex bison -Those developing Verilator itself may also want these (see internals.rst): +Those developing Verilator itself also need these (see internals.rst): .. code-block:: shell From 64a82508f2a397989fabfc18be0121bb5ff578c6 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 16:10:19 -0400 Subject: [PATCH 095/252] Fix error on `with` to give UNSUPPORTED (#6134) --- src/V3Width.cpp | 11 ++++++++--- test_regress/t/t_array_method_unsup.out | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8a146cdb6..fbe4dce0b 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3399,11 +3399,16 @@ class WidthVisitor final : public VNVisitor { } return nullptr; } - void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) { + void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg, + bool withUnsup = false) { int narg = 0; for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { if (VN_IS(argp, With)) { - argp->v3error("'with' not legal on this method"); + if (withUnsup) { + argp->v3warn(E_UNSUPPORTED, "Unsupported: 'with' on this method"); + } else { + argp->v3error("'with' not legal on this method"); + } // Delete all arguments as nextp() otherwise dangling VL_DO_DANGLING(pushDeletep(argp->unlinkFrBackWithNext()), argp); break; @@ -4266,7 +4271,7 @@ class WidthVisitor final : public VNVisitor { } if (methodId) { - methodOkArguments(nodep, 0, 0); + methodOkArguments(nodep, 0, 0, true /*withUnsup*/); FileLine* const fl = nodep->fileline(); AstNodeExpr* newp = nullptr; for (int i = 0; i < adtypep->elementsConst(); ++i) { diff --git a/test_regress/t/t_array_method_unsup.out b/test_regress/t/t_array_method_unsup.out index 46f00ef4d..9a5d614e1 100644 --- a/test_regress/t/t_array_method_unsup.out +++ b/test_regress/t/t_array_method_unsup.out @@ -1,22 +1,22 @@ -%Error: t/t_array_method_unsup.v:23:17: 'with' not legal on this method - : ... note: In instance 't' +%Error-UNSUPPORTED: t/t_array_method_unsup.v:23:17: Unsupported: 'with' on this method + : ... note: In instance 't' 23 | i = q.sum with (item + 1); do if ((i) !== (32'h11)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",23, (i), (32'h11)); $stop; end while(0);; | ^~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_array_method_unsup.v:24:21: 'with' not legal on this method - : ... note: In instance 't' + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_array_method_unsup.v:24:21: Unsupported: 'with' on this method + : ... note: In instance 't' 24 | i = q.product with (item + 1); do if ((i) !== (32'h168)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",24, (i), (32'h168)); $stop; end while(0);; | ^~~~ -%Error: t/t_array_method_unsup.v:27:17: 'with' not legal on this method - : ... note: In instance 't' +%Error-UNSUPPORTED: t/t_array_method_unsup.v:27:17: Unsupported: 'with' on this method + : ... note: In instance 't' 27 | i = q.and with (item + 1); do if ((i) !== (32'b1001)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",27, (i), (32'b1001)); $stop; end while(0);; | ^~~~ -%Error: t/t_array_method_unsup.v:28:16: 'with' not legal on this method - : ... note: In instance 't' +%Error-UNSUPPORTED: t/t_array_method_unsup.v:28:16: Unsupported: 'with' on this method + : ... note: In instance 't' 28 | i = q.or with (item + 1); do if ((i) !== (32'b1111)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",28, (i), (32'b1111)); $stop; end while(0);; | ^~~~ -%Error: t/t_array_method_unsup.v:29:17: 'with' not legal on this method - : ... note: In instance 't' +%Error-UNSUPPORTED: t/t_array_method_unsup.v:29:17: Unsupported: 'with' on this method + : ... note: In instance 't' 29 | i = q.xor with (item + 1); do if ((i) !== (32'hb)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",29, (i), (32'hb)); $stop; end while(0);; | ^~~~ %Error: Exiting due to From 47c5b220b338542d2a66780434290996be094905 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Jul 2025 17:13:56 -0400 Subject: [PATCH 096/252] Support non-ansi ports with `wire` before `input` --- src/V3AstNodeOther.h | 12 +----------- src/V3AstNodes.cpp | 15 ++++++++++++++- test_regress/t/t_inst_nansi.py | 18 ++++++++++++++++++ test_regress/t/t_inst_nansi.v | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) create mode 100755 test_regress/t/t_inst_nansi.py create mode 100644 test_regress/t/t_inst_nansi.v diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index f8d12c686..235ac2e5e 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2315,17 +2315,7 @@ public: declDirection(fromp->declDirection()); lifetime(fromp->lifetime()); } - void combineType(const AstVar* typevarp) { - // This is same as typevarp (for combining input & reg decls) - // "this" is the input var. typevarp is the reg var. - propagateAttrFrom(typevarp); - combineType(typevarp->varType()); - if (typevarp->isSigPublic()) sigPublic(true); - if (typevarp->isSigModPublic()) sigModPublic(true); - if (typevarp->isSigUserRdPublic()) sigUserRdPublic(true); - if (typevarp->isSigUserRWPublic()) sigUserRWPublic(true); - if (typevarp->attrScClocked()) attrScClocked(true); - } + void combineType(const AstVar* otherp); void inlineAttrReset(const string& name) { if (direction() == VDirection::INOUT && varType() == VVarType::WIRE) { m_varType = VVarType::TRIWIRE; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b02358d5d..caf0d1331 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -489,7 +489,20 @@ bool AstVar::isScBigUint() const { return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv()); } - +void AstVar::combineType(const AstVar* otherp) { + // "this" is the port var. otherp is the reg var, or vice-versa + propagateAttrFrom(otherp); + combineType(otherp->varType()); + if (otherp->isSigPublic()) sigPublic(true); + if (otherp->isSigModPublic()) sigModPublic(true); + if (otherp->isSigUserRdPublic()) sigUserRdPublic(true); + if (otherp->isSigUserRWPublic()) sigUserRWPublic(true); + if (otherp->attrScClocked()) attrScClocked(true); + if (otherp->varType() == VVarType::PORT) { + varType(otherp->varType()); + direction(otherp->direction()); + } +} void AstVar::combineType(VVarType type) { // These flags get combined with the existing settings of the flags. // We don't test varType for certain types, instead set flags since diff --git a/test_regress/t/t_inst_nansi.py b/test_regress/t/t_inst_nansi.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_inst_nansi.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_inst_nansi.v b/test_regress/t/t_inst_nansi.v new file mode 100644 index 000000000..651df6f41 --- /dev/null +++ b/test_regress/t/t_inst_nansi.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 + +module t(b, si, i, li, w3, w4); + output b; // Output before type + output si; + byte b; + shortint si; + + int i; + longint li; + output i; // Output after type + output li; + + input w3; + wire [2:0] w3; + wire [3:0] w4; + input w4; + + initial begin + if ($bits(b) != 8) $stop; + if ($bits(si) != 16) $stop; + if ($bits(i) != 32) $stop; + if ($bits(li) != 64) $stop; + if ($bits(w3) != 3) $stop; + if ($bits(w4) != 4) $stop; + $finish; + end + +endmodule From e69df457fde67f853fefcac8992eebf2e1e53831 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 01:45:03 -0400 Subject: [PATCH 097/252] Fix MODDUP with duplicate packages to take first package (#6222). --- Changes | 1 + src/V3LinkCells.cpp | 12 ++++-------- test_regress/t/t_package_dup_bad.out | 6 +++--- test_regress/t/t_package_dup_bad2.out | 23 +++++++++++++++++++++++ test_regress/t/t_package_dup_bad2.py | 16 ++++++++++++++++ test_regress/t/t_package_dup_bad2.v | 20 ++++++++++++++++++++ 6 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 test_regress/t/t_package_dup_bad2.out create mode 100755 test_regress/t/t_package_dup_bad2.py create mode 100644 test_regress/t/t_package_dup_bad2.v diff --git a/Changes b/Changes index 4846be015..b0265165d 100644 --- a/Changes +++ b/Changes @@ -52,6 +52,7 @@ Verilator 5.039 devel * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] * Fix 'driver same component' assertion (#6211) (#6215). [Geza Lore] * Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] +* Fix MODDUP with duplicate packages to take first package (#6222). * Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 0c2bb15b4..9d4f9a448 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -628,20 +628,16 @@ class LinkCellsVisitor final : public VNVisitor { if (!(libFoundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP) || hierBlocks.find(nodep->name()) != hierBlocks.end())) { - nodep->v3warn(MODDUP, "Duplicate declaration of module: " + nodep->v3warn(MODDUP, "Duplicate declaration of " + << nodep->verilogKwd() << ": " << nodep->prettyNameQ() << '\n' << nodep->warnContextPrimary() << '\n' << libFoundp->warnOther() << "... Location of original declaration\n" << libFoundp->warnContextSecondary()); } - if (VN_IS(nodep, Package)) { - // Packages may be imported, we instead rename to be unique - nodep->name(nodep->name() + "__Vdedup" + cvtToStr(m_dedupNum++)); - } else { - nodep->unlinkFrBack(); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - } + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (!libFoundp && globalFoundp && globalFoundp != nodep) { // ...__LIB__ stripped by prettyName const string newName = nodep->libname() + "__LIB__" + nodep->origName(); diff --git a/test_regress/t/t_package_dup_bad.out b/test_regress/t/t_package_dup_bad.out index 7cc93102e..916b8bf19 100644 --- a/test_regress/t/t_package_dup_bad.out +++ b/test_regress/t/t_package_dup_bad.out @@ -1,4 +1,4 @@ -%Warning-MODDUP: t/t_package_dup_bad.v:11:9: Duplicate declaration of module: 'pkg' +%Warning-MODDUP: t/t_package_dup_bad.v:11:9: Duplicate declaration of package: 'pkg' 11 | package pkg; | ^~~ t/t_package_dup_bad.v:7:9: ... Location of original declaration @@ -6,13 +6,13 @@ | ^~~ ... For warning description see https://verilator.org/warn/MODDUP?v=latest ... Use "/* verilator lint_off MODDUP */" and lint_on around source to disable this message. -%Warning-MODDUP: t/t_package_dup_bad.v:19:9: Duplicate declaration of module: 'pkg' +%Warning-MODDUP: t/t_package_dup_bad.v:19:9: Duplicate declaration of package: 'pkg' 19 | package pkg; | ^~~ t/t_package_dup_bad.v:7:9: ... Location of original declaration 7 | package pkg; | ^~~ -%Warning-MODDUP: t/t_package_dup_bad.v:22:9: Duplicate declaration of module: 'pkg' +%Warning-MODDUP: t/t_package_dup_bad.v:22:9: Duplicate declaration of package: 'pkg' 22 | package pkg; | ^~~ t/t_package_dup_bad.v:7:9: ... Location of original declaration diff --git a/test_regress/t/t_package_dup_bad2.out b/test_regress/t/t_package_dup_bad2.out new file mode 100644 index 000000000..599412e6f --- /dev/null +++ b/test_regress/t/t_package_dup_bad2.out @@ -0,0 +1,23 @@ +%Warning-MODDUP: t/t_package_dup_bad2.v:19:9: Duplicate declaration of package: 'Pkg' + 19 | package Pkg; + | ^~~ + t/t_package_dup_bad2.v:7:9: ... Location of original declaration + 7 | package Pkg; + | ^~~ + ... For warning description see https://verilator.org/warn/MODDUP?v=latest + ... Use "/* verilator lint_off MODDUP */" and lint_on around source to disable this message. +%Error-MODMISSING: t/t_package_dup_bad2.v:11:3: Cannot find file containing module: 'IOBUF' + 11 | IOBUF iocell ( + | ^~~~~ + ... For error description see https://verilator.org/warn/MODMISSING?v=latest + ... Looked in: + t/IOBUF + t/IOBUF.v + t/IOBUF.sv + IOBUF + IOBUF.v + IOBUF.sv + obj_vlt/t_package_dup_bad2/IOBUF + obj_vlt/t_package_dup_bad2/IOBUF.v + obj_vlt/t_package_dup_bad2/IOBUF.sv +%Error: Exiting due to diff --git a/test_regress/t/t_package_dup_bad2.py b/test_regress/t/t_package_dup_bad2.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_package_dup_bad2.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_package_dup_bad2.v b/test_regress/t/t_package_dup_bad2.v new file mode 100644 index 000000000..122723714 --- /dev/null +++ b/test_regress/t/t_package_dup_bad2.v @@ -0,0 +1,20 @@ +// 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 + +package Pkg; +endpackage + +module t; + IOBUF iocell ( + .O (in), + .IO(pad), + .I ('0), + .T (~oe) + ); +endmodule + +package Pkg; +endpackage From 5c1d7f3ce95e7b242e8f7d6f2b0f68f1b34a9754 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 02:20:57 -0400 Subject: [PATCH 098/252] Fix negate of wide structure selections (#6186). --- Changes | 3 ++- src/V3Cast.cpp | 9 +++++++-- test_regress/t/t_struct_negate.py | 18 ++++++++++++++++++ test_regress/t/t_struct_negate.v | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_struct_negate.py create mode 100644 test_regress/t/t_struct_negate.v diff --git a/Changes b/Changes index b0265165d..dbc9eb50d 100644 --- a/Changes +++ b/Changes @@ -47,9 +47,10 @@ Verilator 5.039 devel * Fix virtual interface member propagation (#6175) (#6184). [Yilou Wang] * Fix `--coverage-expr` null pointer dereference (#6181). [Igor Zaworski, Antmicro Ltd.] * Fix conflicting function/class name linking error (#6182). [Igor Zaworski, Antmicro Ltd.] -* Fix automatic task variables in unrolled loops with forks (#6194) (#6201). [Danny Oler] +* Fix negate of wide structure selections (#6186). * Fix VPI signal range order (#6189) (#6200). [Ibrahim Burak Yorulmaz] * Fix structure select causing 'Wide Op' error (#6191). [Danny Oler] +* Fix automatic task variables in unrolled loops with forks (#6194) (#6201). [Danny Oler] * Fix 'driver same component' assertion (#6211) (#6215). [Geza Lore] * Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] * Fix MODDUP with duplicate packages to take first package (#6222). diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 1cdd43a4d..9795de230 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -173,13 +173,13 @@ class CastVisitor final : public VNVisitor { void visit(AstNegate* nodep) override { iterateChildren(nodep); nodep->user1(nodep->lhsp()->user1()); - if (nodep->lhsp()->widthMin() == 1) { + if (nodep->lhsp()->widthMin() == 1 && !nodep->lhsp()->isWide()) { // We want to avoid a GCC "converting of negative value" warning // from our expansion of // out = {32{a out = - (alhsp(), castSize(nodep)); } else { - ensureCast(nodep->lhsp()); + if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp()); } } void visit(AstVarRef* nodep) override { @@ -221,6 +221,11 @@ class CastVisitor final : public VNVisitor { void visit(AstMemberSel* nodep) override { iterateChildren(nodep); ensureNullChecked(nodep->fromp()); + nodep->user1(true); + } + void visit(AstStructSel* nodep) override { + iterateChildren(nodep); + nodep->user1(true); } // NOPs diff --git a/test_regress/t/t_struct_negate.py b/test_regress/t/t_struct_negate.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_struct_negate.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_struct_negate.v b/test_regress/t/t_struct_negate.v new file mode 100644 index 000000000..6803ef09a --- /dev/null +++ b/test_regress/t/t_struct_negate.v @@ -0,0 +1,19 @@ +// 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 ( + input logic signed [64:0] i_x, + output logic signed [64:0] o_y +); + struct {logic signed [64:0] m_x;} s; + assign s.m_x = i_x; + assign o_y = -s.m_x; + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 0bd291e6cd8d47b2cb8be4a54badf4b4143e3ce1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 02:27:55 -0400 Subject: [PATCH 099/252] Fix unused variable compile error (#6222 partial) --- src/V3LinkCells.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 9d4f9a448..381eb75a7 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -112,7 +112,6 @@ class LinkCellsVisitor final : public VNVisitor { VSymGraph m_mods; // Symbol table of all module names LinkCellsGraph m_graph; // Linked graph of all cell interconnects LibraryVertex* m_libVertexp = nullptr; // Vertex at root of all libraries - int m_dedupNum = 0; // Package dedup number const V3GraphVertex* m_topVertexp = nullptr; // Vertex of top module std::unordered_set m_declfnWarned; // Files we issued DECLFILENAME on string m_origTopModuleName; // original name of the top module From a5b0a0d9dd3a19eea4469a74fb17970873901f27 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 28 Jul 2025 16:14:03 +0200 Subject: [PATCH 100/252] Add support for `$countones` in constraints (#6144) (#6235) --- src/V3AstNodeExpr.h | 2 +- src/V3Randomize.cpp | 38 ++++++++++++++++- test_regress/t/t_constraint_countones.py | 21 ++++++++++ test_regress/t/t_constraint_countones.v | 53 ++++++++++++++++++++++++ test_regress/t/t_constraint_unsup.out | 5 +++ test_regress/t/t_constraint_unsup.py | 16 +++++++ test_regress/t/t_constraint_unsup.v | 19 +++++++++ 7 files changed, 151 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_constraint_countones.py create mode 100644 test_regress/t/t_constraint_countones.v create mode 100644 test_regress/t/t_constraint_unsup.out create mode 100755 test_regress/t/t_constraint_unsup.py create mode 100644 test_regress/t/t_constraint_unsup.v diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 0047e2845..851f9e7f4 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -56,7 +56,7 @@ public: virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV // For documentation on emitC format see EmitCFunc::emitOpName virtual string emitC() = 0; - virtual string emitSMT() const { V3ERROR_NA_RETURN(""); }; + virtual string emitSMT() const { return ""; }; virtual string emitSimpleOperator() { return ""; } // "" means not ok to use virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS virtual bool cleanOut() const = 0; // True if output has extra upper bits zero diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 30581654a..decadd40e 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -532,8 +532,10 @@ class ConstraintExprVisitor final : public VNVisitor { AstNodeExpr* thsp = nullptr) { // Replace incomputable (result-dependent) expression with SMT expression std::string smtExpr = nodep->emitSMT(); // Might need child width (AstExtend) - UASSERT_OBJ(smtExpr != "", nodep, - "Node needs randomization constraint, but no emitSMT: " << nodep); + if (smtExpr == "") { + nodep->v3warn(E_UNSUPPORTED, "Unsupported expression inside constraint"); + return; + } if (lhsp) lhsp = VN_AS(iterateSubtreeReturnEdits(lhsp->backp() ? lhsp->unlinkFrBack() : lhsp), @@ -718,6 +720,38 @@ class ConstraintExprVisitor final : public VNVisitor { initTaskp->addStmtsp(methodp->makeStmt()); } } + void visit(AstCountOnes* nodep) override { + // Convert it to (x & 1) + ((x & 2) >> 1) + ((x & 4) >> 2) + ... + FileLine* const fl = nodep->fileline(); + AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack(); + V3Number numOne{nodep, argp->width(), 1}; + AstNodeExpr* sump = new AstAnd{fl, argp, new AstConst{fl, numOne}}; + sump->user1(true); + for (int i = 1; i < argp->width(); i++) { + V3Number numBitMask{nodep, argp->width(), 0}; + numBitMask.setBit(i, 1); + AstAnd* const andp + = new AstAnd{fl, argp->cloneTreePure(false), new AstConst{fl, numBitMask}}; + andp->user1(true); + AstShiftR* const shiftp = new AstShiftR{ + fl, andp, new AstConst{fl, AstConst::WidthedValue{}, argp->width(), (uint32_t)i}}; + shiftp->user1(true); + shiftp->dtypeFrom(nodep); + sump = new AstAdd{nodep->fileline(), sump, shiftp}; + sump->user1(true); + } + // Restore the original width + if (nodep->width() > sump->width()) { + sump = new AstExtend{fl, sump, nodep->width()}; + sump->user1(true); + } else if (nodep->width() < sump->width()) { + sump = new AstSel{fl, sump, 0, nodep->width()}; + sump->user1(true); + } + nodep->replaceWith(sump); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + iterate(sump); + } void visit(AstNodeBiop* nodep) override { if (editFormat(nodep)) return; editSMT(nodep, nodep->lhsp(), nodep->rhsp()); diff --git a/test_regress/t/t_constraint_countones.py b/test_regress/t/t_constraint_countones.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_countones.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_countones.v b/test_regress/t/t_constraint_countones.v new file mode 100644 index 000000000..86bf60772 --- /dev/null +++ b/test_regress/t/t_constraint_countones.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field, cond) \ +begin \ + longint prev_result; \ + int ok = 0; \ + if (!bit'(cl.randomize())) $stop; \ + prev_result = longint'(field); \ + if (!(cond)) $stop; \ + repeat(9) begin \ + longint result; \ + if (!bit'(cl.randomize())) $stop; \ + result = longint'(field); \ + if (!(cond)) $stop; \ + if (result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class Rand1; + rand bit [4:0] x; + constraint c {$countones(x) == 1;} +endclass + +class Rand2; + rand bit [5:0] x; + rand bit [2:0] y; + constraint c {10'b1 + 10'($countones(x + 6'(y))) == 3;} +endclass + +class Rand3; + rand bit [32:0] x; + constraint c {$countones(x) == 1;} +endclass + +module t ( /*AUTOARG*/); + Rand1 r1 = new; + Rand2 r2 = new; + Rand3 r3 = new; + initial begin + `check_rand(r1, r1.x, $countones(r1.x) == 1); + `check_rand(r2, r2.x, 10'b1 + 10'($countones(r2.x + 6'(r2.y))) == 3); + `check_rand(r3, r3.x, $countones(r3.x) == 1); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_unsup.out b/test_regress/t/t_constraint_unsup.out new file mode 100644 index 000000000..2708313a4 --- /dev/null +++ b/test_regress/t/t_constraint_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_constraint_unsup.v:9:22: Unsupported expression inside constraint + 9 | constraint cons { $onehot(m_one) == 1; } + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_unsup.py b/test_regress/t/t_constraint_unsup.py new file mode 100755 index 000000000..6585af685 --- /dev/null +++ b/test_regress/t/t_constraint_unsup.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=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_unsup.v b/test_regress/t/t_constraint_unsup.v new file mode 100644 index 000000000..8369075bb --- /dev/null +++ b/test_regress/t/t_constraint_unsup.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int m_one; + constraint cons { $onehot(m_one) == 1; } +endclass + +module t; + Packet p; + + initial begin + p = new; + void'(p.randomize()); + end +endmodule From 833c31b031c56881b53a46c49e7d651132b0245c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 18:01:50 -0400 Subject: [PATCH 101/252] Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. --- Changes | 1 + docs/guide/exe_verilator.rst | 4 +++- include/verilated.mk.in | 1 + test_regress/Makefile_obj | 3 --- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changes b/Changes index dbc9eb50d..d9e6ad37c 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.039 devel * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base data type, and wire data type checking per IEEE. * Add error on missing forward declarations (#6206). [Alex Solomatnikov] +* Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 3ed785b6d..1a1c3d802 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -32,7 +32,9 @@ Summary: makefile's VPATH the appropriate directory to find the file. See also :vlopt:`-CFLAGS` and :vlopt:`-LDFLAGS` options, which are - useful when the C++ files need special compiler flags. + useful when the C++ files need special compiler flags. The compiler + flags add by default `-DVERILATOR=1`, so an `#ifdef VERILATOR` may be + used to conditionally preprocess .cpp code for different simulators. .. option:: diff --git a/include/verilated.mk.in b/include/verilated.mk.in index a7a1f021c..fe1b081e1 100644 --- a/include/verilated.mk.in +++ b/include/verilated.mk.in @@ -84,6 +84,7 @@ VK_CPPFLAGS_ALWAYS += \ -MMD \ -I$(VERILATOR_ROOT)/include \ -I$(VERILATOR_ROOT)/include/vltstd \ + -DVERILATOR=1 \ -DVM_COVERAGE=$(VM_COVERAGE) \ -DVM_SC=$(VM_SC) \ -DVM_TIMING=$(VM_TIMING) \ diff --git a/test_regress/Makefile_obj b/test_regress/Makefile_obj index 81620d2f1..ac40f9146 100644 --- a/test_regress/Makefile_obj +++ b/test_regress/Makefile_obj @@ -22,9 +22,6 @@ VPATH += ../.. ####################################################################### -# Needed by DPI tests -CPPFLAGS += -DVERILATOR=1 - # Debugging CPPFLAGS += -DVL_DEBUG=1 # Assertions disabled as SystemC libraries are not clean From 2ff6ee96137b3ec8bb856250fe838c976f4cc614 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 18:03:21 -0400 Subject: [PATCH 102/252] Support vpi_handle_by_name with `$root.`. --- include/verilated_vpi.cpp | 5 ++++- test_regress/t/t_vpi_package.cpp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index a244b9e48..a08dea624 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -2059,7 +2059,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { const VerilatedScope* scopep; const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope); std::string scopeAndName = namep; - if (voScopep) { + if (0 == std::strncmp(namep, "$root.", std::strlen("$root."))) { + namep += std::strlen("$root."); + scopeAndName = namep; + } else if (voScopep) { const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr; scopeAndName = std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + namep; namep = const_cast(scopeAndName.c_str()); diff --git a/test_regress/t/t_vpi_package.cpp b/test_regress/t/t_vpi_package.cpp index c2ba1eb52..c36b28f1e 100644 --- a/test_regress/t/t_vpi_package.cpp +++ b/test_regress/t/t_vpi_package.cpp @@ -120,6 +120,7 @@ int mon_check() { CHECK_RESULT_Z(check_handle(const_cast("someOtherInt"), tHandle)) CHECK_RESULT_Z(check_handle(const_cast("t.someOtherInt"), NULL)) + CHECK_RESULT_Z(check_handle(const_cast("$root.t.someOtherInt"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("someString"), tHandle)) CHECK_RESULT_Z(check_handle(const_cast("t.someString"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("someInt"), pkgHandle)) From 0da9f6eb03b622375b150802fb78571438830055 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Jul 2025 19:36:52 -0400 Subject: [PATCH 103/252] Fix queue typedef with unbounded slice (#6236). --- Changes | 1 + src/V3Width.cpp | 4 ++-- test_regress/t/t_queue_slice.v | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Changes b/Changes index d9e6ad37c..695df2cce 100644 --- a/Changes +++ b/Changes @@ -56,6 +56,7 @@ Verilator 5.039 devel * Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] * Fix MODDUP with duplicate packages to take first package (#6222). * Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] +* Fix queue typedef with unbounded slice (#6236). Verilator 5.038 2025-07-08 diff --git a/src/V3Width.cpp b/src/V3Width.cpp index fbe4dce0b..ba22aa1cd 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1469,10 +1469,10 @@ class WidthVisitor final : public VNVisitor { AstNode* backp = nodep->backp(); if (VN_IS(backp, Sub)) backp = backp->backp(); if (const auto* const selp = VN_CAST(backp, SelExtract)) { - if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; + if (VN_IS(selp->fromp()->dtypep()->skipRefp(), QueueDType)) return; } if (const auto* const selp = VN_CAST(backp, SelBit)) { - if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; + if (VN_IS(selp->fromp()->dtypep()->skipRefp(), QueueDType)) return; } // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); diff --git a/test_regress/t/t_queue_slice.v b/test_regress/t/t_queue_slice.v index db1440f17..d7eec445a 100644 --- a/test_regress/t/t_queue_slice.v +++ b/test_regress/t/t_queue_slice.v @@ -11,7 +11,8 @@ module t (/*AUTOARG*/); initial begin - string q[$]; + typedef string q_t[$]; + q_t q; string v; int i; int qi[$:5]; From 2b62f5a625c84e66985f58b7e997c0e15b60ab23 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Wed, 30 Jul 2025 09:48:02 +0200 Subject: [PATCH 104/252] Add error when trying to assign class object to variable of non-class types (#6237) --- src/V3Width.cpp | 21 +++++++----- .../t/t_class_to_basic_assignment_bad.out | 5 +++ .../t/t_class_to_basic_assignment_bad.py | 16 ++++++++++ .../t/t_class_to_basic_assignment_bad.v | 32 +++++++++++++++++++ test_regress/t/t_process_copy_constr.v | 2 +- 5 files changed, 67 insertions(+), 9 deletions(-) create mode 100755 test_regress/t/t_class_to_basic_assignment_bad.out create mode 100755 test_regress/t/t_class_to_basic_assignment_bad.py create mode 100644 test_regress/t/t_class_to_basic_assignment_bad.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ba22aa1cd..a9bff2d59 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -7329,21 +7329,26 @@ class WidthVisitor final : public VNVisitor { } return false; } - void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp, + void checkClassAssign(const AstNode* nodep, const char* side, AstNode* rhsp, AstNodeDType* const lhsDTypep) { - if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep->skipRefp(), ClassRefDType)) { - UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type"); - AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp(); - if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) { + UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type"); + const AstNodeDType* const lhsRawDTypep = lhsDTypep->skipRefp(); + const AstNodeDType* const rhsRawDTypep = rhsp->dtypep()->skipRefp(); + if (const AstClassRefDType* const lhsClassRefp = VN_CAST(lhsRawDTypep, ClassRefDType)) { + if (const AstClassRefDType* const rhsClassRefp + = VN_CAST(rhsRawDTypep, ClassRefDType)) { if (isBaseClassRecurse(lhsClassRefp->classp(), rhsClassRefp->classp())) return; } else if (rhsp->isNull()) { return; } nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got " - << rhsDtypep->prettyTypeName()); + << rhsRawDTypep->prettyTypeName()); + } else if (VN_IS(rhsRawDTypep, ClassRefDType)) { + nodep->v3error(side << " " << rhsRawDTypep->prettyDTypeNameQ() + << " cannot be assigned to non-class " + << lhsDTypep->prettyDTypeNameQ()); } - if (VN_IS(lhsDTypep->skipRefp(), DynArrayDType) - && VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType)) { + if (VN_IS(lhsRawDTypep, DynArrayDType) && VN_IS(rhsRawDTypep, UnpackArrayDType)) { VNRelinker relinker; rhsp->unlinkFrBack(&relinker); relinker.relink( diff --git a/test_regress/t/t_class_to_basic_assignment_bad.out b/test_regress/t/t_class_to_basic_assignment_bad.out new file mode 100755 index 000000000..a1b195381 --- /dev/null +++ b/test_regress/t/t_class_to_basic_assignment_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_class_to_basic_assignment_bad.v:26:29: Assign RHS 'class{}Foo' cannot be assigned to non-class 'int' + 26 | new_node.phase_done = get(); + | ^ + ... 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_class_to_basic_assignment_bad.py b/test_regress/t/t_class_to_basic_assignment_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_class_to_basic_assignment_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_to_basic_assignment_bad.v b/test_regress/t/t_class_to_basic_assignment_bad.v new file mode 100644 index 000000000..e0f020cce --- /dev/null +++ b/test_regress/t/t_class_to_basic_assignment_bad.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; + int phase_done; + + static function Foo get(); + Foo ans = new; + return ans; + endfunction + + static function int create (); + return 3; + endfunction + + function string get_name (); + return "bar"; + endfunction + + function void add(Foo phase); + Foo new_node; + if (new_node.get_name() == "run") begin + new_node.phase_done = get(); + end + else begin + new_node.phase_done = create(); + end + endfunction +endclass diff --git a/test_regress/t/t_process_copy_constr.v b/test_regress/t/t_process_copy_constr.v index 13f57775e..3bbadf5c2 100644 --- a/test_regress/t/t_process_copy_constr.v +++ b/test_regress/t/t_process_copy_constr.v @@ -7,7 +7,7 @@ class Cls; int x = 1; function new(); - int p = process::self(); + process p = process::self(); endfunction endclass From 9d2adf3e49f9e6174fd31b9863c406d3d3d2af4f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 30 Jul 2025 12:41:21 +0100 Subject: [PATCH 105/252] Internals: Enable EmitV to output formatted code to std::ostream (#6239) Introduce V3OutStream as a V3OutFormatter that writes to a stream instead of a file. This can be used to emit formatted code fragments e.g. in debug prints and graph dumps. --- src/V3EmitV.cpp | 63 +++++++++++++++++++++++++++---------------------- src/V3File.cpp | 17 +++++++++---- src/V3File.h | 29 +++++++++++++++++++---- 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index be6addce6..e9121517b 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -28,12 +28,13 @@ VL_DEFINE_DEBUG_FUNCTIONS; // ###################################################################### // Emit statements and expressions -class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { +class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { // STATE - across all visitors - const bool m_suppressUnknown = false; + const bool m_alwaysTrackText; // Always track all NodeSimpleText + const bool m_suppressUnknown; // Do not error on unknown node // STATE - for current visit position (use VL_RESTORER) - AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE + AstSenTree* m_sensesp = nullptr; // Domain for printing one a ALWAYS under a ACTIVE bool m_suppressSemi = false; // Non-statement, don't print ; bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_arrayPost = false; // Print array information that goes after identifier (vs after) @@ -72,7 +73,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { // VISITORS void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNodeModule* nodep) override { - putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n"); + putfs(nodep, nodep->verilogKwd() + " " + EmitCBase::prefixNameProtect(nodep) + ";\n"); iterateChildrenConst(nodep); putqs(nodep, "end" + nodep->verilogKwd() + "\n"); } @@ -461,7 +462,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(";\n"); } void visit(AstNodeSimpleText* nodep) override { - if (nodep->tracking() || m_trackText) { + if (nodep->tracking() || m_alwaysTrackText) { puts(nodep->text()); } else { putsNoTracking(nodep->text()); @@ -965,9 +966,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } public: - explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) - : m_suppressUnknown{suppressUnknown} - , m_sensesp{domainp} {} + explicit EmitVBaseVisitorConst(bool alwaysTrackText, bool suppressUnknown) + : m_alwaysTrackText{alwaysTrackText} + , m_suppressUnknown{suppressUnknown} {} ~EmitVBaseVisitorConst() override = default; }; @@ -975,18 +976,19 @@ public: // Emit to an output file class EmitVFileVisitor final : public EmitVBaseVisitorConst { + // STATE + V3OutVFile& m_of; // The output file // METHODS - void puts(const string& str) override { ofp()->puts(str); } - void putbs(const string& str) override { ofp()->putbs(str); } + void putsNoTracking(const string& str) override { m_of.putsNoTracking(str); } + void puts(const string& str) override { m_of.puts(str); } + void putbs(const string& str) override { m_of.putbs(str); } void putfs(AstNode*, const string& str) override { putbs(str); } void putqs(AstNode*, const string& str) override { putbs(str); } - void putsNoTracking(const string& str) override { ofp()->putsNoTracking(str); } public: - EmitVFileVisitor(AstNode* nodep, V3OutVFile* ofp, bool trackText, bool suppressUnknown) - : EmitVBaseVisitorConst{suppressUnknown, nullptr} { - m_ofp = ofp; - m_trackText = trackText; + EmitVFileVisitor(AstNode* nodep, V3OutVFile& of, bool alwaysTrackText, bool suppressUnknown) + : EmitVBaseVisitorConst{alwaysTrackText, suppressUnknown} + , m_of{of} { iterateConst(nodep); } ~EmitVFileVisitor() override = default; @@ -996,19 +998,25 @@ public: // Emit to a stream (perhaps stringstream) class EmitVStreamVisitor final : public EmitVBaseVisitorConst { - // MEMBERS - std::ostream& m_os; + // STATE + V3OutStream m_os; // The output stream formatter + bool m_tracking; // Use line tracking // METHODS - void putsNoTracking(const string& str) override { m_os << str; } - void puts(const string& str) override { putsNoTracking(str); } - void putbs(const string& str) override { puts(str); } + void putsNoTracking(const string& str) override { m_os.putsNoTracking(str); } + void puts(const string& str) override { + m_tracking ? m_os.puts(str) : m_os.putsNoTracking(str); + } + void putbs(const string& str) override { + m_tracking ? m_os.putbs(str) : m_os.putsNoTracking(str); + } void putfs(AstNode*, const string& str) override { putbs(str); } void putqs(AstNode*, const string& str) override { putbs(str); } public: - EmitVStreamVisitor(const AstNode* nodep, std::ostream& os) - : EmitVBaseVisitorConst{false, nullptr} - , m_os(os) { // Need () or GCC 4.8 false warning + EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking) + : EmitVBaseVisitorConst{false, false} + , m_os{os, V3OutFormatter::LA_VERILOG} + , m_tracking{tracking} { iterateConst(const_cast(nodep)); } ~EmitVStreamVisitor() override = default; @@ -1018,7 +1026,7 @@ public: // EmitV class functions void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) { - { EmitVStreamVisitor{nodep, os}; } + { EmitVStreamVisitor{nodep, os, /* tracking: */ false}; } } void V3EmitV::emitvFiles() { @@ -1028,9 +1036,8 @@ void V3EmitV::emitvFiles() { AstVFile* const vfilep = VN_CAST(filep, VFile); if (vfilep && vfilep->tblockp()) { V3OutVFile of{vfilep->name()}; - of.puts("// DESCR" - "IPTION: Verilator generated Verilog\n"); - { EmitVFileVisitor{vfilep->tblockp(), &of, true, false}; } + of.puts("// DESCRIPTION: Verilator generated Verilog\n"); + { EmitVFileVisitor{vfilep->tblockp(), of, true, false}; } } } } @@ -1038,5 +1045,5 @@ void V3EmitV::emitvFiles() { void V3EmitV::debugEmitV(const string& filename) { UINFO(2, __FUNCTION__ << ":"); V3OutVFile of{filename}; - { EmitVFileVisitor{v3Global.rootp(), &of, true, true}; } + { EmitVFileVisitor{v3Global.rootp(), of, true, true}; } } diff --git a/src/V3File.cpp b/src/V3File.cpp index 10b3232d6..d2c7cf973 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -640,11 +640,10 @@ bool VInFilter::readWholefile(const string& filename, VInFilter::StrList& outl) } //###################################################################### -// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. +// V3OutFormatter: A class for printing code with automatic indentation. -V3OutFormatter::V3OutFormatter(const string& filename, V3OutFormatter::Language lang) - : m_filename{filename} - , m_lang{lang} { +V3OutFormatter::V3OutFormatter(V3OutFormatter::Language lang) + : m_lang{lang} { m_blockIndent = v3Global.opt.decoration() ? 4 : 1; m_commaWidth = v3Global.opt.decoration() ? 50 : 150; } @@ -962,7 +961,8 @@ void V3OutFormatter::printf(const char* fmt...) { // V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang) - : V3OutFormatter{filename, lang} + : V3OutFormatter{lang} + , m_filename{filename} , m_bufferp{new std::array{}} { if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) { v3fatal("Can't write file: " << filename); @@ -992,6 +992,13 @@ void V3OutCFile::putsGuard() { puts("#define " + var + " // guard\n"); } +//###################################################################### +// V3OutStream + +V3OutStream::V3OutStream(std::ostream& ostream, V3OutFormatter::Language lang) + : V3OutFormatter{lang} + , m_ostream{ostream} {} + //###################################################################### // VIdProtect diff --git a/src/V3File.h b/src/V3File.h index 5c75ed1a5..009b658e7 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -102,7 +102,7 @@ public: }; //============================================================================ -// V3OutFormatter: A class for automatic indentation of C++ or Verilog code. +// V3OutFormatter: A class for automatic indentation of output code. class V3OutFormatter VL_NOT_FINAL { // TYPES @@ -113,7 +113,6 @@ public: private: // MEMBERS - const string m_filename; const Language m_lang; // Indenting Verilog code int m_blockIndent; // Characters per block indent int m_commaWidth; // Width after which to break at ,'s @@ -132,10 +131,9 @@ private: void putcNoTracking(char chr); public: - V3OutFormatter(const string& filename, Language lang); + V3OutFormatter(Language lang); virtual ~V3OutFormatter() = default; // ACCESSORS - string filename() const { return m_filename; } int column() const { return m_column; } int blockIndent() const { return m_blockIndent; } void blockIndent(int flag) { m_blockIndent = flag; } @@ -165,7 +163,7 @@ public: void indentInc() { m_indentLevel += m_blockIndent; } void indentDec() { m_indentLevel -= m_blockIndent; - UASSERT(m_indentLevel >= 0, ": " << m_filename << ": Underflow of indentation"); + UASSERT(m_indentLevel >= 0, "Underflow of indentation"); } void blockInc() { m_parenVec.push(m_indentLevel + m_blockIndent); } void blockDec() { @@ -201,6 +199,7 @@ class V3OutFile VL_NOT_FINAL : public V3OutFormatter { static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024; // MEMBERS + const std::string m_filename; FILE* m_fp = nullptr; std::size_t m_usedBytes = 0; // Number of bytes stored in m_bufferp std::size_t m_writtenBytes = 0; // Number of bytes written to output @@ -214,6 +213,8 @@ public: V3OutFile& operator=(V3OutFile&&) = delete; ~V3OutFile() override; + std::string filename() const { return m_filename; } + void putsForceIncs(); void statRecordWritten() { @@ -430,6 +431,24 @@ public: virtual void putsHeader() { puts("\n"); } }; +//============================================================================ +// V3OutStream: A class for printing formatted code to any std::ostream + +class V3OutStream VL_NOT_FINAL : public V3OutFormatter { + // MEMBERS + std::ostream& m_ostream; + + VL_UNCOPYABLE(V3OutStream); + VL_UNMOVABLE(V3OutStream); + +public: + V3OutStream(std::ostream& ostream, V3OutFormatter::Language lang); + ~V3OutStream() override = default; + + void putcOutput(char chr) override { m_ostream << chr; }; + void putsOutput(const char* str) override { m_ostream << str; }; +}; + //============================================================================ // VIdProtect: Hash identifier names in output files to protect them From 9d38e632693b75507ea496b3de1f991afaa84185 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Jul 2025 09:14:13 -0400 Subject: [PATCH 106/252] Remove error on missing forward declarations of typedef in class (#6207). --- src/V3AstNodeOther.h | 6 +++++- src/V3AstNodes.cpp | 2 ++ src/V3LinkDot.cpp | 4 +++- test_regress/t/t_dump_json.out | 2 +- test_regress/t/t_json_only_tag.out | 2 +- test_regress/t/t_typedef_fwd_class.py | 18 ++++++++++++++++++ test_regress/t/t_typedef_fwd_class.v | 25 +++++++++++++++++++++++++ 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_typedef_fwd_class.py create mode 100644 test_regress/t/t_typedef_fwd_class.v diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 235ac2e5e..d4a603ed8 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1833,6 +1833,7 @@ class AstTypedef final : public AstNode { bool m_attrPublic = false; bool m_isHideLocal : 1; // Verilog local bool m_isHideProtected : 1; // Verilog protected + bool m_isUnderClass : 1; // Underneath class public: AstTypedef(FileLine* fl, const string& name, AstNode* attrsp, VFlagChildDType, @@ -1841,7 +1842,8 @@ public: , m_name{name} , m_declTokenNum{fl->tokenNum()} , m_isHideLocal{false} - , m_isHideProtected{false} { + , m_isHideProtected{false} + , m_isUnderClass{false} { childDTypep(dtp); // Only for parser addAttrsp(attrsp); dtypep(nullptr); // V3Width will resolve @@ -1868,6 +1870,8 @@ public: void isHideLocal(bool flag) { m_isHideLocal = flag; } bool isHideProtected() const { return m_isHideProtected; } void isHideProtected(bool flag) { m_isHideProtected = flag; } + bool isUnderClass() const { return m_isUnderClass; } + void isUnderClass(bool flag) { m_isUnderClass = flag; } void tag(const string& text) override { m_tag = text; } string tag() const override { return m_tag; } }; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index caf0d1331..d2184cab0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2169,6 +2169,7 @@ void AstTimeImport::dumpJson(std::ostream& str) const { void AstTypedef::dump(std::ostream& str) const { this->AstNode::dump(str); if (attrPublic()) str << " [PUBLIC]"; + if (isUnderClass()) str << " [UNDCLS]"; if (subDTypep()) { str << " -> "; subDTypep()->dump(str); @@ -2177,6 +2178,7 @@ void AstTypedef::dump(std::ostream& str) const { void AstTypedef::dumpJson(std::ostream& str) const { // dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file dumpJsonBoolFunc(str, attrPublic); + dumpJsonBoolFunc(str, isUnderClass); dumpJsonGen(str); } void AstTypedefFwd::dump(std::ostream& str) const { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 585c9416c..59182ca99 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1564,6 +1564,7 @@ class LinkDotFindVisitor final : public VNVisitor { } void visit(AstTypedef* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Typedef not under module/package/$unit"); + if (VN_IS(m_classOrPackagep, Class)) nodep->isUnderClass(true); iterateChildren(nodep); m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); } @@ -4580,7 +4581,8 @@ class LinkDotResolveVisitor final : public VNVisitor { if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) { // Don't check if typedef is to a :: as might not be resolved // yet - if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); + if (!nodep->classOrPackagep() && !defp->isUnderClass()) + checkDeclOrder(nodep, defp); nodep->typedefp(defp); nodep->classOrPackagep(foundp->classOrPackagep()); } else if (AstParamTypeDType* const defp diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 0e44b1848..7f456ecf7 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -865,7 +865,7 @@ ],"extendsp": []}, {"type":"CLASS","name":"process","addr":"(SN)","loc":"d,124:4,124:9","isExtended":false,"isInterfaceClass":false,"isVirtual":false,"origName":"process","level":5,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","classOrPackagep":"UNLINKED","inlinesp": [], "stmtsp": [ - {"type":"TYPEDEF","name":"state","addr":"(TN)","loc":"d,131:9,131:14","dtypep":"UNLINKED","attrPublic":false, + {"type":"TYPEDEF","name":"state","addr":"(TN)","loc":"d,131:9,131:14","dtypep":"UNLINKED","attrPublic":false,"isUnderClass":false, "childDTypep": [ {"type":"DEFIMPLICITDTYPE","name":"__typeimpenum0","addr":"(UN)","loc":"d,125:15,125:19","dtypep":"UNLINKED","generic":false, "childDTypep": [ diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index 18f7938c2..96ab43284 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -5,7 +5,7 @@ {"type":"VAR","name":"clk_ip","addr":"(F)","loc":"d,14:11,14:17","dtypep":"(G)","origName":"clk_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"rst_ip","addr":"(H)","loc":"d,15:11,15:17","dtypep":"(G)","origName":"rst_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"foo_op","addr":"(I)","loc":"d,16:11,16:17","dtypep":"(G)","origName":"foo_op","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"childDTypep": [],"attrsp": []}, + {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"isUnderClass":false,"childDTypep": [],"attrsp": []}, {"type":"CELL","name":"itop","addr":"(L)","loc":"d,29:8,29:12","origName":"itop","recursive":false,"modp":"(M)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, {"type":"VAR","name":"itop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"this_struct","addr":"(P)","loc":"d,31:14,31:25","dtypep":"(Q)","origName":"this_struct","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, diff --git a/test_regress/t/t_typedef_fwd_class.py b/test_regress/t/t_typedef_fwd_class.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_typedef_fwd_class.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_typedef_fwd_class.v b/test_regress/t/t_typedef_fwd_class.v new file mode 100644 index 000000000..67ae2e046 --- /dev/null +++ b/test_regress/t/t_typedef_fwd_class.v @@ -0,0 +1,25 @@ +// 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 + +typedef logic [3:0] T; + +class Cls; + extern static function int f(T x); + // This is after the usage above, but to match other simulators, + // no error about use after declaration + typedef logic [7:0] T; +endclass +function int Cls::f(T x); + return $bits(x); +endfunction + +module t; + initial begin + if (Cls::f('1) != 8) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From d8f1e1113a35bc47b3e4ff39e024b3a77ba3186a Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 31 Jul 2025 12:48:37 +0200 Subject: [PATCH 107/252] Fix error when force assignment is used with ref function args (#6244) --- src/V3Force.cpp | 9 ++++--- test_regress/t/t_force_readwrite.py | 18 +++++++++++++ test_regress/t/t_force_readwrite.v | 25 +++++++++++++++++ test_regress/t/t_force_readwrite_unsup.out | 8 ++++++ test_regress/t/t_force_readwrite_unsup.py | 16 +++++++++++ test_regress/t/t_force_readwrite_unsup.v | 31 ++++++++++++++++++++++ 6 files changed, 103 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_force_readwrite.py create mode 100644 test_regress/t/t_force_readwrite.v create mode 100644 test_regress/t/t_force_readwrite_unsup.out create mode 100755 test_regress/t/t_force_readwrite_unsup.py create mode 100644 test_regress/t/t_force_readwrite_unsup.v diff --git a/src/V3Force.cpp b/src/V3Force.cpp index e22804fae..a09e3288d 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -364,9 +364,6 @@ class ForceReplaceVisitor final : public VNVisitor { // Replace VarRef from forced LHS with rdVscp. if (ForceState::ForceComponentsVarScope* const fcp = m_state.tryGetForceComponents(nodep)) { - FileLine* const flp = nodep->fileline(); - AstVarRef* const origp = new AstVarRef{flp, nodep->varScopep(), VAccess::READ}; - ForceState::markNonReplaceable(origp); nodep->varp(fcp->m_rdVscp->varp()); nodep->varScopep(fcp->m_rdVscp); } @@ -398,7 +395,11 @@ class ForceReplaceVisitor final : public VNVisitor { break; } default: - nodep->v3error("Unsupported: Signals used via read-write reference cannot be forced"); + if (!m_inLogic) return; + if (m_state.tryGetForceComponents(nodep) || ForceState::getValVscp(nodep)) { + nodep->v3error( + "Unsupported: Signals used via read-write reference cannot be forced"); + } break; } } diff --git a/test_regress/t/t_force_readwrite.py b/test_regress/t/t_force_readwrite.py new file mode 100755 index 000000000..4ff4dfaa5 --- /dev/null +++ b/test_regress/t/t_force_readwrite.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('vlt') + +test.compile(verilator_flags2=["--exe", "--main", "--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_readwrite.v b/test_regress/t/t_force_readwrite.v new file mode 100644 index 000000000..202bfb408 --- /dev/null +++ b/test_regress/t/t_force_readwrite.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + task take_ref(ref logic s); + endtask +endclass + +module t; + logic a; + logic b = 1; + logic c; + Cls cls = new; + + initial begin + force a = b; + cls.take_ref(c); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_force_readwrite_unsup.out b/test_regress/t/t_force_readwrite_unsup.out new file mode 100644 index 000000000..97d7ed581 --- /dev/null +++ b/test_regress/t/t_force_readwrite_unsup.out @@ -0,0 +1,8 @@ +%Error: t/t_force_readwrite_unsup.v:25:18: Unsupported: Signals used via read-write reference cannot be forced + 25 | cls.take_ref(a); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_force_readwrite_unsup.v:26:18: Unsupported: Signals used via read-write reference cannot be forced + 26 | cls.take_ref(b); + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_force_readwrite_unsup.py b/test_regress/t/t_force_readwrite_unsup.py new file mode 100755 index 000000000..44c75cc0f --- /dev/null +++ b/test_regress/t/t_force_readwrite_unsup.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('simulator') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_force_readwrite_unsup.v b/test_regress/t/t_force_readwrite_unsup.v new file mode 100644 index 000000000..e3f8074b0 --- /dev/null +++ b/test_regress/t/t_force_readwrite_unsup.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + task take_ref(ref logic s); + endtask +endclass + +module t; + logic a; + logic b = 1; + Cls cls = new; + + initial begin + force a = b; + cls.take_ref(a); + cls.take_ref(b); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 5faaa7ec58fd5ab25449f974482ffa38b9f7ebf9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 31 Jul 2025 18:09:43 -0400 Subject: [PATCH 108/252] Commentary (#6246) --- docs/guide/verilating.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index ab0d0b7d8..1126c275e 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -147,8 +147,6 @@ Hierarchy blocks have some limitations, including: * The hierarchy block cannot be accessed using dot (.) from the upper module(s) or other hierarchy blocks. -* Signals in the block cannot be traced. - * Modport cannot be used at the hierarchical block boundary. * The simulation speed is likely not as fast as flat Verilation, in which From 00948a45a310ec0a450290fb99edba240cab3218 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 31 Jul 2025 18:09:51 -0400 Subject: [PATCH 109/252] Commentary: Changes update --- Changes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changes b/Changes index 695df2cce..f19ed3227 100644 --- a/Changes +++ b/Changes @@ -18,12 +18,14 @@ Verilator 5.039 devel * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base data type, and wire data type checking per IEEE. * Add error on missing forward declarations (#6206). [Alex Solomatnikov] +* Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support bit queue streaming (#5830) (#6103). [Paul Swirhun] * Support Verilog real ports as SystemC double ports (#6136) (#6158). [George Polack] +* Support `$countones` in constraints (#6144 partial) (#6235). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] @@ -57,6 +59,7 @@ Verilator 5.039 devel * Fix MODDUP with duplicate packages to take first package (#6222). * Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] * Fix queue typedef with unbounded slice (#6236). +* Fix error when force assignment is used with ref function args (#6244). [Ryszard Rozak, Antmicro Ltd.] Verilator 5.038 2025-07-08 From bd1ac03828c09e30bb1ee68ddcd2115b81f20c27 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 31 Jul 2025 18:38:50 -0400 Subject: [PATCH 110/252] Add I/O versus data declaration checking. --- Changes | 2 +- src/V3AstNodeOther.h | 4 +++ src/V3LinkDot.cpp | 20 ++++++++++++- test_regress/t/t_func_nansi_bad.out | 44 +++++++++++++++++++++++++++++ test_regress/t/t_func_nansi_bad.py | 16 +++++++++++ test_regress/t/t_func_nansi_bad.v | 38 +++++++++++++++++++++++++ test_regress/t/t_inst_nansi.v | 4 +-- test_regress/t/t_inst_nansi_bad.out | 32 +++++++++++++++++++++ test_regress/t/t_inst_nansi_bad.py | 16 +++++++++++ test_regress/t/t_inst_nansi_bad.v | 30 ++++++++++++++++++++ test_regress/t/t_param_sel.v | 2 +- test_regress/t/t_var_dup_bad.out | 6 ++++ 12 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 test_regress/t/t_func_nansi_bad.out create mode 100755 test_regress/t/t_func_nansi_bad.py create mode 100644 test_regress/t/t_func_nansi_bad.v create mode 100644 test_regress/t/t_inst_nansi_bad.out create mode 100755 test_regress/t/t_inst_nansi_bad.py create mode 100644 test_regress/t/t_inst_nansi_bad.v diff --git a/Changes b/Changes index f19ed3227..71fe31bf2 100644 --- a/Changes +++ b/Changes @@ -16,7 +16,7 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. -* Add enum base data type, and wire data type checking per IEEE. +* Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. * Add error on missing forward declarations (#6206). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d4a603ed8..16dcfb6fb 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1987,6 +1987,7 @@ class AstVar final : public AstNode { bool m_attrSFormat : 1; // User sformat attribute bool m_attrSplitVar : 1; // declared with split_var metacomment bool m_fileDescr : 1; // File descriptor + bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration bool m_isConst : 1; // Table contains constant data bool m_isContinuously : 1; // Ever assigned continuously (for force/release) bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier @@ -2034,6 +2035,7 @@ class AstVar final : public AstNode { m_attrSFormat = false; m_attrSplitVar = false; m_fileDescr = false; + m_gotNansiType = false; m_isConst = false; m_isContinuously = false; m_hasStrengthAssignment = false; @@ -2188,6 +2190,8 @@ public: if (flag) m_funcLocalSticky = true; } void funcReturn(bool flag) { m_funcReturn = flag; } + void gotNansiType(bool flag) { m_gotNansiType = flag; } + bool gotNansiType() { return m_gotNansiType; } void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; } bool hasStrengthAssignment() { return m_hasStrengthAssignment; } void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 59182ca99..20ac29fea 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1470,7 +1470,8 @@ class LinkDotFindVisitor final : public VNVisitor { const bool nansiBad = (((findvarp->isDeclTyped() || findvarp->isNet()) && (nodep->isDeclTyped() || nodep->isNet())) - || (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output) + || (findvarp->isIO() && nodep->isIO()) // e.g. !(output && output) + || findvarp->gotNansiType()); // e.g. int x && int x const bool ansiBad = findvarp->isAnsi() || nodep->isAnsi(); // dup illegal with ANSI if (ansiBad || nansiBad) { @@ -1496,6 +1497,7 @@ class LinkDotFindVisitor final : public VNVisitor { } else { findvarp->combineType(nodep); findvarp->fileline()->modifyStateInherit(nodep->fileline()); + findvarp->gotNansiType(true); UASSERT_OBJ(nodep->subDTypep(), nodep, "Var has no type"); if (nodep->subDTypep()->numeric().isSigned() && !findvarp->subDTypep()->numeric().isSigned()) { @@ -1513,6 +1515,22 @@ class LinkDotFindVisitor final : public VNVisitor { newdtypep->unlinkFrBack(); findvarp->childDTypep(newdtypep); } + if (nodep->childDTypep() && findvarp->childDTypep() + && !(VN_IS(nodep->childDTypep(), BasicDType) + && VN_AS(nodep->childDTypep(), BasicDType)->keyword() + == VBasicDTypeKwd::LOGIC_IMPLICIT) + && !(VN_IS(findvarp->childDTypep(), BasicDType) + && VN_AS(findvarp->childDTypep(), BasicDType)->keyword() + == VBasicDTypeKwd::LOGIC_IMPLICIT) + && !nodep->sameTree(findvarp)) { + nodep->v3error("Non-ANSI I/O declaration of signal " + "conflicts with type declaration: " + << nodep->prettyNameQ() << '\n' + << nodep->warnContextPrimary() << '\n' + << findvarp->warnOther() + << "... Location of other declaration\n" + << findvarp->warnContextSecondary()); + } } VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } else { diff --git a/test_regress/t/t_func_nansi_bad.out b/test_regress/t/t_func_nansi_bad.out new file mode 100644 index 000000000..6ba6f4330 --- /dev/null +++ b/test_regress/t/t_func_nansi_bad.out @@ -0,0 +1,44 @@ +%Error: t/t_func_nansi_bad.v:13:14: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' + 13 | shortint bad1; + | ^~~~ + t/t_func_nansi_bad.v:12:18: ... Location of other declaration + 12 | input [15:0] bad1; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_func_nansi_bad.v:18:7: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' + 18 | T bad2; + | ^~~~ + t/t_func_nansi_bad.v:17:18: ... Location of other declaration + 17 | input [31:0] bad2; + | ^~~~ +%Error: t/t_func_nansi_bad.v:23:15: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' + 23 | reg [3:0] bad3; + | ^~~~ + t/t_func_nansi_bad.v:22:17: ... Location of other declaration + 22 | input [7:0] bad3; + | ^~~~ +%Error: t/t_func_nansi_bad.v:28:9: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad4' + 28 | reg bad4; + | ^~~~ + t/t_func_nansi_bad.v:27:17: ... Location of other declaration + 27 | input [7:0] bad4; + | ^~~~ +%Error: t/t_func_nansi_bad.v:29:9: Duplicate declaration of signal: 'bad4' + 29 | reg bad4; + | ^~~~ + t/t_func_nansi_bad.v:27:17: ... Location of original declaration + 27 | input [7:0] bad4; + | ^~~~ +%Error: t/t_func_nansi_bad.v:34:17: Duplicate declaration of signal: 'bad5' + 34 | input [7:0] bad5; + | ^~~~ + t/t_func_nansi_bad.v:33:17: ... Location of original declaration + 33 | input [7:0] bad5; + | ^~~~ +%Error: t/t_func_nansi_bad.v:35:9: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad5' + 35 | reg bad5; + | ^~~~ + t/t_func_nansi_bad.v:33:17: ... Location of other declaration + 33 | input [7:0] bad5; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_func_nansi_bad.py b/test_regress/t/t_func_nansi_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_func_nansi_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_func_nansi_bad.v b/test_regress/t/t_func_nansi_bad.v new file mode 100644 index 000000000..c69df2b8c --- /dev/null +++ b/test_regress/t/t_func_nansi_bad.v @@ -0,0 +1,38 @@ +// 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 + +typedef int T; + +module test; + + task t1; + input [15:0] bad1; + shortint bad1; // <--- Error (type doesn't match above) + endtask + + task t2; + input [31:0] bad2; + T bad2; // <--- Error (type doesn't match above) + endtask + + task t3; + input [7:0] bad3; + reg [3:0] bad3; // <--- Error (type doesn't match above) + endtask + + task t4; + input [7:0] bad4; + reg bad4; + reg bad4; // <--- Error (duplicate) + endtask + + task t5; + input [7:0] bad5; + input [7:0] bad5; // <--- Error (duplicate) + reg bad5; + endtask + +endmodule diff --git a/test_regress/t/t_inst_nansi.v b/test_regress/t/t_inst_nansi.v index 651df6f41..370d31894 100644 --- a/test_regress/t/t_inst_nansi.v +++ b/test_regress/t/t_inst_nansi.v @@ -15,10 +15,10 @@ module t(b, si, i, li, w3, w4); output i; // Output after type output li; - input w3; + input [2:0] w3; wire [2:0] w3; wire [3:0] w4; - input w4; + input [3:0] w4; initial begin if ($bits(b) != 8) $stop; diff --git a/test_regress/t/t_inst_nansi_bad.out b/test_regress/t/t_inst_nansi_bad.out new file mode 100644 index 000000000..3576c08e5 --- /dev/null +++ b/test_regress/t/t_inst_nansi_bad.out @@ -0,0 +1,32 @@ +%Error: t/t_inst_nansi_bad.v:14:12: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' + 14 | shortint bad1; + | ^~~~ + t/t_inst_nansi_bad.v:13:17: ... Location of other declaration + 13 | output [15:0] bad1; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_inst_nansi_bad.v:17:5: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' + 17 | T bad2; + | ^~~~ + t/t_inst_nansi_bad.v:16:17: ... Location of other declaration + 16 | output [31:0] bad2; + | ^~~~ +%Error: t/t_inst_nansi_bad.v:20:13: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' + 20 | reg [7:0] bad3; + | ^~~~ + t/t_inst_nansi_bad.v:19:16: ... Location of other declaration + 19 | output [3:0] bad3; + | ^~~~ +%Error: t/t_inst_nansi_bad.v:24:7: Duplicate declaration of signal: 'bad4' + 24 | reg bad4; + | ^~~~ + t/t_inst_nansi_bad.v:22:10: ... Location of original declaration + 22 | output bad4; + | ^~~~ +%Error: t/t_inst_nansi_bad.v:27:10: Duplicate declaration of signal: 'bad5' + 27 | output bad5; + | ^~~~ + t/t_inst_nansi_bad.v:26:10: ... Location of original declaration + 26 | output bad5; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_inst_nansi_bad.py b/test_regress/t/t_inst_nansi_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_inst_nansi_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_inst_nansi_bad.v b/test_regress/t/t_inst_nansi_bad.v new file mode 100644 index 000000000..b87a602a1 --- /dev/null +++ b/test_regress/t/t_inst_nansi_bad.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +typedef int T; + +module test ( /*AUTOARG*/ + // Outputs + bad1, bad2, bad3, bad4, bad5 + ); + output [15:0] bad1; + shortint bad1; // <--- Error (type doesn't match above) + + output [31:0] bad2; + T bad2; // <--- Error (type doesn't match above due to range) + + output [3:0] bad3; + reg [7:0] bad3; // <--- Error (range doesn't match) + + output bad4; + reg bad4; + reg bad4; // <--- Error (duplicate) + + output bad5; + output bad5; // <--- Error (duplicate) + reg bad5; // <--- Error (duplicate) + +endmodule diff --git a/test_regress/t/t_param_sel.v b/test_regress/t/t_param_sel.v index cf57dfb29..b87db56e2 100644 --- a/test_regress/t/t_param_sel.v +++ b/test_regress/t/t_param_sel.v @@ -90,5 +90,5 @@ module add (/*AUTOARG*/ parameter PASSDOWN = 9999; input [31:0] in; output [31:0] out; - wire out = in + PASSDOWN; + assign out = in + PASSDOWN; endmodule diff --git a/test_regress/t/t_var_dup_bad.out b/test_regress/t/t_var_dup_bad.out index 9af8a58c4..a89c6b69d 100644 --- a/test_regress/t/t_var_dup_bad.out +++ b/test_regress/t/t_var_dup_bad.out @@ -35,6 +35,12 @@ t/t_var_dup_bad.v:31:11: ... Location of original declaration 31 | output oi; | ^~ +%Error: t/t_var_dup_bad.v:36:11: Duplicate declaration of signal: 'og' + 36 | reg og; + | ^~ + t/t_var_dup_bad.v:34:11: ... Location of original declaration + 34 | output og; + | ^~ %Error: t/t_var_dup_bad.v:39:15: Duplicate declaration of signal: 'org' 39 | output reg org; | ^~~ From d0f09198301aff04c5bf3b9b1e85ce752954642c Mon Sep 17 00:00:00 2001 From: Rodrigo Batista de Moraes <51273772+Rodrigodd@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:54:18 -0300 Subject: [PATCH 111/252] Fix write of 0 in '%c' (#6248) (#6249) --- docs/CONTRIBUTORS | 1 + src/V3Number.cpp | 5 +---- test_regress/t/t_sys_file_null.py | 2 +- test_regress/t/t_sys_file_null.v | 2 ++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f20d2519b..9b75ecf02 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -206,6 +206,7 @@ Richard Myers Risto Pejašinović Robert Balas Robin Heinemann +Rodrigo Batista de Moraes Rupert Swarbrick Ryan Ziegler Ryszard Rozak diff --git a/src/V3Number.cpp b/src/V3Number.cpp index d483c0cf3..ea9be8a29 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -746,10 +746,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB if (width() > 8) fl->v3warn(WIDTHTRUNC, "$display-like format of %c format of > 8 bit value"); const unsigned int v = bitsValue(0, 8); - char strc[2]; - strc[0] = v & 0xff; - strc[1] = '\0'; - str = strc; + str.push_back(static_cast(v)); return str; } case 's': { diff --git a/test_regress/t/t_sys_file_null.py b/test_regress/t/t_sys_file_null.py index 29e82f03c..6f9225ca3 100755 --- a/test_regress/t/t_sys_file_null.py +++ b/test_regress/t/t_sys_file_null.py @@ -16,7 +16,7 @@ test.compile() test.execute() filename = test.obj_dir + "/zeros.log" -if os.path.getsize(filename) != 16: +if os.path.getsize(filename) != 20: test.error(filename + ": Wrong file size") test.passes() diff --git a/test_regress/t/t_sys_file_null.v b/test_regress/t/t_sys_file_null.v index 32b2ef685..de152ce3a 100644 --- a/test_regress/t/t_sys_file_null.v +++ b/test_regress/t/t_sys_file_null.v @@ -10,6 +10,8 @@ module t; integer fd, cnt; initial begin fd = $fopen({`STRINGIFY(`TEST_OBJ_DIR),"/zeros.log"}, "w"); + for (cnt = 0; cnt < 4; cnt = cnt + 1) + $fwrite(fd, "%c", 8'd0); for (cnt = 0; cnt < 16; cnt = cnt + 4) $fwrite(fd, "%u", 32'd0); $fclose(fd); From 61f4c97f405432688e45ee8178dd290b1e0d33dc Mon Sep 17 00:00:00 2001 From: Szymon Gizler Date: Fri, 1 Aug 2025 12:39:13 +0200 Subject: [PATCH 112/252] Support unassigned virtual interfaces (#5265) (#6245) --- src/V3LinkDot.cpp | 55 +++++++++++-------- ...d_bad.py => t_interface_virtual_unused.py} | 6 +- ...sed_bad.v => t_interface_virtual_unused.v} | 0 .../t/t_interface_virtual_unused_bad.out | 5 -- 4 files changed, 36 insertions(+), 30 deletions(-) rename test_regress/t/{t_interface_virtual_unused_bad.py => t_interface_virtual_unused.py} (83%) rename test_regress/t/{t_interface_virtual_unused_bad.v => t_interface_virtual_unused.v} (100%) delete mode 100644 test_regress/t/t_interface_virtual_unused_bad.out diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 20ac29fea..d41eef7d7 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -516,30 +516,39 @@ public: } else { ifacerefp->v3fatalSrc("Unlinked interface"); } - } else if (ifacerefp->ifaceViaCellp()->dead()) { - if (varp->isIfaceRef()) { - if (forPrimary() && !varp->isIfaceParent() - && !v3Global.opt.topIfacesSupported()) { - // Only AstIfaceRefDType's at this point correspond to ports; - // haven't made additional ones for interconnect yet, so assert is simple - // What breaks later is we don't have a Scope/Cell representing - // the interface to attach to - varp->v3warn(E_UNSUPPORTED, - "Unsupported: Interfaced port on top level module"); - } - ifacerefp->v3error("Parent instance's interface is not found: " - << AstNode::prettyNameQ(ifacerefp->ifaceName()) << '\n' - << ifacerefp->warnMore() - << "... Perhaps intended an interface instantiation but " - "are missing parenthesis (IEEE 1800-2023 25.3)?"); - } else { - ifacerefp->v3warn( - E_UNSUPPORTED, - "Unsupported: virtual interface never assigned any actual interface"); - varp->dtypep(ifacerefp->findCHandleDType()); - VL_DO_DANGLING(ifacerefp->unlinkFrBack()->deleteTree(), ifacerefp); + } else if (ifacerefp->ifaceViaCellp()->dead() && varp->isIfaceRef()) { + if (forPrimary() && !varp->isIfaceParent() && !v3Global.opt.topIfacesSupported()) { + // Only AstIfaceRefDType's at this point correspond to ports; + // haven't made additional ones for interconnect yet, so assert is simple + // What breaks later is we don't have a Scope/Cell representing + // the interface to attach to + varp->v3warn(E_UNSUPPORTED, + "Unsupported: Interfaced port on top level module"); } + ifacerefp->v3error("Parent instance's interface is not found: " + << AstNode::prettyNameQ(ifacerefp->ifaceName()) << '\n' + << ifacerefp->warnMore() + << "... Perhaps intended an interface instantiation but " + "are missing parenthesis (IEEE 1800-2023 25.3)?"); continue; + } else if (ifacerefp->ifaceViaCellp()->dead() + || !existsNodeSym(ifacerefp->ifaceViaCellp())) { + // virtual interface never assigned any actual interface + ifacerefp->ifacep()->dead(false); + varp->dtypep(ifacerefp->dtypep()); + // Create dummy cell to keep the virtual interface alive + // (later stages assume that non-dead interface has associated cell) + AstCell* const ifacecellp + = new AstCell{ifacerefp->ifacep()->fileline(), + ifacerefp->ifacep()->fileline(), + ifacerefp->ifacep()->name() + "__02E" + varp->name(), + ifacerefp->ifaceName(), + nullptr, + nullptr, + nullptr}; + ifacecellp->modp(ifacerefp->ifacep()); + VSymEnt* const symp = new VSymEnt{&m_syms, ifacecellp}; + ifacerefp->ifacep()->user1p(symp); } VSymEnt* const ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp()); VSymEnt* ifOrPortSymp = ifaceSymp; @@ -2787,7 +2796,7 @@ class LinkDotResolveVisitor final : public VNVisitor { void visit(AstTypeTable*) override {} void visit(AstConstPool*) override {} void visit(AstNodeModule* nodep) override { - if (nodep->dead()) return; + if (nodep->dead() || !m_statep->existsNodeSym(nodep)) return; LINKDOT_VISIT_START(); UINFO(8, indent() << "visit " << nodep); checkNoDot(nodep); diff --git a/test_regress/t/t_interface_virtual_unused_bad.py b/test_regress/t/t_interface_virtual_unused.py similarity index 83% rename from test_regress/t/t_interface_virtual_unused_bad.py rename to test_regress/t/t_interface_virtual_unused.py index 31228c9a7..d4f986441 100755 --- a/test_regress/t/t_interface_virtual_unused_bad.py +++ b/test_regress/t/t_interface_virtual_unused.py @@ -9,8 +9,10 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_interface_virtual_unused_bad.v b/test_regress/t/t_interface_virtual_unused.v similarity index 100% rename from test_regress/t/t_interface_virtual_unused_bad.v rename to test_regress/t/t_interface_virtual_unused.v diff --git a/test_regress/t/t_interface_virtual_unused_bad.out b/test_regress/t/t_interface_virtual_unused_bad.out deleted file mode 100644 index 5775cfc79..000000000 --- a/test_regress/t/t_interface_virtual_unused_bad.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_interface_virtual_unused_bad.v:14:12: Unsupported: virtual interface never assigned any actual interface - 14 | virtual QBus q8; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to From f9bdab65f0988d44335b6567cc96e900ef0a98fd Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 1 Aug 2025 13:24:18 +0200 Subject: [PATCH 113/252] Fix coverage of variables of complex types (#6250) --- src/V3AstNodeExpr.h | 1 + src/V3AstNodes.cpp | 10 ++++++++++ src/V3Clock.cpp | 2 +- test_regress/t/t_cover_toggle.out | 19 +++++++++++++++++++ test_regress/t/t_cover_toggle.v | 19 +++++++++++++++++++ test_regress/t/t_cover_toggle__points.out | 21 +++++++++++++++++++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 851f9e7f4..dbc9932f3 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -3895,6 +3895,7 @@ public: AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { return new AstNeq{fileline(), lhsp, rhsp}; } + static AstNodeBiop* newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp); void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { out.opNeq(lhs, rhs); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d2184cab0..a8b488648 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -317,6 +317,16 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* r } } +AstNodeBiop* AstNeq::newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) { + if (lhsp->isString() && rhsp->isString()) { + return new AstNeqN{fl, lhsp, rhsp}; + } else if (lhsp->isDouble() && rhsp->isDouble()) { + return new AstNeqD{fl, lhsp, rhsp}; + } else { + return new AstNeq{fl, lhsp, rhsp}; + } +} + AstExecGraph::AstExecGraph(FileLine* fileline, const string& name) VL_MT_DISABLED : ASTGEN_SUPER_ExecGraph(fileline), m_depGraphp{new V3Graph}, diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 72e78ad57..b9d2e2c6f 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -111,7 +111,7 @@ class ClockVisitor final : public VNVisitor { = VN_CAST(origp->dtypep()->skipRefp(), BasicDType)) { if (!bdtypep->isOpaque()) comparedp = new AstXor{nodep->fileline(), origp, changeRdp}; } - if (!comparedp) comparedp = AstEq::newTyped(nodep->fileline(), origp, changeRdp); + if (!comparedp) comparedp = AstNeq::newTyped(nodep->fileline(), origp, changeRdp); AstIf* const newp = new AstIf{nodep->fileline(), comparedp, incp}; // We could add another IF to detect posedges, and only increment if so. // It's another whole branch though versus a potential memory miss. diff --git a/test_regress/t/t_cover_toggle.out b/test_regress/t/t_cover_toggle.out index d68a87d6d..29a7dc0a1 100644 --- a/test_regress/t/t_cover_toggle.out +++ b/test_regress/t/t_cover_toggle.out @@ -5,6 +5,8 @@ // any use, without warranty, 2008 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + typedef struct packed {logic a;} str_logic; + module t (/*AUTOARG*/ // Inputs clk, check_real, check_array_real, check_string @@ -27,6 +29,8 @@ %000002 str_t stoggle; initial stoggle='0; + 000019 str_logic strl; initial strl='0; + union { real val1; // TODO use bit [7:0] here real val2; // TODO use bit [3:0] here @@ -45,6 +49,8 @@ } str_queue_t; str_queue_t str_queue; + assign strl.a = clk; + alpha a1 (/*AUTOINST*/ // Outputs .toggle_up (toggle_up), @@ -80,6 +86,11 @@ .clk (clk), .toggle (toggle)); + mod_struct i_mod_struct (/*AUTOINST*/ + // Inputs + .input_struct (strl)); + + %000001 reg [1:0] memory[121:110]; wire [1023:0] largeish = {992'h0, cyc}; @@ -209,3 +220,11 @@ end endmodule + module mod_struct(/*AUTOARG*/ + // Inputs + input_struct + ); + + 000019 input str_logic input_struct; + endmodule + diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v index 1590a813c..e41aa437a 100644 --- a/test_regress/t/t_cover_toggle.v +++ b/test_regress/t/t_cover_toggle.v @@ -4,6 +4,8 @@ // any use, without warranty, 2008 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +typedef struct packed {logic a;} str_logic; + module t (/*AUTOARG*/ // Inputs clk, check_real, check_array_real, check_string @@ -26,6 +28,8 @@ module t (/*AUTOARG*/ str_t stoggle; initial stoggle='0; + str_logic strl; initial strl='0; + union { real val1; // TODO use bit [7:0] here real val2; // TODO use bit [3:0] here @@ -44,6 +48,8 @@ module t (/*AUTOARG*/ } str_queue_t; str_queue_t str_queue; + assign strl.a = clk; + alpha a1 (/*AUTOINST*/ // Outputs .toggle_up (toggle_up), @@ -79,6 +85,11 @@ module t (/*AUTOARG*/ .clk (clk), .toggle (toggle)); + mod_struct i_mod_struct (/*AUTOINST*/ + // Inputs + .input_struct (strl)); + + reg [1:0] memory[121:110]; wire [1023:0] largeish = {992'h0, cyc}; @@ -207,3 +218,11 @@ module param #(parameter P = 2) (/*AUTOARG*/ assign z = 1; end endmodule + +module mod_struct(/*AUTOARG*/ + // Inputs + input_struct + ); + + input str_logic input_struct; +endmodule diff --git a/test_regress/t/t_cover_toggle__points.out b/test_regress/t/t_cover_toggle__points.out index f73846a2f..144f94d9f 100644 --- a/test_regress/t/t_cover_toggle__points.out +++ b/test_regress/t/t_cover_toggle__points.out @@ -5,6 +5,8 @@ // any use, without warranty, 2008 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + typedef struct packed {logic a;} str_logic; + module t (/*AUTOARG*/ // Inputs clk, check_real, check_array_real, check_string @@ -31,6 +33,9 @@ -000002 point: comment=stoggle.b hier=top.t -000002 point: comment=stoggle.u.ua hier=top.t + 000019 str_logic strl; initial strl='0; ++000019 point: comment=strl.a hier=top.t + union { real val1; // TODO use bit [7:0] here real val2; // TODO use bit [3:0] here @@ -62,6 +67,8 @@ } str_queue_t; str_queue_t str_queue; + assign strl.a = clk; + alpha a1 (/*AUTOINST*/ // Outputs .toggle_up (toggle_up), @@ -97,6 +104,11 @@ .clk (clk), .toggle (toggle)); + mod_struct i_mod_struct (/*AUTOINST*/ + // Inputs + .input_struct (strl)); + + %000001 reg [1:0] memory[121:110]; -000001 point: comment=memory[110][0] hier=top.t -000000 point: comment=memory[110][1] hier=top.t @@ -301,3 +313,12 @@ end endmodule + module mod_struct(/*AUTOARG*/ + // Inputs + input_struct + ); + + 000019 input str_logic input_struct; ++000019 point: comment=input_struct.a hier=top.t.i_mod_struct + endmodule + From 7bb790f7e47fa9a68b772db1080c2828c24eb1d9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 1 Aug 2025 12:05:53 -0400 Subject: [PATCH 114/252] CI: Proactively use windows-2025 --- .github/workflows/msbuild.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index d4c801767..b0c37e87f 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -27,7 +27,7 @@ concurrency: jobs: windows: name: run on windows - runs-on: windows-latest + runs-on: windows-2025 steps: - uses: actions/checkout@v4 with: From dca504c706a3f6c1e33be92a9bff679aef9a584e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 2 Aug 2025 13:44:40 -0400 Subject: [PATCH 115/252] Internals: Use UINFOTREE istead of dumpTree --- src/V3Assert.cpp | 2 +- src/V3Begin.cpp | 4 +- src/V3Case.cpp | 14 +++--- src/V3Cast.cpp | 2 +- src/V3Clock.cpp | 2 +- src/V3Const.cpp | 68 +++++++++++++------------- src/V3Depth.cpp | 2 +- src/V3DepthBlock.cpp | 6 +-- src/V3Descope.cpp | 2 +- src/V3Error.h | 5 +- src/V3Gate.cpp | 2 +- src/V3Inline.cpp | 3 +- src/V3Inst.cpp | 10 ++-- src/V3Life.cpp | 2 +- src/V3LinkDot.cpp | 20 ++++---- src/V3LinkInc.cpp | 4 +- src/V3LinkResolve.cpp | 6 +-- src/V3Param.cpp | 8 +-- src/V3Randomize.cpp | 2 +- src/V3Reloop.cpp | 4 +- src/V3Simulate.h | 2 +- src/V3Slice.cpp | 6 +-- src/V3Split.cpp | 4 +- src/V3SplitAs.cpp | 6 +-- src/V3Subst.cpp | 4 +- src/V3Table.cpp | 2 +- src/V3Task.cpp | 14 +++--- src/V3Trace.cpp | 2 +- src/V3Tristate.cpp | 36 +++++++------- src/V3Unknown.cpp | 22 ++++----- src/V3Unroll.cpp | 8 +-- src/V3Width.cpp | 111 +++++++++++++++++++++--------------------- src/V3WidthCommit.cpp | 4 +- src/V3WidthSel.cpp | 36 +++++++------- 34 files changed, 214 insertions(+), 211 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index bd1423034..57ca6092b 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -473,7 +473,7 @@ class AssertVisitor final : public VNVisitor { AstNode* const assp = new AstAssignDly{ nodep->fileline(), new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}, inp}; alwaysp->addStmtsp(assp); - // if (debug() >= 9) assp->dumpTree("- ass: "); + // UINFOTREE(9, assp, "", "ass"); invarp = outvarp; inp = new AstVarRef{nodep->fileline(), invarp, VAccess::READ}; } diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index b1f5ce772..2a9733bf1 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -423,7 +423,7 @@ static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, : VNType::atGteS); } AstNode* V3Begin::convertToWhile(AstForeach* nodep) { - // if (debug()) dumpTree("- foreach-old: "); + // UINFOTREE(1, nodep, "", "foreach-old"); const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars); UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach"); AstNodeExpr* const fromp = loopsp->fromp(); @@ -542,6 +542,6 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) { } VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp); VL_DO_DANGLING(nodep->deleteTree(), nodep); - // if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: "); + // UINFOTREE(1, newp, "", "foreach-new"); return newp; } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index c0b3f8648..1c13b744a 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -201,7 +201,7 @@ class CaseVisitor final : public VNVisitor { for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) { - // if (debug() >= 9) icondp->dumpTree("- caseitem: "); + // UINFOTREE(9, icondp, "", "caseitem"); AstConst* const iconstp = VN_AS(icondp, Const); UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this"); if (neverItem(nodep, iconstp)) { @@ -396,7 +396,7 @@ class CaseVisitor final : public VNVisitor { } VL_DO_DANGLING(nodep->deleteTree(), nodep); VL_DO_DANGLING(cexprp->deleteTree(), cexprp); - if (debug() >= 9) ifrootp->dumpTree("- _simp: "); + UINFOTREE(9, ifrootp, "", "_simp"); } void replaceCaseComplicated(AstCase* nodep) { @@ -407,7 +407,7 @@ class CaseVisitor final : public VNVisitor { AstNodeExpr* const cexprp = nodep->exprp()->unlinkFrBack(); // We'll do this in two stages. First stage, convert the conditions to // the appropriate IF AND terms. - if (debug() >= 9) nodep->dumpTree("- _comp_IN::: "); + UINFOTREE(9, nodep, "", "_comp_IN::"); bool hadDefault = false; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { @@ -474,7 +474,7 @@ class CaseVisitor final : public VNVisitor { nodep->addItemsp(new AstCaseItem{ nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr}); } - if (debug() >= 9) nodep->dumpTree("- _comp_COND: "); + UINFOTREE(9, nodep, "", "_comp_COND"); // Now build the IF statement tree // The tree can be quite huge. Pull ever group of 8 out, and make a OR tree. // This reduces the depth for the bottom elements, at the cost of @@ -524,12 +524,12 @@ class CaseVisitor final : public VNVisitor { itemnextp = newp; } } - if (debug() >= 9) nodep->dumpTree("- _comp_TREE: "); + UINFOTREE(9, nodep, "", "_comp_TREE"); // Handle any assertions replaceCaseParallel(nodep, false); // Replace the CASE... with IF... - if (debug() >= 9 && grouprootp) grouprootp->dumpTree("- _new: "); if (grouprootp) { + UINFOTREE(9, grouprootp, "", "_new"); nodep->replaceWith(grouprootp); } else { nodep->unlinkFrBack(); @@ -563,7 +563,7 @@ class CaseVisitor final : public VNVisitor { void visit(AstCase* nodep) override { V3Case::caseLint(nodep); iterateChildren(nodep); - if (debug() >= 9) nodep->dumpTree("- case_old: "); + UINFOTREE(9, nodep, "", "case_old"); if (isCaseTreeFast(nodep) && v3Global.opt.fCase()) { // It's a simple priority encoder or complete statement // we can make a tree of statements to avoid extra comparisons diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 9795de230..9a2972e96 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -64,7 +64,7 @@ class CastVisitor final : public VNVisitor { = new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()}; UINFO(4, " MadeCast " << static_cast(castp) << " for " << nodep); relinkHandle.relink(castp); - // if (debug() > 8) castp->dumpTree("- castins: "); + // UINFOTREE(9, castp, "", "castins"); // ensureLower32Cast(castp); nodep->user1(1); // Now must be of known size diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index b9d2e2c6f..7dc65f903 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor { } // VISITORS void visit(AstCoverToggle* nodep) override { - // if (debug()) nodep->dumpTree("- ct: "); + // UINFOTREE(1, nodep, "", "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 058bf0493..3c5e568c1 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -396,7 +396,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { if (fail && !m_failed) { UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:" << line << " when checking:" << nodep); - // if (debug() >= 9) m_rootp->dumpTree("- root: "); + // UINFOTREE(9, m_rootp, "", "root"); m_failed = true; } return m_failed; @@ -1334,12 +1334,12 @@ class ConstVisitor final : public VNVisitor { // Add or any operation which doesn't care if we discard top bits AstNodeBiop* const bip = VN_CAST(nodep->fromp(), NodeBiop); if (!(m_doV && bip && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0)) return false; - if (debug() >= 9) nodep->dumpTree("- SEL(BI)-in: "); + UINFOTREE(9, nodep, "", "SEL(BI)-in"); AstNodeExpr* const bilhsp = bip->lhsp()->unlinkFrBack(); AstNodeExpr* const birhsp = bip->rhsp()->unlinkFrBack(); bip->lhsp(new AstSel{nodep->fileline(), bilhsp, 0, nodep->widthConst()}); bip->rhsp(new AstSel{nodep->fileline(), birhsp, 0, nodep->widthConst()}); - if (debug() >= 9) bip->dumpTree("- SEL(BI)-ou: "); + UINFOTREE(9, bip, "", "SEL(BI)-ou"); VL_DO_DANGLING(replaceWChild(nodep, bip), nodep); return true; } @@ -1362,11 +1362,11 @@ class ConstVisitor final : public VNVisitor { if (newLsb + nodep->widthConst() > ap->width()) return false; // UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)"); - if (debug() >= 9) nodep->dumpTree("- SEL(SH)-in: "); + UINFOTREE(9, nodep, "", "SEL(SH)-in"); AstSel* const newp = new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()}; nodep->replaceWithKeepDType(newp); - if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: "); + UINFOTREE(9, newp, "", "SEL(SH)-ou"); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -1384,7 +1384,7 @@ class ConstVisitor final : public VNVisitor { if (!constp) return false; if (!constp->num().isBitsZero(constp->width() - 1, subsize)) return false; // - if (debug() >= 9) nodep->dumpTree("- BI(EXTEND)-in: "); + UINFOTREE(9, nodep, "", "BI(EXTEND)-in"); smallerp->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(extendp->unlinkFrBack()), extendp); // aka nodep->lhsp. nodep->rhsp(smallerp); @@ -1393,7 +1393,7 @@ class ConstVisitor final : public VNVisitor { 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: "); + UINFOTREE(9, nodep, "", "BI(EXTEND)-ou"); return true; } bool operandBiExtendConstOver(const AstNodeBiop* nodep) { @@ -1588,8 +1588,8 @@ class ConstVisitor final : public VNVisitor { "Already constant??"); AstNode* const newp = new AstConst{oldp->fileline(), num}; oldp->replaceWithKeepDType(newp); - if (debug() > 5) oldp->dumpTree("- const_old: "); - if (debug() > 5) newp->dumpTree("- _new: "); + UINFOTREE(6, oldp, "", "const_old"); + UINFOTREE(6, newp, "", "_new"); VL_DO_DANGLING(pushDeletep(oldp), oldp); } void replaceNum(AstNode* nodep, uint32_t val) { @@ -1663,8 +1663,8 @@ class ConstVisitor final : public VNVisitor { // Replace oldp node with a constant set to specified value UASSERT(oldp, "Null old"); AstNode* const newp = new AstConst{oldp->fileline(), AstConst::String{}, num}; - if (debug() > 5) oldp->dumpTree("- const_old: "); - if (debug() > 5) newp->dumpTree("- _new: "); + UINFOTREE(6, oldp, "", "const_old"); + UINFOTREE(6, newp, "", "_new"); oldp->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(oldp), oldp); } @@ -1721,7 +1721,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) - // if (debug()) nodep->dumpTree("- repAsvConst_old: "); + // UINFOTREE(1, nodep, "", "repAsvConst_old"); AstNodeExpr* const ap = nodep->lhsp(); AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop); AstNodeExpr* const bp = rp->lhsp(); @@ -1736,7 +1736,7 @@ class ConstVisitor final : public VNVisitor { 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: "); + // UINFOTREE(1, nodep, "", "repAsvConst_new"); } void replaceAsvLUp(AstNodeBiop* nodep) { // BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) @@ -1749,7 +1749,7 @@ class ConstVisitor final : public VNVisitor { lp->lhsp(lrp); lp->rhsp(rp); lp->dtypeFrom(nodep); // Upper widthMin more likely correct - // if (debug()) nodep->dumpTree("- repAsvLUp_new: "); + // UINFOTREE(1, nodep, "", "repAsvLUp_new"); } void replaceAsvRUp(AstNodeBiop* nodep) { // BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) @@ -1762,7 +1762,7 @@ class ConstVisitor final : public VNVisitor { rp->lhsp(lp); rp->rhsp(rrp); rp->dtypeFrom(nodep); // Upper widthMin more likely correct - // if (debug()) nodep->dumpTree("- repAsvRUp_new: "); + // UINFOTREE(1, nodep, "", "repAsvRUp_new"); } void replaceAndOr(AstNodeBiop* nodep) { // OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr)) @@ -1795,7 +1795,7 @@ class ConstVisitor final : public VNVisitor { } else { nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched"); } - // if (debug()) nodep->dumpTree("- repAndOr_new: "); + // UINFOTREE(1, nodep, "", "repAndOr_new"); } void replaceShiftSame(AstNodeBiop* nodep) { // Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr) @@ -1814,7 +1814,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); - // if (debug()) nodep->dumpTree("- repShiftSame_new: "); + // UINFOTREE(1, nodep, "", "repShiftSame_new"); } void replaceConcatSel(AstConcat* nodep) { // {a[1], a[0]} -> a[1:0] @@ -1940,7 +1940,7 @@ class ConstVisitor final : public VNVisitor { } void replaceShiftShift(AstNodeBiop* nodep) { UINFO(4, "SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) " << nodep); - if (debug() >= 9) nodep->dumpTree("- repShiftShift_old: "); + UINFOTREE(9, nodep, "", "repShiftShift_old"); AstNodeBiop* const lhsp = VN_AS(nodep->lhsp(), NodeBiop); lhsp->unlinkFrBack(); AstNodeExpr* const ap = lhsp->lhsp()->unlinkFrBack(); @@ -1992,7 +1992,7 @@ class ConstVisitor final : public VNVisitor { newp = new AstAnd{nodep->fileline(), newp, new AstConst{nodep->fileline(), mask}}; nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); - // if (debug()) newp->dumpTree("- repShiftShift_new: "); + // UINFOTREE(1, newp, "", "repShiftShift_new"); iterate(newp); // Further reduce, either node may have more reductions. } VL_DO_DANGLING(pushDeletep(lhsp), lhsp); @@ -2035,8 +2035,8 @@ class ConstVisitor final : public VNVisitor { const bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt()); UINFO(4, "replaceAssignMultiSel " << nodep); UINFO(4, " && " << nextp); - // if (debug()) nodep->dumpTree("- comb1: "); - // if (debug()) nextp->dumpTree("- comb2: "); + // UINFOTREE(1, nodep, "", "comb1"); + // UINFOTREE(1, nextp, "", "comb2"); AstNodeExpr* const rhs1p = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const rhs2p = nextp->rhsp()->unlinkFrBack(); AstNodeAssign* newp; @@ -2049,7 +2049,7 @@ class ConstVisitor final : public VNVisitor { sel2p->lsbConst(), sel1p->width() + sel2p->width()}, new AstConcat{rhs1p->fileline(), rhs1p, rhs2p}); } - // if (debug()) pnewp->dumpTree("- conew: "); + // UINFOTREE(1, pnewp, "", "conew"); nodep->replaceWith(newp); // dypep intentionally changing VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBack()), nextp); @@ -2133,7 +2133,7 @@ class ConstVisitor final : public VNVisitor { // ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})), // ASSIGN(lc2,SEL(newrhs,{size})) } - if (debug() >= 9) nodep->dumpTree("- Ass_old: "); + UINFOTREE(9, nodep, "", "Ass_old"); // Unlink the stuff AstNodeExpr* const lc1p = VN_AS(nodep->lhsp(), Concat)->lhsp()->unlinkFrBack(); AstNodeExpr* const lc2p = VN_AS(nodep->lhsp(), Concat)->rhsp()->unlinkFrBack(); @@ -2369,7 +2369,7 @@ class ConstVisitor final : public VNVisitor { return true; } void replaceBoolShift(AstNode* nodep) { - if (debug() >= 9) nodep->dumpTree("- bshft_old: "); + UINFOTREE(9, nodep, "", "bshft_old"); AstConst* const andConstp = VN_AS(VN_AS(nodep, And)->lhsp(), Const); AstNodeExpr* const fromp = VN_AS(VN_AS(nodep, And)->rhsp(), ShiftR)->lhsp()->unlinkFrBack(); @@ -2383,7 +2383,7 @@ class ConstVisitor final : public VNVisitor { newp->dtypeSetLogicSized(nodep->width(), VSigning::UNSIGNED); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); - if (debug() >= 9) newp->dumpTree("- _new: "); + UINFOTREE(9, newp, "", "_new"); } void replaceWithSimulation(AstNode* nodep) { @@ -2796,7 +2796,7 @@ class ConstVisitor final : public VNVisitor { UASSERT_OBJ(nodep->varp(), nodep, "Not linked"); bool did = false; if (m_doV && nodep->varp()->valuep() && !m_attrp) { - // if (debug()) valuep->dumpTree("- visitvaref: "); + // UINFOTREE(1, valuep, "", "visitvaref"); iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep() AstNode* const valuep = nodep->varp()->valuep(); if (nodep->access().isReadOnly() @@ -2859,7 +2859,7 @@ class ConstVisitor final : public VNVisitor { UASSERT_OBJ(nodep->itemp(), nodep, "Not linked"); bool did = false; if (nodep->itemp()->valuep()) { - // if (debug()) nodep->itemp()->valuep()->dumpTree("- visitvaref: "); + // UINFOTREE(1, nodep->itemp()->valuep(), "", "visitvaref"); if (nodep->itemp()->user4()) { nodep->v3error("Recursive enum value: " << nodep->itemp()->prettyNameQ()); } else { @@ -3003,7 +3003,7 @@ class ConstVisitor final : public VNVisitor { void visit(AstSenTree* nodep) override { iterateChildren(nodep); if (m_doExpensive) { - // if (debug()) nodep->dumpTree("- ssin: "); + // UINFOTREE(1, nodep, "", "ssin"); // Optimize ideas for the future: // SENTREE(... SENGATE(x,a), SENGATE(SENITEM(x),b) ...) => SENGATE(x,OR(a,b)) @@ -3952,7 +3952,7 @@ uint32_t ConstVisitor::s_globalPassNum = 0; //! Force this cell node's parameter list to become a constant //! @return Pointer to the edited node. AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { - // if (debug() > 0) nodep->dumpTree("- forceConPRE : "); + // UINFOTREE(1, nodep, "", "forceConPRE"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized yet. @@ -3968,14 +3968,14 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { nodep = visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump if have next line - // if (debug() > 0) nodep->dumpTree("- forceConDONE: "); + // UINFOTREE(1, nodep, "", "forceConDONE"); return nodep; } //! Constify this cell node's parameter list if possible //! @return Pointer to the edited node. AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { - // if (debug() > 0) nodep->dumpTree("- forceConPRE : "); + // UINFOTREE(1, nodep, "", "forceConPRE"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized yet. @@ -3991,7 +3991,7 @@ AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { nodep = visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump this. - // if (debug() > 0) nodep->dumpTree("- forceConDONE: "); + // UINFOTREE(1, nodep, "", "forceConDONE"); return nodep; } @@ -4004,7 +4004,7 @@ AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { //! width check. //! @return Pointer to the edited node. AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { - // if (debug() > 0) nodep->dumpTree("- forceConPRE:: "); + // UINFOTREE(1, nodep, "", "forceConPRE:"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized // yet. @@ -4021,7 +4021,7 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { nodep = visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump this. - // if (debug() > 0) nodep->dumpTree("- forceConDONE: "); + // UINFOTREE(1, nodep, "", "forceConDONE"); return nodep; } diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 96994fe66..7ff71ecdd 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -48,7 +48,7 @@ class DepthVisitor final : public VNVisitor { void createDeepTemp(AstNodeExpr* nodep) { UINFO(6, " Deep " << nodep); - // if (debug() >= 9) nodep->dumpTree("- deep: "); + // UINFOTREE(9, nodep, "", "deep"); AstVar* const varp = new AstVar{nodep->fileline(), VVarType::STMTTEMP, m_tempNames.get(nodep), nodep->dtypep()}; if (m_cfuncp) { diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index f4f54ee8c..cbd5bd771 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -89,11 +89,11 @@ class DepthBlockVisitor final : public VNVisitor { if (m_depth > v3Global.opt.compLimitBlocks()) { // Already done UINFO(4, "DeepBlocks " << m_depth << " " << nodep); const AstNode* const backp = nodep->backp(); // Only for debug - if (debug() >= 9) backp->dumpTree("- pre : "); + UINFOTREE(9, backp, "", "pre "); AstCFunc* const funcp = createDeepFunc(nodep); iterate(funcp); - if (debug() >= 9) backp->dumpTree("- post: "); - if (debug() >= 9) funcp->dumpTree("- func: "); + UINFOTREE(9, backp, "", "post"); + UINFOTREE(9, funcp, "", "func"); } else { iterateChildren(nodep); } diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 0545551ae..8d670b967 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -195,7 +195,7 @@ class DescopeVisitor final : public VNVisitor { // "%%Error: "s+name+"() called with bad // scope", nullptr)); // newfuncp->addStmtsp(new AstStop(newfuncp->fileline())); - if (debug() >= 9) newfuncp->dumpTree("- newfunc: "); + UINFOTREE(9, newfuncp, "", "newfunc"); } else { // Only a single function under this name, we can rename it UINFO(6, " Wrapping " << name << " just one " << topFuncp); diff --git a/src/V3Error.h b/src/V3Error.h index 6706e4891..4e2eadb64 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -643,11 +643,14 @@ void v3errorEndFatal(std::ostringstream& sstr) do { std::cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; } while (false) /// Based on debug level, call UINFO then dumpTree on nodep, using given message prefix +/// If uinfo_msg = "", suppress the first UINFO /// If dt_msg = "", use the uinfo_msg; note any uinfo_msg side effects will happen twice. #define UINFOTREE(level, nodep, uinfo_msg, dt_msg) \ do { \ if (VL_UNCOVERABLE(debug() >= (level))) { \ - UINFO(level, uinfo_msg); \ + std::ostringstream us; \ + us << uinfo_msg; \ + if (!us.str().empty()) UINFO(level, us.str()); \ std::ostringstream ss; \ ss << dt_msg; \ if (ss.str().empty()) ss << uinfo_msg; \ diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index e7368142f..578799b04 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -867,7 +867,7 @@ class GateInline final { if (!canInline) continue; // Cannot optimize this replacement - if (debug() >= 9) dstVtxp->nodep()->dumpTree(" inside: "); + UINFOTREE(9, dstVtxp->nodep(), "", "inside"); if (logicp == dstVtxp->nodep()) { // This is a bit involved. The graph tells us that the logic is circular diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 37c6f73d1..d998bb922 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -365,8 +365,7 @@ class InlineRelinkVisitor final : public VNVisitor { const string name = m_cellp->name() + "__DOT__" + nodep->name(); if (!nodep->isFuncLocal() && !nodep->isClassMember()) nodep->inlineAttrReset(name); if (!m_cellp->isTrace()) nodep->trace(false); - if (debug() >= 9) nodep->dumpTree("- varchanged: "); - if (debug() >= 9 && nodep->valuep()) nodep->valuep()->dumpTree("- varchangei: "); + UINFOTREE(9, nodep, "", "varchanged"); } void visit(AstNodeFTask* nodep) override { // Function under the inline cell, need to rename to avoid conflicts diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 4fc415479..e53d9f8c6 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -59,7 +59,7 @@ class InstVisitor final : public VNVisitor { V3Inst::pinReconnectSimple(nodep, m_cellp, false); } if (!nodep->exprp()) return; // No-connect - if (debug() >= 9) nodep->dumpTree("- Pin_oldb: "); + UINFOTREE(9, nodep, "", "Pin_oldb"); V3Inst::checkOutputShort(nodep); // Use user1p on the PIN to indicate we created an assign for this pin if (!nodep->user1SetOnce()) { @@ -83,7 +83,7 @@ class InstVisitor final : public VNVisitor { m_cellp->name(), VAccess::WRITE}, exprp}; m_cellp->addNextHere(assp); - if (debug() >= 9) assp->dumpTree("- _new: "); + UINFOTREE(9, assp, "", "_new"); } else if (nodep->modVarp()->isIfaceRef() || (VN_IS(nodep->modVarp()->dtypep()->skipRefp(), UnpackArrayDType) && VN_IS(VN_AS(nodep->modVarp()->dtypep()->skipRefp(), UnpackArrayDType) @@ -642,7 +642,7 @@ public: // Done. Constant. } else { // Make a new temp wire - // if (1 || debug() >= 9) pinp->dumpTree("- in_pin: "); + // UINFOTREE(9, pinp, "", "in_pin"); V3Inst::checkOutputShort(pinp); AstNodeExpr* const pinexprp = VN_AS(pinp->exprp(), NodeExpr)->unlinkFrBack(); const string newvarname @@ -674,8 +674,8 @@ public: pinp->exprp(new AstVarRef{pinexprp->fileline(), newvarp, VAccess::READ}); } if (assignp) cellp->addNextHere(assignp); - // if (debug()) pinp->dumpTree("- out: "); - // if (debug()) assignp->dumpTree("- aout: "); + // UINFOTREE(1, pinp, "", "out"); + // UINFOTREE(1, assignp, "", "aout"); } return assignp; } diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 1cdd0c17d..c51d9d216 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -152,7 +152,7 @@ public: // Redundant assignment, in same level block // Don't delete it now as it will confuse iteration since it maybe WAY // above our current iteration point. - if (debug() > 4) oldassp->dumpTree("- REMOVE/SAMEBLK: "); + UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK"); entp->complexAssign(); oldassp->unlinkFrBack(); if (VN_IS(oldassp, CReset)) { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index d41eef7d7..7df735e67 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2210,7 +2210,7 @@ class LinkDotScopeVisitor final : public VNVisitor { 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: "); + UINFOTREE(9, nodep, "", "alias"); AstVarScope* const fromVscp = VN_AS(nodep->lhsp(), VarRef)->varScopep(); AstVarScope* const toVscp = VN_AS(nodep->rhsp(), VarRef)->varScopep(); UASSERT_OBJ(fromVscp && toVscp, nodep, "Bad alias scopes"); @@ -2219,7 +2219,7 @@ class LinkDotScopeVisitor final : public VNVisitor { } void visit(AstAssignVarScope* nodep) override { // ScopeVisitor:: UINFO(5, "ASSIGNVARSCOPE " << nodep); - if (debug() >= 9) nodep->dumpTree("- avs: "); + UINFOTREE(9, nodep, "", "avs"); VSymEnt* rhsSymp; { AstVarRef* const refp = VN_CAST(nodep->rhsp(), VarRef); @@ -2852,8 +2852,8 @@ class LinkDotResolveVisitor final : public VNVisitor { else { m_pinSymp = m_statep->getNodeSym(nodep->modp()); UINFO(4, indent() << "(Backto) visit " << nodep); - // if (debug()) nodep->dumpTree("- linkcell: "); - // if (debug()) nodep->modp()->dumpTree("- linkcemd: "); + // UINFOTREE(1, nodep, "", "linkcell"); + // UINFOTREE(1, nodep->modp(), "", "linkcemd"); iterateChildren(nodep); } } @@ -2875,8 +2875,8 @@ class LinkDotResolveVisitor final : public VNVisitor { // ClassRef's have pins, so track m_pinSymp = m_statep->getNodeSym(nodep->classp()); UINFO(4, indent() << "(Backto) visit " << nodep); - // if (debug()) nodep->dumpTree("- linkcell: "); - // if (debug()) nodep->modp()->dumpTree("- linkcemd: "); + // UINFOTREE(1, nodep, "", "linkcell"); + // UINFOTREE(1, nodep->modp(), "", "linkcemd"); iterateChildren(nodep); } } @@ -2958,7 +2958,7 @@ class LinkDotResolveVisitor final : public VNVisitor { VL_RESTORER(m_randSymp); { if (start) { // Starting dot sequence - if (debug() >= 9) nodep->dumpTree("- dot-in: "); + UINFOTREE(9, nodep, "", "dot-in"); m_ds.init(m_curSymp); // Start from current point } m_ds.m_dotp = nodep; // Always, not just at start @@ -3057,7 +3057,7 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(8, indent() << "iter.lhs " << m_ds.ascii() << " " << nodep); iterateAndNextNull(nodep->lhsp()); UINFO(8, indent() << "iter.ldone " << m_ds.ascii() << " " << nodep); - // if (debug() >= 9) nodep->dumpTree("- dot-lho: "); + // UINFOTREE(9, nodep, "", "dot-lho"); } if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) { // Dots of paramed classes will be linked after deparameterization @@ -3077,7 +3077,7 @@ class LinkDotResolveVisitor final : public VNVisitor { // m_ds.m_dotSymp points at lhsp()'s symbol table, so resolve RHS under that iterateAndNextNull(nodep->rhsp()); UINFO(8, indent() << "iter.rdone " << m_ds.ascii() << " " << nodep); - // if (debug() >= 9) nodep->dumpTree("- dot-rho: "); + // UINFOTREE(9, nodep, "", "dot-rho"); } if (!m_ds.m_unresolvedClass) { if (start) { @@ -3087,7 +3087,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } else { newp = nodep->rhsp()->unlinkFrBack(); } - if (debug() >= 9) newp->dumpTree("- dot-out: "); + UINFOTREE(9, newp, "", "dot-out"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // Dot midpoint diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index bb17df158..f8c08fb51 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -81,7 +81,7 @@ class LinkIncVisitor final : public VNVisitor { } void insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any - if (debug() >= 9) newp->dumpTree("- newstmt: "); + UINFOTREE(9, newp, "", "newstmt"); UASSERT_OBJ(m_insStmtp, nodep, "Expression not underneath a statement"); // In a while condition, the statement also needs to go on the // back-edge to the loop header, 'incsp' is that place. @@ -209,7 +209,7 @@ class LinkIncVisitor final : public VNVisitor { } void prepost_stmt_sel_visit(AstNodeTriop* nodep) { // Special case array[something]++, see comments at file top - // if (debug() >= 9) nodep->dumpTree("-pp-stmt-sel-in: "); + // UINFOTREE(9, nodep, "", "pp-stmt-sel-in"); iterateChildren(nodep); AstConst* const constp = VN_AS(nodep->lhsp(), Const); UASSERT_OBJ(nodep, constp, "Expecting CONST"); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 9694dc6c5..c6506f530 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()); } - // if (debug()) letp->dumpTree("-let-let "); - // if (debug()) nodep->dumpTree("-let-ref "); + // UINFOTREE(1, letp, "", "let-let"); + // UINFOTREE(1, nodep, "", "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); } }); - // if (debug()) newp->dumpTree("-let-new "); + // UINFOTREE(1, newp, "", "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/V3Param.cpp b/src/V3Param.cpp index 4eba561cc..7bf2a98b3 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -745,7 +745,7 @@ class ParamProcessor final { AstConst* const exprp = VN_CAST(pinp->exprp(), Const); AstConst* const origp = VN_CAST(modvarp->valuep(), Const); if (!exprp) { - if (debug()) pinp->dumpTree("- "); + UINFOTREE(1, pinp, "", "errnode"); pinp->v3error("Can't convert defparam value to constant: Param " << pinp->prettyNameQ() << " of " << nodep->prettyNameQ()); pinp->exprp()->replaceWith(new AstConst{ @@ -884,9 +884,9 @@ class ParamProcessor final { AstPin* pinsp, bool any_overrides) { // Make sure constification worked // Must be a separate loop, as constant conversion may have changed some pointers. - // if (debug()) nodep->dumpTree("- cel2: "); + // UINFOTREE(1, nodep, "", "cel2"); string longname = srcModpr->name() + "_"; - if (debug() > 8 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: "); + if (debug() >= 9 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: "); if (srcModpr->hierBlock()) { longname = parameterizedHierBlockName(srcModpr, paramsp); @@ -991,7 +991,7 @@ public: // and remove any recursive references UINFO(4, "De-parameterize: " << nodep); // Create new module name with _'s between the constants - if (debug() >= 10) nodep->dumpTree("- cell: "); + UINFOTREE(10, nodep, "", "cell"); // Evaluate all module constants V3Const::constifyParamsEdit(nodep); // Set name for warnings for when we param propagate the module diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index decadd40e..ecf20d1d4 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -2191,7 +2191,7 @@ class RandomizeVisitor final : public VNVisitor { // else warning // Note this code assumes that the expressions after V3Const are fast to compute // Optimize: we would be better with a binary search tree to reduce ifs that execute - if (debug() >= 9) nodep->dumpTree("- rcin:: "); + UINFOTREE(9, nodep, "", "rcin:"); AstNodeDType* const sumDTypep = nodep->findUInt64DType(); FileLine* const fl = nodep->fileline(); diff --git a/src/V3Reloop.cpp b/src/V3Reloop.cpp index 19133d323..99855dbf0 100644 --- a/src/V3Reloop.cpp +++ b/src/V3Reloop.cpp @@ -121,8 +121,8 @@ class ReloopVisitor final : public VNVisitor { rbitp->replaceWith(m_mgOffset < 0 ? new AstAdd{fl, rvrefp, offsetp} : rvrefp); VL_DO_DANGLING(rbitp->deleteTree(), lbitp); } - if (debug() >= 9) initp->dumpTree("- new: "); - if (debug() >= 9) whilep->dumpTree("- new: "); + UINFOTREE(9, initp, "", "new"); + UINFOTREE(9, whilep, "", "new"); // Remove remaining assigns for (AstNodeAssign* assp : m_mgAssignps) { diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 516af3400..190b3580f 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -739,7 +739,7 @@ private: // Values are in the "real" tree under the InitArray so can eventually extract it, // Not in the usual setValue (via m_varAux) initp->addIndexValuep(index, valuep); - if (debug() >= 9) initp->dumpTree("- array: "); + UINFOTREE(9, initp, "", "array"); assignOutValue(nodep, vscp, initp); } } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index b96d980ad..685334575 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -272,10 +272,10 @@ class SliceVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(exprp), exprp); }); } - if (debug() >= 9) newp->dumpTree("- new: "); + UINFOTREE(9, newp, "", "new"); newlistp = AstNode::addNext(newlistp, newp); } - if (debug() >= 9) nodep->dumpTree("- Deslice-Dn: "); + UINFOTREE(9, nodep, "", "Deslice-Dn"); nodep->replaceWith(newlistp); VL_DO_DANGLING(nodep->deleteTree(), nodep); // Normal edit iterator will now iterate on all of the expansion assignments @@ -287,7 +287,7 @@ class SliceVisitor final : public VNVisitor { // Called recursively on newly created assignments if (nodep->user1SetOnce()) return; // Process once if (VN_IS(nodep, AssignAlias)) return; - if (debug() >= 9) nodep->dumpTree("- Deslice-In: "); + UINFOTREE(9, nodep, "", "Deslice-In"); VL_RESTORER(m_assignError); VL_RESTORER(m_assignp); VL_RESTORER(m_okInitArray); // Set in assignOptimize diff --git a/src/V3Split.cpp b/src/V3Split.cpp index 323ebd5b3..dca6a21f2 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -598,10 +598,10 @@ protected: void visit(AstAlways* nodep) override { UINFO(4, " ALW " << nodep); - if (debug() >= 9) nodep->dumpTree("- alwIn:: "); + UINFOTREE(9, nodep, "", "alwIn:"); scoreboardClear(); processBlock(nodep->stmtsp()); - if (debug() >= 9) nodep->dumpTree("- alwOut: "); + UINFOTREE(9, nodep, "", "alwOut"); } void visit(AstNodeIf* nodep) override { diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp index 4f29d6767..c5c5fb5fd 100644 --- a/src/V3SplitAs.cpp +++ b/src/V3SplitAs.cpp @@ -131,7 +131,7 @@ class SplitAsVisitor final : public VNVisitor { // METHODS void splitAlways(AstAlways* nodep, AstVarScope* splitVscp) { - if (debug() >= 9) nodep->dumpTree("- in: "); + UINFOTREE(9, nodep, "", "in"); // Duplicate it and link in // Below cloneTree should perhaps be cloneTreePure, but given // isolate_assignments is required to hit this code, we presume the user @@ -141,11 +141,11 @@ class SplitAsVisitor final : public VNVisitor { nodep->addNextHere(newp); { // Delete stuff we don't want in old const SplitAsCleanVisitor visitor{nodep, splitVscp, false}; - if (debug() >= 9) nodep->dumpTree("- out0: "); + UINFOTREE(9, nodep, "", "out0"); } { // Delete stuff we don't want in new const SplitAsCleanVisitor visitor{newp, splitVscp, true}; - if (debug() >= 9) newp->dumpTree("- out1: "); + UINFOTREE(9, newp, "", "out1"); } } diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index 98ceea886..f86e4fa8c 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -284,12 +284,12 @@ class SubstVisitor final : public VNVisitor { if (!hit) iterate(nodep->lhsp()); } void replaceSubstEtc(AstNode* nodep, AstNodeExpr* substp) { - if (debug() > 5) nodep->dumpTree("- substw_old: "); + UINFOTREE(6, nodep, "", "substw_old"); AstNodeExpr* newp = substp->cloneTreePure(true); if (!nodep->isQuad() && newp->isQuad()) { newp = new AstCCast{newp->fileline(), newp, nodep}; } - if (debug() > 5) newp->dumpTree("- w_new: "); + UINFOTREE(6, newp, "", "w_new"); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); ++m_statSubsts; diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 74fd45945..3f9fabd4b 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -275,7 +275,7 @@ private: // Keep sensitivity list, but delete all else nodep->stmtsp()->unlinkFrBackWithNext()->deleteTree(); nodep->addStmtsp(stmtsp); - if (debug() >= 6) nodep->dumpTree("- table_new: "); + UINFOTREE(6, nodep, "", "table_new"); } void createTables(AstAlways* nodep, TableBuilder& outputAssignedTableBuilder) { diff --git a/src/V3Task.cpp b/src/V3Task.cpp index e7cfc5357..3cab796a2 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -530,7 +530,7 @@ class TaskVisitor final : public VNVisitor { } } } else if (portp->isInout()) { - // if (debug() >= 9) pinp->dumpTree("-pinrsize- "); + // UINFOTREE(9, pinp, "", "pinrsize-"); AstVarScope* const newvscp = createVarScope(portp, namePrefix + "__" + portp->shortName()); @@ -1411,7 +1411,7 @@ class TaskVisitor final : public VNVisitor { // Delete rest of cloned task and return new func VL_DO_DANGLING(pushDeletep(nodep), nodep); - if (debug() >= 9) cfuncp->dumpTree("- userFunc: "); + UINFOTREE(9, cfuncp, "", "userFunc"); return cfuncp; } @@ -1424,9 +1424,9 @@ class TaskVisitor final : public VNVisitor { iterate(nodep); } void insertBeforeStmt(AstNode* nodep, AstNode* newp) { - if (debug() >= 9) nodep->dumpTree("- newstmt: "); + UINFOTREE(9, nodep, "", "newstmt"); UASSERT_OBJ(m_insStmtp, nodep, "Function call not underneath a statement"); - if (debug() >= 9) newp->dumpTree("- newfunc: "); + UINFOTREE(9, newp, "", "newfunc"); m_insStmtp->addHereThisAsNext(newp); } @@ -1464,7 +1464,7 @@ class TaskVisitor final : public VNVisitor { UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked?"); iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs UINFO(4, " FTask REF " << nodep); - if (debug() >= 9) nodep->dumpTree("- inlfunc: "); + UINFOTREE(9, nodep, "", "inlfunc"); UASSERT_OBJ(m_scopep, nodep, "func ref not under scope"); const string namePrefix = ((VN_IS(nodep, FuncRef) ? "__Vfunc_" : "__Vtask_") + nodep->taskp()->shortName() + "__" + cvtToStr(m_modNCalls++)); @@ -1853,7 +1853,7 @@ void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconn // Change outside call to connect to new function nodep->taskp(newTaskp); nodep->name(newTaskp->name()); - // if (debug() >= 9) nodep->dumpTree("-taskConnectWrap-call "); + // UINFOTREE(9, nodep, "", "taskConnectWrap-call"); } AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newname, @@ -1928,7 +1928,7 @@ AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newn const auto it = oldNewVars.find(refp->varp()); if (it != oldNewVars.end()) refp->varp(it->second); }); - // if (debug() >= 9) newTaskp->dumpTree("-taskConnectWrap-new "); + // UINFOTREE(9, newTaskp, "", "taskConnectWrap-new"); return newTaskp; } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index cbc8833f4..1a14843f4 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -309,7 +309,7 @@ class TraceVisitor final : public VNVisitor { if (TraceTraceVertex* const vtxp = vtx.cast()) { ActCodeSet actSet; UINFO(9, " Add to sort: " << vtxp); - if (debug() >= 9) vtxp->nodep()->dumpTree("- trnode: "); + UINFOTREE(9, vtxp->nodep(), "", "trnode"); for (const V3GraphEdge& edge : vtxp->inEdges()) { const TraceActivityVertex* const cfvertexp = edge.fromp()->cast(); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index be40fec9a..36ebdd123 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -822,7 +822,7 @@ class TristateVisitor final : public TristateBaseVisitor { if (envarp) { AstAssignW* const enAssp = new AstAssignW{ enp->fileline(), new AstVarRef{envarp->fileline(), envarp, VAccess::WRITE}, enp}; - if (debug() >= 9) enAssp->dumpTree("- enAssp: "); + UINFOTREE(9, enAssp, "", "enAssp"); nodep->addStmtsp(enAssp); } @@ -830,7 +830,7 @@ class TristateVisitor final : public TristateBaseVisitor { AstNode* const assp = new AstAssignW{ lhsp->fileline(), new AstVarRef{lhsp->fileline(), lhsp, VAccess::WRITE}, orp}; assp->user2(U2_BOTH); // Don't process further; already resolved - if (debug() >= 9) assp->dumpTree("- lhsp-eqn: "); + UINFOTREE(9, assp, "", "lhsp-eqn"); nodep->addStmtsp(assp); // If this is a top-level inout, make sure that the INOUT pins get __en and __out @@ -1145,7 +1145,7 @@ class TristateVisitor final : public TristateBaseVisitor { AstNodeExpr* const newp = newEnableDeposit(nodep, VN_AS(nodep->user1p(), NodeExpr)); nodep->fromp()->user1p(newp); // Push to varref (etc) - if (debug() >= 9) newp->dumpTree("- assign-sel: "); + UINFOTREE(9, newp, "", "assign-sel"); m_tgraph.didProcess(nodep); } iterateChildren(nodep); @@ -1223,7 +1223,7 @@ class TristateVisitor final : public TristateBaseVisitor { associateLogic(nodep->rhsp(), nodep); m_tgraph.setTristate(nodep); } else { - if (debug() >= 9) nodep->backp()->dumpTree("- bufif: "); + UINFOTREE(9, nodep->backp(), "", "bufif"); if (m_alhs) { nodep->v3warn(E_UNSUPPORTED, "Unsupported LHS tristate construct: " << nodep->prettyTypeName()); @@ -1328,7 +1328,7 @@ class TristateVisitor final : public TristateBaseVisitor { nodep->user2(U2_NONGRAPH); iterateAndNextNull(nodep->rhsp()); UINFO(9, dbgState() << nodep); - if (debug() >= 9) nodep->dumpTree("- assign: "); + UINFOTREE(9, nodep, "", "assign"); // if the rhsp of this assign statement has an output enable driver, // then propagate the corresponding output enable assign statement. // down the lvalue tree by recursion for eventual attachment to @@ -1398,8 +1398,8 @@ class TristateVisitor final : public TristateBaseVisitor { new AstEqCase{fl, new AstConst{fl, oneIfEnOne}, rhsp}}; if (neq) newp = new AstLogNot{fl, newp}; UINFO(9, " newceq " << newp); - if (debug() >= 9) nodep->dumpTree("- caseeq-old: "); - if (debug() >= 9) newp->dumpTree("- caseeq-new: "); + UINFOTREE(9, nodep, "", "caseeq-old"); + UINFOTREE(9, newp, "", "caseeq-new"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (constp && nodep->rhsp()->user1p()) { @@ -1414,8 +1414,8 @@ class TristateVisitor final : public TristateBaseVisitor { if (neq) newp = new AstLogNot{fl, newp}; rhsp->user1p(nullptr); UINFO(9, " newceq " << newp); - if (debug() >= 9) nodep->dumpTree("- caseeq-old: "); - if (debug() >= 9) newp->dumpTree("- caseeq-new: "); + UINFOTREE(9, nodep, "", "caseeq-old"); + UINFOTREE(9, newp, "", "caseeq-new"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { @@ -1495,7 +1495,7 @@ class TristateVisitor final : public TristateBaseVisitor { } newp = new AstAdd{nodep->fileline(), nodep, newp}; } - if (debug() >= 9) newp->dumpTree("- countout: "); + UINFOTREE(9, newp, "", "countout"); relinkHandle.relink(newp); } iterateChildren(nodep); @@ -1511,7 +1511,7 @@ class TristateVisitor final : public TristateBaseVisitor { varrefp = VN_AS(VN_AS(nodep->lhsp(), Sel)->fromp(), VarRef); } if (!varrefp) { - if (debug() >= 4) nodep->dumpTree("- "); + UINFOTREE(4, nodep, "", ""); nodep->v3warn(E_UNSUPPORTED, "Unsupported pullup/down (weak driver) construct."); } else { if (m_graphing) { @@ -1591,7 +1591,7 @@ class TristateVisitor final : public TristateBaseVisitor { } // Tristate exists: UINFO(9, dbgState() << nodep); - if (debug() >= 9) nodep->dumpTree("- pin-pre: "); + UINFOTREE(9, nodep, "", "pin-pre"); // Empty/in-only; need Z to propagate const bool inDeclProcessing = (nodep->exprp() @@ -1646,7 +1646,7 @@ class TristateVisitor final : public TristateBaseVisitor { m_modp->addStmtsp(enVarp); enrefp = new AstVarRef{nodep->fileline(), enVarp, VAccess::READ}; UINFO(9, " newvrf " << enrefp); - if (debug() >= 9) enpinp->dumpTree("- pin-ena: "); + UINFOTREE(9, enpinp, "", "pin-ena"); } // Create new output pin const AstAssignW* outAssignp = nullptr; // If reconnected, the related assignment @@ -1677,11 +1677,11 @@ class TristateVisitor final : public TristateBaseVisitor { // a VarRef without any ArraySel, etc TristatePinVisitor{outexprp, m_tgraph, true}; } - if (debug() >= 9) outpinp->dumpTree("- pin-opr: "); + UINFOTREE(9, outpinp, "", "pin-opr"); outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, true); // Note may change outpinp->exprp() - if (debug() >= 9) outpinp->dumpTree("- pin-out: "); - if (debug() >= 9 && outAssignp) outAssignp->dumpTree("- pin-out: "); + UINFOTREE(9, outpinp, "", "pin-out"); + UINFOTREE(9, outAssignp, "", "pin-oas"); // Must still iterate the outAssignp, as need to build output equation } @@ -1689,8 +1689,8 @@ class TristateVisitor final : public TristateBaseVisitor { const TristatePinVisitor visitor{nodep->exprp(), m_tgraph, false}; const AstNode* const inAssignp = V3Inst::pinReconnectSimple( nodep, m_cellp, true); // Note may change nodep->exprp() - if (debug() >= 9) nodep->dumpTree("- pin-in:: "); - if (debug() >= 9 && inAssignp) inAssignp->dumpTree("- pin-as:: "); + UINFOTREE(9, nodep, "", "pin-in:"); + UINFOTREE(9, inAssignp, "", "pin-as:"); // Connect enable to output signal AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 2536862ac..42fb8d822 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -134,7 +134,7 @@ class UnknownVisitor final : public VNVisitor { fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}))}; newp->branchPred(VBranchPred::BP_LIKELY); newp->isBoundsCheck(true); - if (debug() >= 9) newp->dumpTree("- _new: "); + UINFOTREE(9, newp, "", "_new"); abovep->addNextStmt(newp, abovep); prep->user2p(newp); // Save so we may LogAnd it next time } @@ -319,7 +319,7 @@ class UnknownVisitor final : public VNVisitor { void visit(AstConst* nodep) override { if (m_constXCvt && nodep->num().isFourState()) { UINFO(4, " CONST4 " << nodep); - if (debug() >= 9) nodep->dumpTree("- Const_old: "); + UINFOTREE(9, nodep, "", "Const_old"); // CONST(num) -> VARREF(newvarp) // -> VAR(newvarp) // -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X)) @@ -368,9 +368,9 @@ class UnknownVisitor final : public VNVisitor { m_modp->addStmtsp(newvarp); m_modp->addStmtsp(newinitp); m_modp->addStmtsp(afterp); - if (debug() >= 9) newref1p->dumpTree("- _new: "); - if (debug() >= 9) newvarp->dumpTree("- _new: "); - if (debug() >= 9) newinitp->dumpTree("- _new: "); + UINFOTREE(9, newref1p, "", "_newref"); + UINFOTREE(9, newvarp, "", "_newvar"); + UINFOTREE(9, newinitp, "", "_newini"); VL_DO_DANGLING(nodep->deleteTree(), nodep); } } @@ -388,7 +388,7 @@ class UnknownVisitor final : public VNVisitor { // Find range of dtype we are selecting from // Similar code in V3Const::warnSelect const int maxmsb = nodep->fromp()->dtypep()->width() - 1; - if (debug() >= 9) nodep->dumpTree("- sel_old: "); + UINFOTREE(9, nodep, "", "sel_old"); // If (maxmsb >= selected), we're in bound AstNodeExpr* condp @@ -412,7 +412,7 @@ class UnknownVisitor final : public VNVisitor { AstNodeExpr* const newp = condp->isZero() ? xexprp : new AstCondBound{nodep->fileline(), condp, nodep, xexprp}; - if (debug() >= 9) newp->dumpTree("- _new: "); + UINFOTREE(9, newp, "", "_new"); // Link in conditional replaceHandle.relink(newp); // Added X's, tristate them too @@ -429,7 +429,7 @@ class UnknownVisitor final : public VNVisitor { void visit(AstArraySel* nodep) override { iterateChildren(nodep); if (!nodep->user1SetOnce()) { - if (debug() == 9) nodep->dumpTree("- in: "); + UINFOTREE(9, nodep, "", "in"); // Guard against reading/writing past end of arrays AstNode* const basefromp = AstArraySel::baseFromp(nodep->fromp(), true); bool lvalue = false; @@ -447,7 +447,7 @@ class UnknownVisitor final : public VNVisitor { const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType); UASSERT_OBJ(adtypep, nodep, "Select from non-array " << dtypep->prettyTypeName()); declElements = adtypep->elementsConst(); - if (debug() >= 9) nodep->dumpTree("- arraysel_old: "); + UINFOTREE(9, nodep, "", "arraysel_old"); // If value MODDIV constant, where constant <= declElements, known ok // V3Random makes these to intentionally prevent exceeding enum array bounds. @@ -487,7 +487,7 @@ class UnknownVisitor final : public VNVisitor { } AstNode* const newp = new AstCondBound{nodep->fileline(), condp, nodep, new AstConst{nodep->fileline(), xnum}}; - if (debug() >= 9) newp->dumpTree("- _new: "); + UINFOTREE(9, newp, "", "_new"); // Link in conditional, can blow away temp xor replaceHandle.relink(newp); // Added X's, tristate them too @@ -500,7 +500,7 @@ class UnknownVisitor final : public VNVisitor { bitp->fileline(), condp, bitp, new AstConst{bitp->fileline(), AstConst::WidthedValue{}, bitp->width(), 0}}; // Added X's, tristate them too - if (debug() >= 9) newp->dumpTree("- _new: "); + UINFOTREE(9, newp, "", "_new"); replaceHandle.relink(newp); iterate(newp); } else { // lvalue diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 99e7aa89f..23084fe1e 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -68,7 +68,7 @@ class UnrollVisitor final : public VNVisitor { if (m_generate) nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason); UINFO(4, " Can't Unroll: " << reason << " :" << nodep); - // if (debug() >= 9) nodep->dumpTree("- cant: "); + // UINFOTREE(9, nodep, "", "cant"); V3Stats::addStatSum("Unrolling gave up, "s + reason, 1); return false; } @@ -158,7 +158,7 @@ class UnrollVisitor final : public VNVisitor { } else { UINFO(8, " Loop Variable: " << m_forVarp); } - if (debug() >= 9) nodep->dumpTree("- for: "); + UINFOTREE(9, nodep, "", "for"); if (!m_generate) { const AstAssign* const incpAssign = VN_AS(incp, Assign); @@ -219,7 +219,7 @@ class UnrollVisitor final : public VNVisitor { simvis.mainParamEmulate(clonep); if (!simvis.optimizable()) { UINFO(4, "Unable to simulate"); - if (debug() >= 9) nodep->dumpTree("- _simtree: "); + UINFOTREE(9, nodep, "", "_simtree"); VL_DO_DANGLING(clonep->deleteTree(), clonep); return false; } @@ -383,7 +383,7 @@ class UnrollVisitor final : public VNVisitor { if (bodysp) VL_DO_DANGLING(pushDeletep(bodysp), bodysp); if (initp) VL_DO_DANGLING(pushDeletep(initp), initp); if (incp && !incp->backp()) VL_DO_DANGLING(pushDeletep(incp), incp); - if (debug() >= 9 && newbodysp) newbodysp->dumpTree("- _new: "); + if (newbodysp) UINFOTREE(9, newbodysp, "", "_new"); return true; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a9bff2d59..7e2f4af83 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -911,6 +911,7 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeStream* nodep) override { VL_RESTORER(m_streamConcat); + UINFOTREE(1, nodep, "stream-in vup" << m_vup, "stream-in "); if (m_vup->prelim()) { m_streamConcat = true; iterateCheckSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); @@ -977,7 +978,7 @@ class WidthVisitor final : public VNVisitor { if (nodep->didWidth()) return; UASSERT_OBJ(m_vup, nodep, "Select under an unexpected context"); if (m_vup->prelim()) { - if (debug() >= 9) nodep->dumpTree("- selWidth: "); + UINFOTREE(9, nodep, "", "selWidth"); userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->lsbp(), WidthVP{SELF, PRELIM}.p()); checkCvtUS(nodep->fromp(), false); @@ -1095,7 +1096,7 @@ class WidthVisitor final : public VNVisitor { widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP, false /*NOWARN*/); } - // if (debug() >= 9) nodep->dumpTree("-seldone "); + // UINFOTREE(9, nodep, "", "seldone"); } } @@ -2131,8 +2132,8 @@ class WidthVisitor final : public VNVisitor { UINFO(9, "CAST " << nodep); nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); if (m_vup->prelim()) { - if (debug() >= 9) nodep->dumpTree("- CastPre: "); - // if (debug()) nodep->backp()->dumpTree("- CastPreUpUp: "); + UINFOTREE(9, nodep, "", "CastPre"); + // UINFOTREE(1, nodep->backp(), "", "CastPreUpUp"); if (AstSigned* const fromp = VN_CAST(nodep->fromp(), Signed)) { if (VN_IS(fromp->lhsp(), NodeStream)) { AstNode* const lhsp = fromp->lhsp()->unlinkFrBack(); @@ -2141,7 +2142,7 @@ class WidthVisitor final : public VNVisitor { } } userIterateAndNext(nodep->fromp(), WidthVP{SELF, PRELIM}.p()); - if (debug() >= 9) nodep->dumpTree("- CastDit: "); + UINFOTREE(9, nodep, "", "CastDit"); AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump(); AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp()); @@ -2237,20 +2238,20 @@ class WidthVisitor final : public VNVisitor { } if (!newp) newp = nodep->fromp()->unlinkFrBack(); nodep->fromp(newp); - if (debug() >= 9) nodep->dumpTree("- CastOut: "); - // if (debug()) nodep->backp()->dumpTree("- CastOutUpUp: "); + UINFOTREE(9, nodep, "", "CastOut"); + // UINFOTREE(1, nodep->backp(), "", "CastOutUpUp"); } if (m_vup->final()) { - if (debug() >= 9) nodep->dumpTree("- CastFPit: "); + UINFOTREE(9, nodep, "", "CastFPit"); iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(), EXTEND_EXP, false); - if (debug() >= 9) nodep->dumpTree("- CastFin: "); + UINFOTREE(9, nodep, "", "CastFin"); AstNodeExpr* const underp = nodep->fromp()->unlinkFrBack(); underp->dtypeFrom(nodep); underp->didWidth(true); AstNodeExpr* const newp = new AstCastWrap{nodep->fileline(), underp}; newp->didWidth(true); - if (debug() >= 9) newp->dumpTree("- CastRep: "); + UINFOTREE(9, newp, "", "CastRep"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2258,7 +2259,7 @@ class WidthVisitor final : public VNVisitor { void visit(AstCastSize* nodep) override { // IEEE: Signedness of result is same as self-determined signedness // However, the result is same as BITSEL, so we do not sign extend the LHS - // if (debug()) nodep->dumpTree("- CastSizePre: "); + // UINFOTREE(1, nodep, "", "CastSizePre"); if (m_vup->prelim()) { int width = nodep->rhsp()->toSInt(); if (width < 1) { @@ -2274,7 +2275,7 @@ class WidthVisitor final : public VNVisitor { nodep->replaceWithKeepDType(underp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } - // if (debug()) nodep->dumpTree("- CastSizeOut: "); + // UINFOTREE(1, nodep, "", "CastSizeOut"); } void visit(AstCastWrap* nodep) override { // Inserted by V3Width only so we know has been resolved @@ -2305,7 +2306,7 @@ class WidthVisitor final : public VNVisitor { VL_DANGLING(underp); underp = nodep->op1p(); // Above asserts that op1 was underp pre-relink } - // if (debug()) nodep->dumpTree("- CastSizeClc: "); + // UINFOTREE(1, nodep, "", "CastSizeClc"); // Next step, make the proper output width { AstNodeDType* const outDtp @@ -2337,7 +2338,7 @@ class WidthVisitor final : public VNVisitor { nodep->dtypep(nodep->rangep()->dtypep()); } void visit(AstVar* nodep) override { - // if (debug()) nodep->dumpTree("- InitPre: "); + // UINFOTREE(1, nodep, "", "InitPre"); // Must have deterministic constant width // We can't skip this step when width()!=0, as creating a AstVar // with non-constant range gets size 1, not size 0. So use didWidth(). @@ -2472,14 +2473,14 @@ class WidthVisitor final : public VNVisitor { << badDtp->warnContextSecondary()); } if (nodep->valuep() && !didchk) { - // if (debug()) nodep->dumpTree("- final: "); + // UINFOTREE(1, nodep, "", "final"); // AstPattern requires assignments to pass datatype on PRELIM userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p()); iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); } userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p()); UINFO(4, "varWidthed " << nodep); - // if (debug()) nodep->dumpTree("- InitOut: "); + // UINFOTREE(1, nodep, "", "InitOut"); nodep->didWidth(true); nodep->doingWidth(false); } @@ -2500,14 +2501,14 @@ class WidthVisitor final : public VNVisitor { // Var hasn't been widthed, so make it so. userIterate(nodep->varp(), nullptr); } - // if (debug()>=9) { nodep->dumpTree("- VRin:: "); - // nodep->varp()->dumpTree("- forvar: "); } + // UINFOTREE(9, nodep, "", "VRin"); + // UINFOTREE(9, nodep->varp(), "", "forvar"); // Note genvar's are also entered as integers nodep->dtypeFrom(nodep->varp()); if (VN_IS(nodep->backp(), NodeAssign) && nodep->access().isWriteOrRW()) { // On LHS UASSERT_OBJ(nodep->dtypep(), nodep, "LHS var should be dtype completed"); } - // if (debug() >= 9) nodep->dumpTree("- VRout: "); + // UINFOTREE(9, nodep, "", "VRout"); if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) { nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ()); } else if (nodep->access().isWriteOrRW() && nodep->varp()->isInput() @@ -2848,7 +2849,7 @@ class WidthVisitor final : public VNVisitor { } if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; - if (debug() >= 9) nodep->dumpTree("- dist-out: "); + UINFOTREE(9, nodep, "", "dist-out"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2895,7 +2896,7 @@ class WidthVisitor final : public VNVisitor { EXTEND_EXP); } - if (debug() >= 9) nodep->dumpTree("- inside-in: "); + UINFOTREE(9, nodep, "", "inside-in"); // Now rip out the inside and replace with simple math AstNodeExpr* newp = nullptr; for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { @@ -2905,7 +2906,7 @@ class WidthVisitor final : public VNVisitor { newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp; } if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; - if (debug() >= 9) newp->dumpTree("- inside-out: "); + UINFOTREE(9, newp, "", "inside-out"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2951,7 +2952,7 @@ class WidthVisitor final : public VNVisitor { void visit(AstNodeUOrStructDType* nodep) override { if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed UINFO(5, " NODEUORS " << nodep); - // if (debug() >= 9) nodep->dumpTree("- class-in: "); + // UINFOTREE(9, nodep, "", "class-in"); if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true); userIterateChildren(nodep, nullptr); // First size all members nodep->dtypep(nodep); @@ -3010,7 +3011,7 @@ class WidthVisitor final : public VNVisitor { } else { nodep->widthForce(1, 1); } - // if (debug() >= 9) nodep->dumpTree("- class-out: "); + // UINFOTREE(9, nodep, "", "class-out"); } void visit(AstClass* nodep) override { if (nodep->didWidthAndSet()) return; @@ -3088,9 +3089,9 @@ class WidthVisitor final : public VNVisitor { void visit(AstMemberSel* nodep) override { UINFO(5, " MEMBERSEL " << nodep); if (nodep->didWidth()) return; - if (debug() >= 9) nodep->dumpTree("- mbs-in: "); + UINFOTREE(9, nodep, "", "mbs-in"); userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); - if (debug() >= 9) nodep->dumpTree("- mbs-ic: "); + UINFOTREE(9, nodep, "", "mbs-ic"); // Find the fromp dtype - should be a class UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type"); AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); @@ -3335,7 +3336,7 @@ class WidthVisitor final : public VNVisitor { void visit(AstMethodCall* nodep) override { UINFO(5, " METHODCALL " << nodep); if (nodep->didWidth()) return; - if (debug() >= 9) nodep->dumpTree("- mts-in: "); + UINFOTREE(9, nodep, "", "mts-in"); // Should check types the method requires, but at present we don't do much userIterate(nodep->fromp(), WidthVP{SELF, BOTH}.p()); // Args are checked within each particular method's decode @@ -4869,7 +4870,7 @@ class WidthVisitor final : public VNVisitor { } else { nodep->v3error("Assignment pattern with no members"); } - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) { @@ -4898,7 +4899,7 @@ class WidthVisitor final : public VNVisitor { newp = newap; } nodep->replaceWith(newp); - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp, @@ -4918,7 +4919,7 @@ class WidthVisitor final : public VNVisitor { newp = newap; } nodep->replaceWith(newp); - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) { @@ -4937,7 +4938,7 @@ class WidthVisitor final : public VNVisitor { newp = newap; } nodep->replaceWith(newp); - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember*) { @@ -4958,7 +4959,7 @@ class WidthVisitor final : public VNVisitor { newp = newap; } nodep->replaceWith(newp); - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) { @@ -5007,7 +5008,7 @@ class WidthVisitor final : public VNVisitor { } else { nodep->v3error("Assignment pattern with no members"); } - // if (debug() >= 9) newp->dumpTree("- apat-out: "); + // UINFOTREE(9, newp, "", "apat-out"); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } AstNodeExpr* patternMemberValueIterate(AstPatMember* patp) { @@ -5240,13 +5241,13 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeIf* nodep) override { assertAtStatement(nodep); - // if (debug()) nodep->dumpTree("- IfPre: "); + // UINFOTREE(1, nodep, "", "IfPre"); if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly userIterateAndNext(nodep->thensp(), nullptr); userIterateAndNext(nodep->elsesp(), nullptr); } iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition. - // if (debug()) nodep->dumpTree("- IfOut: "); + // UINFOTREE(1, nodep, "", "IfOut"); } void visit(AstExprStmt* nodep) override { userIterateAndNext(nodep->stmtsp(), nullptr); @@ -5259,7 +5260,7 @@ class WidthVisitor final : public VNVisitor { nodep->didWidth(true); const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars); UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach"); - // if (debug()) nodep->dumpTree("- foreach-old: "); + // UINFOTREE(1, nodep, "", "foreach-old"); userIterateAndNext(loopsp->fromp(), WidthVP{SELF, BOTH}.p()); AstNodeExpr* const fromp = loopsp->fromp(); UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type"); @@ -5320,19 +5321,19 @@ class WidthVisitor final : public VNVisitor { // handled in each visitor. // Then LHS sign-extends only if *RHS* is signed assertAtStatement(nodep); - // if (debug()) nodep->dumpTree("- AssignPre: "); + // UINFOTREE(1, nodep, "", "AssignPre"); { - // if (debug()) nodep->dumpTree("- assin:: "); + // UINFOTREE(1, nodep, "", "assin:"); userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LHS be untyped?"); UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LHS be unsized?"); nodep->dtypeFrom(nodep->lhsp()); // // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim - // if (debug()) nodep->dumpTree("- assrhs: "); + // UINFOTREE(1, nodep, "", "assrhs"); userIterateAndNext(nodep->rhsp(), WidthVP{nodep->dtypep(), PRELIM}.p()); // - // if (debug()) nodep->dumpTree("- assign: "); + // UINFOTREE(1, nodep, "", "assign"); AstNodeDType* const lhsDTypep = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined @@ -5372,7 +5373,7 @@ class WidthVisitor final : public VNVisitor { } iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); - // if (debug()) nodep->dumpTree("- AssignOut: "); + // UINFOTREE(1, nodep, "", "AssignOut"); } if (AstNode* const controlp = nodep->timingControlp()) { if (VN_IS(m_ftaskp, Func)) { @@ -5840,7 +5841,7 @@ class WidthVisitor final : public VNVisitor { iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. } void visit(AstPin* nodep) override { - // if (debug()) nodep->dumpTree("- PinPre: "); + // UINFOTREE(1, nodep, "", "PinPre"); // TOP LEVEL NODE if (nodep->modVarp() && nodep->modVarp()->isGParam()) { // Widthing handled as special init() case @@ -5969,7 +5970,7 @@ class WidthVisitor final : public VNVisitor { iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep); } } - // if (debug()) nodep->dumpTree("- PinOut: "); + // UINFOTREE(1, nodep, "", "PinOut"); } void visit(AstCell* nodep) override { VL_RESTORER(m_cellp); @@ -6142,7 +6143,7 @@ class WidthVisitor final : public VNVisitor { } } } - // if (debug()) nodep->dumpTree("- FuncOut: "); + // UINFOTREE(1, nodep, "", "FuncOut"); } // Returns true if dtypep0 and dtypep1 have same dimensions static bool areSameSize(AstUnpackArrayDType* dtypep0, AstUnpackArrayDType* dtypep1) { @@ -7081,7 +7082,7 @@ class WidthVisitor final : public VNVisitor { // because the rhs could be larger, and we need to have proper editing to get the widths // to be the same for our operations. // - // if (debug() >= 9) { UINFO(0, "rus " << m_vup); nodep->dumpTree("- rusin: "); } + // UINFOTREE(9, nodep, "rus " << m_vup, "rusin"); if (m_vup->prelim()) { // First stage evaluation // Determine expression widths only relying on what's in the subops userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); @@ -7137,7 +7138,7 @@ class WidthVisitor final : public VNVisitor { iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP, rhsWarn); } - // if (debug() >= 9) nodep->dumpTree("- rusou: "); + // UINFOTREE(9, nodep, "", "rusou"); } void visit_real_add_sub(AstNodeBiop* nodep) { // CALLER: AddD, MulD, ... @@ -7290,8 +7291,8 @@ class WidthVisitor final : public VNVisitor { num.opRepl(constp->num(), expWidth); // {width{'1}} AstNodeExpr* const newp = new AstConst{constp->fileline(), num}; // Spec says always unsigned with proper width - if (debug() > 4) constp->dumpTree("- fixAutoExtend_old: "); - if (debug() > 4) newp->dumpTree("- _new: "); + UINFOTREE(5, constp, "", "fixAutoExtend_old"); + UINFOTREE(5, newp, "", "_new"); constp->replaceWith(newp); VL_DO_DANGLING(constp->deleteTree(), constp); // Tell caller the new constp, and that we changed it. @@ -7309,8 +7310,8 @@ class WidthVisitor final : public VNVisitor { num.opExtendXZ(constp->num(), constp->width()); AstNodeExpr* const newp = new AstConst{constp->fileline(), num}; // Spec says always unsigned with proper width - if (debug() > 4) constp->dumpTree("- fixUnszExtend_old: "); - if (debug() > 4) newp->dumpTree("- _new: "); + UINFOTREE(5, constp, "", "fixUnszExtend_old"); + UINFOTREE(5, newp, "", "_new"); constp->replaceWith(newp); VL_DO_DANGLING(constp->deleteTree(), constp); // Tell caller the new constp, and that we changed it. @@ -7477,7 +7478,7 @@ class WidthVisitor final : public VNVisitor { void iterateCheckAssign(AstNode* nodep, const char* side, AstNode* rhsp, Stage stage, AstNodeDType* lhsDTypep) { // Check using assignment-like context rules - // if (debug()) nodep->dumpTree("- checkass: "); + // UINFOTREE(1, nodep, "", "checkass"); UASSERT_OBJ(stage == FINAL, nodep, "Bad width call"); // Create unpacked byte from string see IEEE 1800-2023 5.9 if (AstConst* constp = VN_CAST(rhsp, Const)) { @@ -7517,7 +7518,7 @@ class WidthVisitor final : public VNVisitor { = (VN_IS(nodep, NodeAssign) && VN_IS(VN_AS(nodep, NodeAssign)->lhsp(), NodeStream)); rhsp = iterateCheck(nodep, side, rhsp, ASSIGN, FINAL, lhsDTypep, lhsStream ? EXTEND_OFF : EXTEND_LHS); - // if (debug()) nodep->dumpTree("- checkout: "); + // UINFOTREE(1, nodep, "", "checkout"); (void)rhsp; // cppcheck } @@ -7558,7 +7559,7 @@ class WidthVisitor final : public VNVisitor { const bool bad = widthBad(underp, nodep->findBitDType()); if (bad) { { // if (warnOn), but not needed here - if (debug() > 4) nodep->backp()->dumpTree("- back: "); + UINFOTREE(5, nodep->backp(), "", "back"); nodep->v3widthWarn(1, underp->width(), "Logical operator " << nodep->prettyTypeName() << " expects 1 bit on the " @@ -7627,7 +7628,7 @@ class WidthVisitor final : public VNVisitor { << " (IEEE 1800-2023 6.19.3)\n" << nodep->warnMore() << "... Suggest use enum's mnemonic, or static cast"); - // if (debug()) nodep->backp()->dumpTree("- back: "); + // UINFOTREE(1, nodep->backp(), "", "back"); } } AstNodeDType* subDTypep = expDTypep; @@ -7747,7 +7748,7 @@ class WidthVisitor final : public VNVisitor { warnOn = false; } if (bad && warnOn) { - if (debug() > 4) nodep->backp()->dumpTree("- back: "); + UINFOTREE(5, nodep->backp(), "", "back"); nodep->v3widthWarn( expWidth, underp->width(), diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 53e2e152f..0f1c841ba 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -161,8 +161,8 @@ private: nodep->replaceWith(newp); AstNode* const oldp = nodep; nodep = newp; - // if (debug() > 4) oldp->dumpTree("- fixConstSize_old: "); - // if (debug() > 4) newp->dumpTree("- _new: "); + // UINFOTREE(5, oldp, "", "fixConstSize_old"); + // UINFOTREE(5, newp, "", "_new"); VL_DO_DANGLING(pushDeletep(oldp), oldp); } editDType(nodep); diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index b53e2ae3c..139006d68 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -227,11 +227,11 @@ class WidthSelVisitor final : public VNVisitor { // Select of a non-width specified part of an array, i.e. "array[2]" // This select style has a lsb and msb (no user specified width) UINFO(6, "SELBIT " << nodep); - if (debug() >= 9) nodep->backp()->dumpTree("- SELBT0: "); + UINFOTREE(9, nodep->backp(), "", "SELBT0"); // lhsp/rhsp do not need to be constant AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const rhsp = nodep->bitp()->unlinkFrBack(); // bit we're extracting - if (debug() >= 9) nodep->dumpTree("- SELBT2: "); + UINFOTREE(9, nodep, "", "SELBT2"); const FromData fromdata = fromDataForArray(nodep, fromp); AstNodeDType* const ddtypep = fromdata.m_dtypep; const VNumRange fromRange = fromdata.m_fromRange; @@ -244,7 +244,7 @@ class WidthSelVisitor final : public VNVisitor { } AstArraySel* const newp = new AstArraySel{nodep->fileline(), fromp, subp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstPackArrayDType* const adtypep = VN_CAST(ddtypep, PackArrayDType)) { @@ -266,14 +266,14 @@ class WidthSelVisitor final : public VNVisitor { newp->declRange(fromRange); newp->declElWidth(elwidth); newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstAssocArrayDType* const adtypep = VN_CAST(ddtypep, AssocArrayDType)) { // SELBIT(array, index) -> ASSOCSEL(array, index) AstAssocSel* const newp = new AstAssocSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstWildcardArrayDType* const adtypep @@ -281,7 +281,7 @@ class WidthSelVisitor final : public VNVisitor { // SELBIT(array, index) -> WILDCARDSEL(array, index) AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) { @@ -290,7 +290,7 @@ class WidthSelVisitor final : public VNVisitor { AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, methodName, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference - if (debug() >= 9) newp->dumpTree("- SELBTq: "); + UINFOTREE(9, newp, "", "SELBTq"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstQueueDType* const adtypep = VN_CAST(ddtypep, QueueDType)) { @@ -304,7 +304,7 @@ class WidthSelVisitor final : public VNVisitor { newp = new AstCMethodHard{nodep->fileline(), fromp, methodName, rhsp}; } newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference - if (debug() >= 9) newp->dumpTree("- SELBTq: "); + UINFOTREE(9, newp, "", "SELBTq"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, BasicDType) && ddtypep->isString()) { @@ -335,7 +335,7 @@ class WidthSelVisitor final : public VNVisitor { = new AstSel{nodep->fileline(), fromp, newSubLsbOf(rhsp, fromRange), 1}; newp->declRange(fromRange); UINFO(6, " new " << newp); - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, NodeUOrStructDType)) { // A bit from the packed struct @@ -344,7 +344,7 @@ class WidthSelVisitor final : public VNVisitor { = new AstSel{nodep->fileline(), fromp, newSubLsbOf(rhsp, fromRange), 1}; newp->declRange(fromRange); UINFO(6, " new " << newp); - if (debug() >= 9) newp->dumpTree("- SELBTn: "); + UINFOTREE(9, newp, "", "SELBTn"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // nullptr=bad extract, or unknown node type @@ -361,11 +361,11 @@ class WidthSelVisitor final : public VNVisitor { // SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb) // This select style has a (msb or lsb) and width UINFO(6, "SELEXTRACT " << nodep); - // if (debug() >= 9) nodep->dumpTree("- SELEX0: "); + // UINFOTREE(9, nodep, "", "SELEX0"); // Below 2 lines may change nodep->widthp() V3Const::constifyParamsNoWarnEdit(nodep->leftp()); // May relink pointed to node V3Const::constifyParamsNoWarnEdit(nodep->rightp()); // May relink pointed to node - // if (debug() >= 9) nodep->dumpTree("- SELEX3: "); + // UINFOTREE(9, nodep, "", "SELEX3"); AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); const FromData fromdata = fromDataForArray(nodep, fromp); AstNodeDType* const ddtypep = fromdata.m_dtypep; @@ -451,7 +451,7 @@ class WidthSelVisitor final : public VNVisitor { newp->declRange(fromRange); newp->declElWidth(elwidth); newp->dtypeFrom(sliceDType(adtypep, msb, lsb)); - // if (debug() >= 9) newp->dumpTree("- EXTBTn: "); + // UINFOTREE(9, newp, "", "EXTBTn"); UASSERT_OBJ(newp->widthMin() == newp->widthConst(), nodep, "Width mismatch"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -476,7 +476,7 @@ class WidthSelVisitor final : public VNVisitor { msb + 1 - lsb}; newp->declRange(fromRange); UINFO(6, " new " << newp); - // if (debug() >= 9) newp->dumpTree("- SELEXnew: "); + // UINFOTREE(9, newp, "", "SELEXnew"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, NodeUOrStructDType)) { @@ -495,7 +495,7 @@ class WidthSelVisitor final : public VNVisitor { msb + 1 - lsb}; newp->declRange(fromRange); UINFO(6, " new " << newp); - // if (debug() >= 9) newp->dumpTree("- SELEXnew: "); + // UINFOTREE(9, newp, "", "SELEXnew"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // nullptr=bad extract, or unknown node type @@ -517,13 +517,13 @@ class WidthSelVisitor final : public VNVisitor { // This select style has a lsb and width UINFO(6, "SELPLUS/MINUS " << nodep); // Below 2 lines may change nodep->widthp() - if (debug() >= 9) nodep->dumpTree("- SELPM0: "); + UINFOTREE(9, nodep, "", "SELPM0"); V3Width::widthParamsEdit(nodep->rhsp()); // constifyEdit doesn't ensure widths finished V3Const::constifyEdit(nodep->rhsp()); // May relink pointed to node, ok if not const V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit slice range isn't a constant"); - if (debug() >= 9) nodep->dumpTree("- SELPM3: "); + UINFOTREE(9, nodep, "", "SELPM3"); // Now replace it with an AstSel AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); @@ -600,7 +600,7 @@ class WidthSelVisitor final : public VNVisitor { newp->declRange(fromRange); newp->declElWidth(elwidth); UINFO(6, " new " << newp); - if (debug() >= 9) newp->dumpTree("- SELNEW: "); + UINFOTREE(9, newp, "", "SELNEW"); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // nullptr=bad extract, or unknown node type From 473d54aa95290dd276392af733308a7d5e5996ac Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 2 Aug 2025 13:47:07 -0400 Subject: [PATCH 116/252] Internals: Fis emit spacing etc. No functional change --- include/verilated_dpi.cpp | 2 ++ src/V3Task.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 37b8a878e..9e2b1cf39 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -80,6 +80,7 @@ void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) { } void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) { + // Verilator supports > 32 bit widths, which is an extension to IEEE DPI // See also VL_SEL_WWI const int msb = lsb + width - 1; const int word_shift = VL_BITWORD_I(lsb); @@ -103,6 +104,7 @@ void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width); } void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) { + // Verilator supports > 32 bit widths, which is an extension to IEEE DPI const int msb = lsb + width - 1; const int word_shift = VL_BITWORD_I(lsb); if (VL_BITBIT_I(lsb) == 0) { diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 3cab796a2..358811654 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -337,7 +337,7 @@ struct TaskDpiUtils final { const string frSvType = portp->basicp()->isDpiBitVec() ? "SVBV" : "SVLV"; if (portp->isWide()) { // Need to convert to wide, using special function - frstmt = "VL_SET_W_" + frSvType + "(" + cvtToStr(portp->width()) + ","; + frstmt = "VL_SET_W_" + frSvType + "(" + cvtToStr(portp->width()) + ", "; return true; } else { const AstNodeDType* const dtypep = portp->dtypep()->skipRefp(); @@ -826,7 +826,7 @@ class TaskVisitor final : public VNVisitor { linesp->addNext(srcp); linesp->addNext( new AstText{portvscp->fileline(), - "," + frName + " + " + cvtToStr(i * widthWords) + ");\n"}); + ", " + frName + " + " + cvtToStr(i * widthWords) + ");\n"}); stmtp = new AstCStmt{portvscp->fileline(), linesp}; } else { string from = frstmt; @@ -884,7 +884,7 @@ class TaskVisitor final : public VNVisitor { stmt += "if (VL_UNLIKELY(__Vfuncnum == -1)) __Vfuncnum = Verilated::exportFuncNum(\"" + nodep->cname() + "\");\n"; // If the find fails, it will throw an error - stmt += "const VerilatedScope* __Vscopep = Verilated::dpiScope();\n"; + stmt += "const VerilatedScope* const __Vscopep = Verilated::dpiScope();\n"; // If dpiScope is fails and is null; the exportFind function throws and error const string cbtype = VIdProtect::protect(v3Global.opt.prefix() + "__Vcb_" + nodep->cname() + "_t"); From 12355270b322f0be46575ea176d88c0c105b59d3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 2 Aug 2025 13:50:16 -0400 Subject: [PATCH 117/252] Tests: Add uvm_dpi --- test_regress/t/t_dist_cinclude.py | 2 + test_regress/t/t_dist_copyright.py | 1 + test_regress/t/t_uvm_dpi.out | 37 +++ test_regress/t/t_uvm_dpi.py | 20 ++ test_regress/t/t_uvm_dpi.v | 161 ++++++++++ test_regress/t/uvm/dpi/.clang-format | 125 ++++++++ test_regress/t/uvm/dpi/uvm_common.c | 52 ++++ test_regress/t/uvm/dpi/uvm_dpi.cc | 63 ++++ test_regress/t/uvm/dpi/uvm_dpi.h | 84 +++++ test_regress/t/uvm/dpi/uvm_dpi.svh | 44 +++ test_regress/t/uvm/dpi/uvm_hdl.c | 40 +++ test_regress/t/uvm/dpi/uvm_hdl.svh | 170 ++++++++++ test_regress/t/uvm/dpi/uvm_hdl_verilator.c | 342 +++++++++++++++++++++ test_regress/t/uvm/dpi/uvm_regex.cc | 239 ++++++++++++++ test_regress/t/uvm/dpi/uvm_regex.svh | 85 +++++ test_regress/t/uvm/dpi/uvm_svcmd_dpi.c | 135 ++++++++ test_regress/t/uvm/dpi/uvm_svcmd_dpi.svh | 65 ++++ 17 files changed, 1665 insertions(+) create mode 100644 test_regress/t/t_uvm_dpi.out create mode 100755 test_regress/t/t_uvm_dpi.py create mode 100644 test_regress/t/t_uvm_dpi.v create mode 100644 test_regress/t/uvm/dpi/.clang-format create mode 100644 test_regress/t/uvm/dpi/uvm_common.c create mode 100644 test_regress/t/uvm/dpi/uvm_dpi.cc create mode 100644 test_regress/t/uvm/dpi/uvm_dpi.h create mode 100644 test_regress/t/uvm/dpi/uvm_dpi.svh create mode 100644 test_regress/t/uvm/dpi/uvm_hdl.c create mode 100644 test_regress/t/uvm/dpi/uvm_hdl.svh create mode 100644 test_regress/t/uvm/dpi/uvm_hdl_verilator.c create mode 100644 test_regress/t/uvm/dpi/uvm_regex.cc create mode 100644 test_regress/t/uvm/dpi/uvm_regex.svh create mode 100644 test_regress/t/uvm/dpi/uvm_svcmd_dpi.c create mode 100644 test_regress/t/uvm/dpi/uvm_svcmd_dpi.svh diff --git a/test_regress/t/t_dist_cinclude.py b/test_regress/t/t_dist_cinclude.py index a7ee86009..7ecafce76 100755 --- a/test_regress/t/t_dist_cinclude.py +++ b/test_regress/t/t_dist_cinclude.py @@ -27,6 +27,8 @@ for filename in files.split(): continue if "include/gtkwave/" in filename: # Standard file - can't change it continue + if "test_regress/t/uvm/dpi/" in filename: # Standard file - can't change it + continue filename = os.path.join(root, filename) if not os.path.exists(filename): continue diff --git a/test_regress/t/t_dist_copyright.py b/test_regress/t/t_dist_copyright.py index 92b4ff4a9..dab107117 100755 --- a/test_regress/t/t_dist_copyright.py +++ b/test_regress/t/t_dist_copyright.py @@ -45,6 +45,7 @@ EXEMPT_FILES_LIST = """ test_regress/t/t_incr_void.v test_regress/t/tsub/t_flag_f_tsub.v test_regress/t/tsub/t_flag_f_tsub_inc.v + test_regress/t/uvm/dpi test_regress/t/uvm/uvm_pkg_all.svh test_regress/t/uvm/uvm_pkg_todo.svh verilator.pc.in diff --git a/test_regress/t/t_uvm_dpi.out b/test_regress/t/t_uvm_dpi.out new file mode 100644 index 000000000..37b3ebd24 --- /dev/null +++ b/test_regress/t/t_uvm_dpi.out @@ -0,0 +1,37 @@ +uvm_dpi_get_tool_name_c() = Verilator +uvm_dpi_get_tool_version_c() = 5.039 devel += uvm_re += uvm_hdl_check_path += uvm_hdl_read simple variable += uvm_hdl_read not found (bad) +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/HDL_SET set: unable to locate hdl path (t.__DEPOSIT_NOT_FOUND) + Either the name is incorrect, or you may not have PLI/ACC visibility to that name += uvm_hdl_deposit simple variable += uvm_hdl_read single bit += uvm_hdl_deposit single bit += uvm_hdl_read multi-bit += uvm_hdl_deposit multi-bit += uvm_hdl_deposit bad ranges +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/HDL_SET set: unable to locate hdl path (t.exposed[10:3]) + Either the name is incorrect, or you may not have PLI/ACC visibility to that name +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/HDL_SET set: unable to locate hdl path (t.exposed[99:15]) + Either the name is incorrect, or you may not have PLI/ACC visibility to that name += uvm_hdl_force +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/VLOG_GET Unsupported: uvh_hdl_force/uvm_hdl_release on hdl path 't.exposed' += uvm_hdl_release +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/VLOG_GET Unsupported: uvh_hdl_force/uvm_hdl_release on hdl path 't.exposed' += uvm_hdl_release_and_read +=== +UVM Report expected on next line: +UVM Report ../../t/uvm/dpi/uvm_hdl_verilator.c: 54: UVM/DPI/VLOG_GET Unsupported: uvh_hdl_force/uvm_hdl_release on hdl path 't.exposed' +*-* All Finished *-* diff --git a/test_regress/t/t_uvm_dpi.py b/test_regress/t/t_uvm_dpi.py new file mode 100755 index 000000000..b4f073231 --- /dev/null +++ b/test_regress/t/t_uvm_dpi.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.pli_filename = "t/uvm/dpi/uvm_dpi.cc" + +test.compile( + verilator_flags2=["--binary", "--build-jobs 4", "--vpi", "+incdir+t/uvm", test.pli_filename]) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_uvm_dpi.v b/test_regress/t/t_uvm_dpi.v new file mode 100644 index 000000000..4c44dc9cf --- /dev/null +++ b/test_regress/t/t_uvm_dpi.v @@ -0,0 +1,161 @@ +// 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 + +// verilator lint_off WIDTH +`include "dpi/uvm_dpi.svh" +// verilator lint_on WIDTH + +// verilog_format: off +`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); +`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); +// verilog_format: on + +// Undocumented DPI available version of uvm_report +// UVM declares without 'context' +package uvm_pkg; + export "DPI-C" function m__uvm_report_dpi; + function void m__uvm_report_dpi(int severity, string id, string message, int verbosity, + string filename, int line); + $display("UVM Report %s:%d: %s %s", filename, line, id, message); + endfunction : m__uvm_report_dpi +endpackage + +module t; + int i; + string s; + chandle h; + + // To cover testing cases, this has non-zero LSB/LO + logic [31+8:8] exposed /*verilator public*/; + + uvm_hdl_data_t lval; + + initial begin + // TODO TEST: + // extern const char* uvm_dpi_get_next_arg_c(int init); + + //===== Tool + s = uvm_dpi_get_tool_name_c(); + $display("uvm_dpi_get_tool_name_c() = %s", s); + `checks(s, "Verilator"); + + s = uvm_dpi_get_tool_version_c(); + $display("uvm_dpi_get_tool_version_c() = %s", s); + if (s == "") $stop; + + //===== Regexp + $display("= uvm_re"); + h = uvm_dpi_regcomp("a.*b"); + + i = uvm_dpi_regexec(h, "__a_b__"); + `checkh(i, 0); + i = uvm_dpi_regexec(h, "__a_z__"); + `checkh(i, 1); + + uvm_dpi_regfree(h); + + i = uvm_re_match("a.*b", "__a__b__"); + `checkh(i, 0); + + i = uvm_re_match("a.*b", "__a__z__"); + `checkh(i, 1); + + s = uvm_glob_to_re("a foo bar"); + `checks(s, "/^a foo bar$/"); + + //===== Hier +`ifdef VERILATOR +`ifdef TEST_VERBOSE + $c("Verilated::scopesDump();"); +`endif +`endif + + $display("= uvm_hdl_check_path"); + i = uvm_hdl_check_path("t.__NOT_FOUND"); + `checkh(i, 0); + i = uvm_hdl_check_path("t.exposed"); + `checkh(i, 1); + i = uvm_hdl_check_path("$root.t.exposed"); + `checkh(i, 1); + + $display("= uvm_hdl_read simple variable"); + exposed = 32'hb001; + lval = '0; // Upper bits not cleared by uvm_hdl_read + i = uvm_hdl_read("t.exposed", lval); + `checkh(i, 1); + `checkh(lval[31:0], exposed); + + lval = '0; + $display("= uvm_hdl_read not found (bad)"); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_deposit("t.__DEPOSIT_NOT_FOUND", lval); + `checkh(i, 0); + + $display("= uvm_hdl_deposit simple variable"); + lval = 1024'hab; + i = uvm_hdl_deposit("t.exposed", lval); + `checkh(i, 1); + `checkh(exposed, 32'hab); + + $display("= uvm_hdl_read single bit"); + exposed = 32'habcd; + lval = '0; // Upper bits not cleared by uvm_hdl_read + i = uvm_hdl_read("t.exposed[11]", lval); + `checkh(i, 1); + `checkh(lval[0], exposed[11]); + + $display("= uvm_hdl_deposit single bit"); + lval = 1024'h0; + i = uvm_hdl_deposit("t.exposed[11]", lval); + `checkh(i, 1); + `checkh(exposed, 32'habc5); + + $display("= uvm_hdl_read multi-bit"); + exposed = 32'habcd; + lval = '0; // Upper bits not cleared by uvm_hdl_read + i = uvm_hdl_read("t.exposed[19:12]", lval); + `checkh(i, 1); + `checkh(lval[7:0], exposed[19:12]); + + $display("= uvm_hdl_deposit multi-bit"); + lval = 1024'h12; + i = uvm_hdl_deposit("t.exposed[19:12]", lval); + `checkh(i, 1); + `checkh(exposed, 32'ha12d); + + $display("= uvm_hdl_deposit bad ranges"); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_deposit("t.exposed[10:3]", lval); + `checkh(i, 0); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_deposit("t.exposed[99:15]", lval); + `checkh(i, 0); + +`ifdef VERILATOR + // UNSUPPORTED: force/release via VPI + // If support, validate or throw unsupported on force/release part-selects + $display("= uvm_hdl_force"); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_force("t.exposed", 62); + `checkh(i, 0); + + $display("= uvm_hdl_release"); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_release("t.exposed"); + `checkh(i, 0); + + $display("= uvm_hdl_release_and_read"); + $display("===\nUVM Report expected on next line:"); + i = uvm_hdl_release_and_read("t.exposed", lval); + `checkh(i, 0); +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/uvm/dpi/.clang-format b/test_regress/t/uvm/dpi/.clang-format new file mode 100644 index 000000000..9225372c1 --- /dev/null +++ b/test_regress/t/uvm/dpi/.clang-format @@ -0,0 +1,125 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 99 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - Q_FOREACH + - BOOST_FOREACH + +# Include grouping/sorting +SortIncludes: true +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '"V3Pch.*\.h"' + Priority: -2 # Precompiled headers + - Regex: '"(config_build|verilated_config|verilatedos)\.h"' + Priority: -1 # Sepecials before main header + - Regex: '(<|")verilated.*' + Priority: 1 # Runtime headers + - Regex: '"V3.*__gen.*\.h"' + Priority: 3 # Generated internal headers separately + - Regex: '"V3.*"' + Priority: 2 # Internal header + - Regex: '".*"' + Priority: 4 # Other non-system headers + - Regex: '<[[:alnum:]_.]+>' + Priority: 5 # Simple system headers next + - Regex: '<.*>' + Priority: 6 # Other system headers next +IncludeIsMainRegex: '$' + +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 0 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... diff --git a/test_regress/t/uvm/dpi/uvm_common.c b/test_regress/t/uvm/dpi/uvm_common.c new file mode 100644 index 000000000..018c24804 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_common.c @@ -0,0 +1,52 @@ +//---------------------------------------------------------------------- +// Copyright 2010-2011 Mentor Graphics Corporation +// Copyright 2010-2013 Synopsys, Inc. +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2010 AMD +// Copyright 2013 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +// Implementation of common methods for DPI + +extern void m__uvm_report_dpi(int,const char*,const char*,int,const char*, int); + +#if defined(XCELIUM) || defined(NCSC) +const static char* uvm_package_scope_name = "uvm_pkg::"; +#else +const static char* uvm_package_scope_name = "uvm_pkg"; +#endif + +void m_uvm_report_dpi( int severity, + char* id, + char* message, + int verbosity, + char* file, + int linenum) { + svScope old_scope = svSetScope(svGetScopeFromName(uvm_package_scope_name)); + m__uvm_report_dpi(severity, id, message, verbosity, file, linenum); + svSetScope(old_scope); + } + + +int int_str_max ( int radix_bits ) { + int val = INT_MAX; + int ret = 1; + while ((val = (val /radix_bits))) + ret++; + return ret; +} diff --git a/test_regress/t/uvm/dpi/uvm_dpi.cc b/test_regress/t/uvm/dpi/uvm_dpi.cc new file mode 100644 index 000000000..3739aba9b --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_dpi.cc @@ -0,0 +1,63 @@ +//---------------------------------------------------------------------- +// Copyright 2010-2017 Mentor Graphics Corporation +// Copyright 2010-2013 Synopsys, Inc. +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2013 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +// +// Top-level file that includes all of the C/C++ files required +// by UVM +// +// The C code may be compiled by compiling this top file only, +// or by compiling individual files then linking them together. +// + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "uvm_dpi.h" + +// Avoid -Wmissing-definitions + int uvm_re_match(const char * re, const char *str); + const char * uvm_glob_to_re(const char *glob); + int uvm_hdl_check_path(char *path); + int uvm_hdl_read(char *path, p_vpi_vecval value); + int uvm_hdl_deposit(char *path, p_vpi_vecval value); + int uvm_hdl_force(char *path, p_vpi_vecval value); + int uvm_hdl_release_and_read(char *path, p_vpi_vecval value); + int uvm_hdl_release(char *path); + void push_data(int lvl,char *entry, int cmd); + void walk_level(int lvl, int argc, char**argv,int cmd); + const char *uvm_dpi_get_next_arg_c (int init); + extern char* uvm_dpi_get_tool_name_c (); + extern char* uvm_dpi_get_tool_version_c (); + extern regex_t* uvm_dpi_regcomp (char* pattern); + extern int uvm_dpi_regexec (regex_t* re, char* str); + extern void uvm_dpi_regfree (regex_t* re); + +#include "uvm_common.c" +#include "uvm_regex.cc" +#include "uvm_hdl.c" +#include "uvm_svcmd_dpi.c" + +#ifdef __cplusplus +} +#endif diff --git a/test_regress/t/uvm/dpi/uvm_dpi.h b/test_regress/t/uvm/dpi/uvm_dpi.h new file mode 100644 index 000000000..6ccee7da5 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_dpi.h @@ -0,0 +1,84 @@ +//---------------------------------------------------------------------- +// Copyright 2010-2017 Mentor Graphics Corporation +// Copyright 2010 Synopsys, Inc. +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2013 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +// +// Top level header filke that wraps all requirements which +// are common to the various C/C++ files in UVM. +// + +#ifndef UVM_DPI__H +#define UVM_DPI__H + +#include +#include "vpi_user.h" +#include "veriuser.h" +#include "svdpi.h" +#include +#include +#include +#include +#include + +// The following consts and method call are for +// internal usage by the UVM DPI implementation, +// and are not intended for public use. +static const int M_UVM_INFO = 0; +static const int M_UVM_WARNING = 1; +static const int M_UVM_ERROR = 2; +static const int M_UVM_FATAL = 3; + +static const int M_UVM_NONE = 0; +static const int M_UVM_LOW = 100; +static const int M_UVM_MEDIUM = 200; +static const int M_UVM_HIGH = 300; +static const int M_UVM_FULL = 400; +static const int M_UVM_DEBUG = 500; + +void m_uvm_report_dpi(int severity, + char* id, + char* message, + int verbosity, + char* file, + int linenum); + +int int_str_max( int ); + +int uvm_re_match(const char * re, const char *str); +const char * uvm_glob_to_re(const char *glob); + +int uvm_hdl_check_path(char *path); +int uvm_hdl_read(char *path, p_vpi_vecval value); +int uvm_hdl_deposit(char *path, p_vpi_vecval value); +int uvm_hdl_force(char *path, p_vpi_vecval value); +int uvm_hdl_release_and_read(char *path, p_vpi_vecval value); +int uvm_hdl_release(char *path); + +void push_data(int lvl,char *entry, int cmd); +void walk_level(int lvl, int argc, char**argv,int cmd); +const char *uvm_dpi_get_next_arg_c (int init); +extern char* uvm_dpi_get_tool_name_c (); +extern char* uvm_dpi_get_tool_version_c (); +extern regex_t* uvm_dpi_regcomp (char* pattern); +extern int uvm_dpi_regexec (regex_t* re, char* str); +extern void uvm_dpi_regfree (regex_t* re); + +#endif diff --git a/test_regress/t/uvm/dpi/uvm_dpi.svh b/test_regress/t/uvm/dpi/uvm_dpi.svh new file mode 100644 index 000000000..e89597f5e --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_dpi.svh @@ -0,0 +1,44 @@ +//---------------------------------------------------------------------- +// Copyright 2010-2011 Mentor Graphics Corporation +// Copyright 2010 Synopsys, Inc. +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2010 AMD +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +`ifndef UVM_DPI_SVH +`define UVM_DPI_SVH + +// +// Top-level file for DPI subroutines used by UVM. +// +// Tool-specific distribution overlays may be required. +// +// To use UVM without any tool-specific overlay, use +defin+UVM_NO_DPI +// + +`ifdef UVM_NO_DPI + `define UVM_HDL_NO_DPI + `define UVM_REGEX_NO_DPI + `define UVM_CMDLINE_NO_DPI +`endif + +`include "dpi/uvm_hdl.svh" +`include "dpi/uvm_svcmd_dpi.svh" +`include "dpi/uvm_regex.svh" + +`endif // UVM_DPI_SVH diff --git a/test_regress/t/uvm/dpi/uvm_hdl.c b/test_regress/t/uvm/dpi/uvm_hdl.c new file mode 100644 index 000000000..0b2041aca --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_hdl.c @@ -0,0 +1,40 @@ +//---------------------------------------------------------------------- +// Copyright 2009-2011 Mentor Graphics Corporation +// Copyright 2010-2011 Synopsys, Inc. +// Copyright 2007-2018 Cadence Design Systems, Inc. +// Copyright 2013 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +// hdl vendor backends are defined for VCS,QUESTA,VERILATOR,XCELIUM +#if defined(VCS) || defined(VCSMX) +#include "uvm_hdl_vcs.c" +#else +#ifdef QUESTA +#include "uvm_hdl_questa.c" +#else +#ifdef VERILATOR +#include "uvm_hdl_verilator.c" +#else +#if defined(XCELIUM) || defined(NCSC) +#include "uvm_hdl_xcelium.c" +#else +#error "hdl vendor backend is missing" +#endif +#endif +#endif +#endif diff --git a/test_regress/t/uvm/dpi/uvm_hdl.svh b/test_regress/t/uvm/dpi/uvm_hdl.svh new file mode 100644 index 000000000..2e1d6e3b6 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_hdl.svh @@ -0,0 +1,170 @@ +//------------------------------------------------------------ +// Copyright 2007-2011 Mentor Graphics Corporation +// Copyright 2015 Analog Devices, Inc. +// Copyright 2010 Synopsys, Inc. +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2014-2018 NVIDIA Corporation +// Copyright 2014 Cisco Systems, Inc. +// Copyright 2012 Accellera Systems Initiative +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//------------------------------------------------------------ + +// TITLE -- NODOCS -- UVM HDL Backdoor Access support routines. +// +// These routines provide an interface to the DPI/PLI +// implementation of backdoor access used by registers. +// +// If you DON'T want to use the DPI HDL API, then compile your +// SystemVerilog code with the vlog switch +//: vlog ... +define+UVM_HDL_NO_DPI ... +// + + +`ifndef UVM_HDL__SVH +`define UVM_HDL__SVH + + +`ifndef UVM_HDL_MAX_WIDTH +`define UVM_HDL_MAX_WIDTH 1024 +`endif + +/* + * VARIABLE -- NODOCS -- UVM_HDL_MAX_WIDTH + * Sets the maximum size bit vector for backdoor access. + * This parameter will be looked up by the + * DPI-C code using: + * vpi_handle_by_name( + * "uvm_pkg::UVM_HDL_MAX_WIDTH", 0); + */ + +// @uvm-ieee 1800.2-2020 manual 19.6.1 +parameter int UVM_HDL_MAX_WIDTH = `UVM_HDL_MAX_WIDTH; + + +typedef logic [UVM_HDL_MAX_WIDTH-1:0] uvm_hdl_data_t; + + +`ifndef UVM_HDL_NO_DPI + + // Function -- NODOCS -- uvm_hdl_check_path + // + // Checks that the given HDL ~path~ exists. Returns 0 if NOT found, 1 otherwise. + // + import "DPI-C" context function int uvm_hdl_check_path(string path); + + + // Function -- NODOCS -- uvm_hdl_deposit + // + // Sets the given HDL ~path~ to the specified ~value~. + // Returns 1 if the call succeeded, 0 otherwise. + // + import "DPI-C" context function int uvm_hdl_deposit(string path, uvm_hdl_data_t value); + + + // Function -- NODOCS -- uvm_hdl_force + // + // Forces the ~value~ on the given ~path~. Returns 1 if the call succeeded, 0 otherwise. + // + import "DPI-C" context function int uvm_hdl_force(string path, uvm_hdl_data_t value); + + + // Function -- NODOCS -- uvm_hdl_force_time + // + // Forces the ~value~ on the given ~path~ for the specified amount of ~force_time~. + // If ~force_time~ is 0, is called. + // Returns 1 if the call succeeded, 0 otherwise. + // + task uvm_hdl_force_time(string path, uvm_hdl_data_t value, time force_time = 0); + if (force_time == 0) begin + void'(uvm_hdl_deposit(path, value)); + return; + end + if (!uvm_hdl_force(path, value)) + return; + #force_time; + void'(uvm_hdl_release_and_read(path, value)); + endtask + + + // Function -- NODOCS -- uvm_hdl_release_and_read + // + // Releases a value previously set with . + // Returns 1 if the call succeeded, 0 otherwise. ~value~ is set to + // the HDL value after the release. For 'reg', the value will still be + // the forced value until it has been procedurally reassigned. For 'wire', + // the value will change immediately to the resolved value of its + // continuous drivers, if any. If none, its value remains as forced until + // the next direct assignment. + // + import "DPI-C" context function int uvm_hdl_release_and_read(string path, inout uvm_hdl_data_t value); + + + // Function -- NODOCS -- uvm_hdl_release + // + // Releases a value previously set with . + // Returns 1 if the call succeeded, 0 otherwise. + // + import "DPI-C" context function int uvm_hdl_release(string path); + + + // Function -- NODOCS -- uvm_hdl_read() + // + // Gets the value at the given ~path~. + // Returns 1 if the call succeeded, 0 otherwise. + // + import "DPI-C" context function int uvm_hdl_read(string path, output uvm_hdl_data_t value); + +`else + + function int uvm_hdl_check_path(string path); + uvm_report_fatal("UVM_HDL_CHECK_PATH", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + return 0; + endfunction + + function int uvm_hdl_deposit(string path, uvm_hdl_data_t value); + uvm_report_fatal("UVM_HDL_DEPOSIT", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + return 0; + endfunction + + function int uvm_hdl_force(string path, uvm_hdl_data_t value); + uvm_report_fatal("UVM_HDL_FORCE", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + return 0; + endfunction + + task uvm_hdl_force_time(string path, uvm_hdl_data_t value, time force_time=0); + uvm_report_fatal("UVM_HDL_FORCE_TIME", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + endtask + + function int uvm_hdl_release(string path); + uvm_report_fatal("UVM_HDL_RELEASE", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + return 0; + endfunction + + function int uvm_hdl_read(string path, output uvm_hdl_data_t value); + uvm_report_fatal("UVM_HDL_READ", + $sformatf("uvm_hdl DPI routines are compiled off. Recompile without +define+UVM_HDL_NO_DPI")); + return 0; + endfunction + +`endif + +`endif diff --git a/test_regress/t/uvm/dpi/uvm_hdl_verilator.c b/test_regress/t/uvm/dpi/uvm_hdl_verilator.c new file mode 100644 index 000000000..41b798f62 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_hdl_verilator.c @@ -0,0 +1,342 @@ +//---------------------------------------------------------------------- +// Copyright 2007-2023 Cadence Design Systems, Inc. +// Copyright 2009-2011 Mentor Graphics Corporation +// Copyright 2013-2024 NVIDIA Corporation +// Copyright 2010-2011 Synopsys, Inc. +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// Git details (see DEVELOPMENT.md): +// +// $File$ +// $Rev$ +// $Hash$ +// +//---------------------------------------------------------------------- + +#include "svdpi.h" +#include "vpi_user.h" + +#include +#include +#include + +static void m_uvm_error(const char *ID, const char *msg, ...); +static int uvm_hdl_set_vlog(char *path, p_vpi_vecval value, PLI_INT32 flag); +static int uvm_hdl_get_vlog(char *path, p_vpi_vecval value, PLI_INT32 flag, int partsel); +static int uvm_hdl_max_width(); + +// static print buffer +static char m_uvm_temp_print_buffer[1024]; + +// static print error +static void m_uvm_error(const char *id, const char *msg, ...) { + va_list argptr; + va_start(argptr, msg); + vsprintf(m_uvm_temp_print_buffer, msg, argptr); + va_end(argptr); + m_uvm_report_dpi(M_UVM_ERROR, (char *)id, &m_uvm_temp_print_buffer[0], M_UVM_NONE, + (char *)__FILE__, __LINE__); +} + +/* + * UVM HDL access C code. + * + */ + +/* + * This C code checks to see if there is PLI handle + * with a value set to define the maximum bit width. + * + * If no such variable is found, then the default + * width of 1024 is used. + * + * This function should only get called once or twice, + * its return value is cached in the caller. + * + */ +static int UVM_HDL_MAX_WIDTH = 0; +static int uvm_hdl_max_width() { + if (!UVM_HDL_MAX_WIDTH) { + vpiHandle ms; + s_vpi_value value_s = {vpiIntVal, {0}}; + ms = vpi_handle_by_name((PLI_BYTE8 *)"uvm_pkg::UVM_HDL_MAX_WIDTH", 0); + if (ms == 0) { + UVM_HDL_MAX_WIDTH = 1024; /* If nothing else is defined, this is the DEFAULT */ + } else { + vpi_get_value(ms, &value_s); + UVM_HDL_MAX_WIDTH = value_s.value.integer; + } + } + return UVM_HDL_MAX_WIDTH; +} + +/* + * Internals: Given a path, look at the path name and determine + * the handle and any partsel's needed to access it. + */ +static vpiHandle uvm_hdl_handle_by_name_partsel(char *path, int *is_partsel_ptr, int *hi_ptr, + int *lo_ptr) { + vpiHandle r; + char *path_ptr; + char *path_base_ptr; + int temp; + *is_partsel_ptr = 0; + + if (!path || !path[0]) return 0; + + // If direct lookup works, go with that + r = vpi_handle_by_name(path, 0); + if (r) return r; + + // Find array subscript + path_ptr = (char *)(path + strlen(path) - 1); + if (*path_ptr != ']') return 0; + + while (path_ptr != path && *path_ptr != ':' && *path_ptr != '[') --path_ptr; + if (path_ptr == path) return 0; + *lo_ptr = *hi_ptr = atoi(path_ptr + 1); + *is_partsel_ptr = 1; + + if (*path_ptr == ':') { + --path_ptr; // back over : + + while (path_ptr != path && *path_ptr != '[') --path_ptr; + *hi_ptr = atoi(path_ptr + 1); + if (path_ptr == path) return 0; + } + + if (*lo_ptr > *hi_ptr) { + temp = *lo_ptr; + *lo_ptr = *hi_ptr; + *hi_ptr = temp; + } + + path_base_ptr = strndup(path, (path_ptr - path)); + + r = vpi_handle_by_name(path_base_ptr, 0); + if (!r) return 0; + + { + vpiHandle rh; + s_vpi_value value; + int decl_ranged = 0; + int decl_lo; + int decl_hi; + int decl_left = -1; + int decl_right = -1; + rh = vpi_handle(vpiLeftRange, r); + if (rh) { + value.format = vpiIntVal; + vpi_get_value(rh, &value); + decl_left = value.value.integer; + vpi_release_handle(rh); + } + rh = vpi_handle(vpiRightRange, r); + if (rh) { + value.format = vpiIntVal; + vpi_get_value(rh, &value); + decl_ranged = 1; + decl_right = value.value.integer; + vpi_release_handle(rh); + } + if (!decl_ranged) { + // vpi_printf((PLI_BYTE8 *)"Outside declaration '%s' range %d:%d\n", + // path, decl_left, decl_right); + return 0; + } + // vpi_printf((PLI_BYTE8 *)"%s:%d: req %d:%d decl %d:%d for '%s'\n", + // __FILE__, __LINE__, *hi_ptr, *lo_ptr, decl_left, decl_right, path); + decl_lo = (decl_left < decl_right) ? decl_left : decl_right; + decl_hi = (decl_left > decl_right) ? decl_left : decl_right; + if (*lo_ptr < decl_lo) return 0; + if (*hi_ptr > decl_hi) return 0; + *lo_ptr -= decl_lo; + *hi_ptr -= decl_lo; + } + return r; +} + +/* + * Given a path, look the path name up using the PLI, + * and set it to 'value'. + */ +static int uvm_hdl_set_vlog(char *path, p_vpi_vecval value, PLI_INT32 flag) { + vpiHandle r; + s_vpi_value value_s = {vpiIntVal, {0}}; + s_vpi_time time_s = {vpiSimTime, 0, 0, 0.0}; + int is_partsel, hi, lo; + int size; + static int s_maxsize = -1; + + if (flag == vpiForceFlag || flag == vpiReleaseFlag) { + // It appears other simulator interfaces likewise don't support this + m_uvm_error("UVM/DPI/VLOG_GET", "Unsupported: uvh_hdl_force/uvm_hdl_release on hdl path '%s'", + path); + return 0; + } + + r = uvm_hdl_handle_by_name_partsel(path, &is_partsel, &hi, &lo); + if (r == 0) { + m_uvm_error("UVM/DPI/HDL_SET", + "set: unable to locate hdl path (%s)\n Either the name is incorrect, " + "or you may not have PLI/ACC visibility to that name", + path); + return 0; + } + + if (!is_partsel) { + value_s.format = vpiVectorVal; + value_s.value.vector = value; + vpi_put_value(r, &value_s, &time_s, flag); + } else { + if (s_maxsize == -1) s_maxsize = uvm_hdl_max_width(); + size = vpi_get(vpiSize, r); + if (size > s_maxsize) { + m_uvm_error("UVM/DPI/VLOG_PUT", + "hdl path '%s' is %0d bits, but the maximum size is %0d. " + "You can increase the maximum via a compile-time flag: " + "+define+UVM_HDL_MAX_WIDTH=", + path, size, s_maxsize); + vpi_release_handle(r); + return 0; + } + + value_s.format = vpiVectorVal; + vpi_get_value(r, &value_s); + + for (int i = 0; i < (((hi - lo + 1) / 32) + 1); ++i) { + int subsize = hi - (lo + (i << 5)) + 1; + if (subsize > 32) subsize = 32; + svPutPartselLogic(&value_s.value.vector[i], value[i], lo + (i << 5), subsize); + } + vpi_put_value(r, &value_s, &time_s, flag); + } + + vpi_release_handle(r); + + return 1; +} + +/* + * Given a path, look the path name up using the PLI + * and return its 'value'. + */ +static int uvm_hdl_get_vlog(char *path, p_vpi_vecval value, PLI_INT32 flag, int partsel) { + static int s_maxsize = -1; + int i, size, chunks; + vpiHandle r; + s_vpi_value value_s; + int is_partsel, hi, lo; + + r = uvm_hdl_handle_by_name_partsel(path, &is_partsel, &hi, &lo); + if (r == 0) { + m_uvm_error("UVM/DPI/VLOG_GET", + "unable to locate hdl path (%s)\n Either the name is incorrect, or you " + "may not have PLI/ACC visibility to that name", + path); + return 0; + } + + if (s_maxsize == -1) s_maxsize = uvm_hdl_max_width(); + size = vpi_get(vpiSize, r); + if (size > s_maxsize) { + m_uvm_error("UVM/DPI/VLOG_GET", + "hdl path '%s' is %0d bits, but the maximum size is %0d. " + "You can increase the maximum via a compile-time flag: " + "+define+UVM_HDL_MAX_WIDTH=", + path, size, s_maxsize); + vpi_release_handle(r); + return 0; + } + + chunks = (size - 1) / 32 + 1; + + value_s.format = vpiVectorVal; + vpi_get_value(r, &value_s); + // Note upper bits are not cleared, other simulators do likewise + if (!is_partsel) { + // Keep as separate branch as subroutine can potentially inline and highly optimize + for (i = 0; i < chunks; ++i) { + value[i].aval = value_s.value.vector[i].aval; + value[i].bval = value_s.value.vector[i].bval; + } + } else { + // Verilator supports > 32 bit widths, which is an extension to IEEE DPI + svGetPartselLogic(value, value_s.value.vector, lo, hi - lo + 1); + } + // vpi_printf((PLI_BYTE8 *)"uvm_hdl_get_vlog(%s,%0x)\n", path, value[0].aval); + vpi_release_handle(r); + + return 1; +} + +/* + * Given a path, look the path name up using the PLI, + * but don't set or get. Just check. + * + * Return 0 if NOT found. + * Return 1 if found. + */ +int uvm_hdl_check_path(char *path) { + vpiHandle handle; + + handle = vpi_handle_by_name(path, 0); + return (handle != 0); +} + +/* + * Given a path, look the path name up using the PLI + * or the FLI, and return its 'value'. + */ +int uvm_hdl_read(char *path, p_vpi_vecval value) { + return uvm_hdl_get_vlog(path, value, vpiNoDelay, 0); +} + +/* + * Given a path, look the path name up using the PLI + * or the FLI, and set it to 'value'. + */ +int uvm_hdl_deposit(char *path, p_vpi_vecval value) { + return uvm_hdl_set_vlog(path, value, vpiNoDelay); +} + +/* + * Given a path, look the path name up using the PLI + * or the FLI, and set it to 'value'. + */ +int uvm_hdl_force(char *path, p_vpi_vecval value) { + return uvm_hdl_set_vlog(path, value, vpiForceFlag); +} + +/* + * Given a path, look the path name up using the PLI + * or the FLI, and release it. + */ +int uvm_hdl_release_and_read(char *path, p_vpi_vecval value) { + return uvm_hdl_set_vlog(path, value, vpiReleaseFlag); +} + +/* + * Given a path, look the path name up using the PLI + * or the FLI, and release it. + */ +int uvm_hdl_release(char *path) { + s_vpi_vecval value; + return uvm_hdl_set_vlog(path, &value, vpiReleaseFlag); +} diff --git a/test_regress/t/uvm/dpi/uvm_regex.cc b/test_regress/t/uvm/dpi/uvm_regex.cc new file mode 100644 index 000000000..bf4a13b02 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_regex.cc @@ -0,0 +1,239 @@ +//---------------------------------------------------------------------- +// Copyright 2007-2011 Mentor Graphics Corporation +// Copyright 2010-2018 Cadence Design Systems, Inc. +// Copyright 2013-2014 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + + +#include "uvm_dpi.h" +#include + + +const char uvm_re_bracket_char = '/'; +#define UVM_REGEX_MAX_LENGTH 2048 +static char uvm_re[UVM_REGEX_MAX_LENGTH+4]; + +static const char* empty_regex="/^$/"; + +//-------------------------------------------------------------------- +// uvm_re_match +// +// Match a string to a regular expression. The regex is first lookup +// up in the regex cache to see if it has already been compiled. If +// so, the compile version is retrieved from the cache. Otherwise, it +// is compiled and cached for future use. After compilation the +// matching is done using regexec(). +//-------------------------------------------------------------------- +int uvm_re_match(const char * re, const char *str) +{ + regex_t *rexp; + int err; + + // safety check. Args should never be ~null~ since this is called + // from DPI. But we'll check anyway. + if(re == NULL) + return 1; + if(str == NULL) + return 1; + + int len = strlen(re); + char * rex = &uvm_re[0]; + + if (len > UVM_REGEX_MAX_LENGTH) { + const char* err_str = "uvm_re_match : regular expression greater than max %0d: |%s|"; + char buffer[strlen(err_str) + int_str_max(10) + strlen(re)]; + sprintf(buffer, err_str, UVM_REGEX_MAX_LENGTH, re); + m_uvm_report_dpi(M_UVM_ERROR, + (char*) "UVM/DPI/REGEX_MAX", + &buffer[0], + M_UVM_NONE, + (char*)__FILE__, + __LINE__); + return 1; + } + + // we copy the regexp because we need to remove any brackets around it + strncpy(&uvm_re[0],re,UVM_REGEX_MAX_LENGTH); + if (len>1 && (re[0] == uvm_re_bracket_char) && re[len-1] == uvm_re_bracket_char) { + uvm_re[len-1] = '\0'; + rex++; + } + + rexp = (regex_t*)malloc(sizeof(regex_t)); + + if (rexp == NULL) { + m_uvm_report_dpi(M_UVM_ERROR, + (char*) "UVM/DPI/REGEX_ALLOC", + (char*) "uvm_re_match: internal memory allocation error", + M_UVM_NONE, + (char*)__FILE__, + __LINE__); + return 1; + } + + err = regcomp(rexp, rex, REG_EXTENDED); + + if (err != 0) { + regerror(err,rexp,uvm_re,UVM_REGEX_MAX_LENGTH-1); + const char * err_str = "uvm_re_match : invalid glob or regular expression: |%s||%s|"; + char buffer[strlen(err_str) + strlen(re) + strlen(uvm_re)+1]; + sprintf(buffer, err_str, re, uvm_re); + m_uvm_report_dpi(M_UVM_ERROR, + (char*) "UVM/DPI/REGEX_INV", + &buffer[0], + M_UVM_NONE, + (char*)__FILE__, + __LINE__); + regfree(rexp); + free(rexp); + return err; + } + + err = regexec(rexp, str, 0, NULL, 0); + + //vpi_printf((PLI_BYTE8*) "UVM_INFO: uvm_re_match: re=%s str=%s ERR=%0d\n",rex,str,err); + regfree(rexp); + free(rexp); + + return err; +} + + +//-------------------------------------------------------------------- +// uvm_glob_to_re +// +// Convert a glob expression to a normal regular expression. +//-------------------------------------------------------------------- + +const char * uvm_glob_to_re(const char *glob) +{ + const char *p; + int len; + + // safety check. Glob should never be ~null~ since this is called + // from DPI. But we'll check anyway. + if(glob == NULL) + return NULL; + + len = strlen(glob); + + if (len > 2040) { + const char * err_str = "uvm_re_match : glob expression greater than max 2040: |%s|"; + char buffer[strlen(err_str) + strlen(glob)+1]; + sprintf(buffer, err_str, glob); + m_uvm_report_dpi(M_UVM_ERROR, + (char*) "UVM/DPI/REGEX_MAX", + &buffer[0], + M_UVM_NONE, + (char*)__FILE__, + __LINE__); + return glob; + } + + // If either of the following cases appear then return an empty string + // + // 1. The glob string is empty (it has zero characters) + // 2. The glob string has a single character that is the + // uvm_re_bracket_char (i.e. "/") + if(len == 0 || (len == 1 && *glob == uvm_re_bracket_char)) + { + return empty_regex; + } + + // If bracketed with the /glob/, then it's already a regex + if(glob[0] == uvm_re_bracket_char && glob[len-1] == uvm_re_bracket_char) + { + strcpy(uvm_re,glob); + return &uvm_re[0]; + } + else + { + // Convert the glob to a true regular expression (Posix syntax) + len = 0; + + uvm_re[len++] = uvm_re_bracket_char; + + // ^ goes at the beginning... + if (*glob != '^') + uvm_re[len++] = '^'; + + for(p = glob; *p; p++) + { + // Replace the glob metacharacters with corresponding regular + // expression metacharacters. + switch(*p) + { + case '*': + uvm_re[len++] = '.'; + uvm_re[len++] = '*'; + break; + + case '+': + uvm_re[len++] = '.'; + uvm_re[len++] = '+'; + break; + + case '.': + uvm_re[len++] = '\\'; + uvm_re[len++] = '.'; + break; + + case '?': + uvm_re[len++] = '.'; + break; + + case '[': + uvm_re[len++] = '\\'; + uvm_re[len++] = '['; + break; + + case ']': + uvm_re[len++] = '\\'; + uvm_re[len++] = ']'; + break; + + case '(': + uvm_re[len++] = '\\'; + uvm_re[len++] = '('; + break; + + case ')': + uvm_re[len++] = '\\'; + uvm_re[len++] = ')'; + break; + + default: + uvm_re[len++] = *p; + break; + } + } + } + + // Let's check to see if the regular expression is bounded by ^ at + // the beginning and $ at the end. If not, add those characters in + // the appropriate position. + + if (uvm_re[len-1] != '$') + uvm_re[len++] = '$'; + + uvm_re[len++] = uvm_re_bracket_char; + + uvm_re[len++] = '\0'; + + return &uvm_re[0]; +} diff --git a/test_regress/t/uvm/dpi/uvm_regex.svh b/test_regress/t/uvm/dpi/uvm_regex.svh new file mode 100644 index 000000000..825a78307 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_regex.svh @@ -0,0 +1,85 @@ +//---------------------------------------------------------------------- +// Copyright 2010-2012 Mentor Graphics Corporation +// Copyright 2010-2018 Cadence Design Systems, Inc. +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//---------------------------------------------------------------------- + + + +`ifndef UVM_REGEX_NO_DPI +import "DPI-C" context function int uvm_re_match(string re, string str); +import "DPI-C" context function string uvm_glob_to_re(string glob); + +`else + +// The Verilog only version does not match regular expressions, +// it only does glob style matching. +function int uvm_re_match(string re, string str); + int e, es, s, ss; + string tmp; + e = 0; s = 0; + es = 0; ss = 0; + + if(re.len() == 0) + return 0; + + // The ^ used to be used to remove the implicit wildcard, but now we don't + // use implicit wildcard so this character is just stripped. + if(re[0] == "^") + re = re.substr(1, re.len()-1); + + //This loop is only needed when the first character of the re may not + //be a *. + while (s != str.len() && re.getc(e) != "*") begin + if ((re.getc(e) != str.getc(s)) && (re.getc(e) != "?")) + return 1; + e++; s++; + end + + while (s != str.len()) begin + if (re.getc(e) == "*") begin + e++; + if (e == re.len()) begin + return 0; + end + es = e; + ss = s+1; + end + else if (re.getc(e) == str.getc(s) || re.getc(e) == "?") begin + e++; + s++; + end + else begin + e = es; + s = ss++; + end + end + while (e < re.len() && re.getc(e) == "*") + e++; + if(e == re.len()) begin + return 0; + end + else begin + return 1; + end +endfunction + +function string uvm_glob_to_re(string glob); + return glob; +endfunction + +`endif diff --git a/test_regress/t/uvm/dpi/uvm_svcmd_dpi.c b/test_regress/t/uvm/dpi/uvm_svcmd_dpi.c new file mode 100644 index 000000000..6947613e0 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_svcmd_dpi.c @@ -0,0 +1,135 @@ +// +//------------------------------------------------------------------------------ +// Copyright 2011-2014 Mentor Graphics Corporation +// Copyright 2011-2018 Cadence Design Systems, Inc. +// Copyright 2010-2012 AMD +// Copyright 2013 NVIDIA Corporation +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//------------------------------------------------------------------------------ + +#include "uvm_dpi.h" +#include + +#define ARGV_STACK_PTR_SIZE 32 + +// the total number of arguments (minus the -f/-F minus associated filenames) +int argc_total; +// the ptr to the array of ptrs to the args +char** argv_stack=NULL; + +char ** argv_ptr=NULL; + + +void push_data(int lvl,char *entry, int cmd) { + if(cmd==0) + argc_total++; + else + *argv_ptr++=entry; +} + +// walk one level (potentially recursive) +void walk_level(int lvl, int argc, char**argv,int cmd) { + int idx; + for(idx=0; ((lvl==0) && idx0) && (*argv));idx++,argv++) { + if(strcmp(*argv, "-f") && strcmp(*argv, "-F")) { + push_data(lvl,*argv,cmd); + } else { + argv++; + idx++; + char **n=(char**) *argv; + walk_level(lvl+1,argc,++n,cmd); + } + } +} + +const char *uvm_dpi_get_next_arg_c (int init) { + s_vpi_vlog_info info; + static int idx=0; + + if(init==1) + { + // free if necessary + free(argv_stack); + argc_total=0; + + vpi_get_vlog_info(&info); + walk_level(0,info.argc,info.argv,0); + + argv_stack = (char**) malloc (sizeof(char*)*argc_total); + argv_ptr=argv_stack; + walk_level(0,info.argc,info.argv,1); + idx=0; + argv_ptr=argv_stack; + } + + if(idx++>=argc_total) + return NULL; + + return *argv_ptr++; +} + +extern char* uvm_dpi_get_tool_name_c () +{ + s_vpi_vlog_info info; + vpi_get_vlog_info(&info); + return info.product; +} + +extern char* uvm_dpi_get_tool_version_c () +{ + s_vpi_vlog_info info; + vpi_get_vlog_info(&info); + return info.version; +} + +extern regex_t* uvm_dpi_regcomp (char* pattern) +{ + regex_t* re = (regex_t*) malloc (sizeof(regex_t)); + int status = regcomp(re, pattern, REG_NOSUB|REG_EXTENDED); + if(status) + { + const char * err_str = "uvm_dpi_regcomp : Unable to compile regex: |%s|, Element 0 is: %c"; + char buffer[strlen(err_str) + strlen(pattern) + 1]; + sprintf(buffer, err_str, pattern, pattern[0]); + m_uvm_report_dpi(M_UVM_ERROR, + (char*)"UVM/DPI/REGCOMP", + &buffer[0], + M_UVM_NONE, + (char*) __FILE__, + __LINE__); + regfree(re); + free (re); + return NULL; + } + return re; +} + +extern int uvm_dpi_regexec (regex_t* re, char* str) +{ + if(!re ) + { + return 1; + } + return regexec(re, str, (size_t)0, NULL, 0); +} + +extern void uvm_dpi_regfree (regex_t* re) +{ + if(!re) return; + regfree(re); + free (re); +} diff --git a/test_regress/t/uvm/dpi/uvm_svcmd_dpi.svh b/test_regress/t/uvm/dpi/uvm_svcmd_dpi.svh new file mode 100644 index 000000000..885e17976 --- /dev/null +++ b/test_regress/t/uvm/dpi/uvm_svcmd_dpi.svh @@ -0,0 +1,65 @@ +// +//------------------------------------------------------------------------------ +// Copyright 2010-2011 Mentor Graphics Corporation +// Copyright 2013-2018 Cadence Design Systems, Inc. +// Copyright 2010-2012 AMD +// All Rights Reserved Worldwide +// +// Licensed under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in +// writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing +// permissions and limitations under the License. +//------------------------------------------------------------------------------ + +// Import DPI functions used by the interface to generate the +// lists. + +`ifndef UVM_CMDLINE_NO_DPI +import "DPI-C" function string uvm_dpi_get_next_arg_c (int init); +import "DPI-C" function string uvm_dpi_get_tool_name_c (); +import "DPI-C" function string uvm_dpi_get_tool_version_c (); + +function string uvm_dpi_get_next_arg(int init=0); + return uvm_dpi_get_next_arg_c(init); +endfunction + +function string uvm_dpi_get_tool_name(); + return uvm_dpi_get_tool_name_c(); +endfunction + +function string uvm_dpi_get_tool_version(); + return uvm_dpi_get_tool_version_c(); +endfunction + +import "DPI-C" function chandle uvm_dpi_regcomp(string regex); +import "DPI-C" function int uvm_dpi_regexec(chandle preg, string str); +import "DPI-C" function void uvm_dpi_regfree(chandle preg); + +`else +function string uvm_dpi_get_next_arg(int init=0); + return ""; +endfunction + +function string uvm_dpi_get_tool_name(); + return "?"; +endfunction + +function string uvm_dpi_get_tool_version(); + return "?"; +endfunction + + +function chandle uvm_dpi_regcomp(string regex); return null; endfunction +function int uvm_dpi_regexec(chandle preg, string str); return 0; endfunction +function void uvm_dpi_regfree(chandle preg); endfunction + +`endif From de9671d4a379b3ad45f62c71bfa29c33e4d3d7c3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 2 Aug 2025 15:03:09 -0400 Subject: [PATCH 118/252] Tests: uvm_regex.cc from upstream has clang warnings --- test_regress/t/t_uvm_dpi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test_regress/t/t_uvm_dpi.py b/test_regress/t/t_uvm_dpi.py index b4f073231..928c0be47 100755 --- a/test_regress/t/t_uvm_dpi.py +++ b/test_regress/t/t_uvm_dpi.py @@ -12,6 +12,9 @@ import vltest_bootstrap test.scenarios('vlt') test.pli_filename = "t/uvm/dpi/uvm_dpi.cc" +if re.search(r'clang', test.cxx_version): + test.skip("uvm_regex.cc from upstream has clang warnings") + test.compile( verilator_flags2=["--binary", "--build-jobs 4", "--vpi", "+incdir+t/uvm", test.pli_filename]) From 36577bb54962410c6569c2b1522b49436d69724d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 2 Aug 2025 17:02:45 -0400 Subject: [PATCH 119/252] Add check for missing 'parameter' on implicit types --- src/verilog.y | 8 +++++++- test_regress/t/t_param_implicit_bad.out | 14 ++++++++++++++ test_regress/t/t_param_implicit_bad.py | 16 ++++++++++++++++ test_regress/t/t_param_implicit_bad.v | 23 +++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test_regress/t/t_param_implicit_bad.out create mode 100755 test_regress/t/t_param_implicit_bad.py create mode 100644 test_regress/t/t_param_implicit_bad.v diff --git a/src/verilog.y b/src/verilog.y index 680c0bd93..ce6933289 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1485,6 +1485,7 @@ paramPortDeclOrArg: // IEEE: param_assignment + parameter_port_decla paramPortDeclOrArgSub { $$ = $1; } | vlTag { $$ = nullptr; } ; + paramPortDeclOrArgSub: parameter_port_declarationFrontE param_assignment { $$ = $2; } | parameter_port_declarationTypeFrontE type_assignment { $$ = $2; } @@ -1945,7 +1946,12 @@ parameter_port_declarationFrontE: // IEEE: local_ or parameter_port_declaration { /*VARRESET-in-varParam*/ // Keep previous type to handle subsequent declarations. // This rule is also used when the previous parameter is a type parameter - } + if ($1) $1->v3error("parameter port declarations require 'parameter'" + " keyword before implicit data types" + " (IEEE 1800-2023 6.20.1/A.2.1.1)\n" + + $1->warnMore() + + "... Suggest add 'parameter' before here"); + } | data_type { /*VARRESET-in-varParam*/ VARDTYPE($1); } ; diff --git a/test_regress/t/t_param_implicit_bad.out b/test_regress/t/t_param_implicit_bad.out new file mode 100644 index 000000000..9c7e1d964 --- /dev/null +++ b/test_regress/t/t_param_implicit_bad.out @@ -0,0 +1,14 @@ +%Error: t/t_param_implicit_bad.v:9:15: parameter port declarations require 'parameter' keyword before implicit data types (IEEE 1800-2023 6.20.1/A.2.1.1) + : ... Suggest add 'parameter' before here + 9 | module sub1 #([7:0] PAR1 = 1); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_param_implicit_bad.v:12:42: parameter port declarations require 'parameter' keyword before implicit data types (IEEE 1800-2023 6.20.1/A.2.1.1) + : ... Suggest add 'parameter' before here + 12 | module sub2 #(parameter real PAR1 = 1.0, signed PAR2 = 2); + | ^~~~~~ +%Error: t/t_param_implicit_bad.v:15:43: parameter port declarations require 'parameter' keyword before implicit data types (IEEE 1800-2023 6.20.1/A.2.1.1) + : ... Suggest add 'parameter' before here + 15 | module sub3 #(localparam real PAR1 = 1.0, signed PAR2 = 2); + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_param_implicit_bad.py b/test_regress/t/t_param_implicit_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_param_implicit_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_param_implicit_bad.v b/test_regress/t/t_param_implicit_bad.v new file mode 100644 index 000000000..dbbceda09 --- /dev/null +++ b/test_regress/t/t_param_implicit_bad.v @@ -0,0 +1,23 @@ +// 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 + +// IEEE parameter_port_declaration has data_type but not data_type_or_implicit + +module sub1 #([7:0] PAR1 = 1); // <--- Error: requires 'parameter' +endmodule + +module sub2 #(parameter real PAR1 = 1.0, signed PAR2 = 2); +endmodule + +module sub3 #(localparam real PAR1 = 1.0, signed PAR2 = 2); +endmodule + +module t; + sub1 sub1(); + sub2 sub2(); + sub3 sub3(); + initial $stop; +endmodule From deed20fb78738fa95ed4813977c79ede1a3b6534 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 3 Aug 2025 14:52:20 +0100 Subject: [PATCH 120/252] Fix partial DFG conversion of concat assignments (#6255) When we had a `{a, b} = ...`, and the DFG conversion of `a = ...` succeeded, but `b = ...` failed, we still used to include `a = ...` in the DFG, which then caused a spurious multi-driver error for `a` on a subsequent DFG pass, as the original `{a, b} = ...` was still present in the Ast, but we also had the extra `a = ...` from converting out of DFG on the previous pass. In this patch we only convert assignments with a concatenation on the LHS, if all target LValues can be converted into DFG. This is the proper fix for #4231 --- src/V3DfgAstToDfg.cpp | 107 +++++++++++++++----------- test_regress/t/t_hier_block_chained.v | 2 + 2 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index b6c91953f..ce9604523 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -269,46 +269,67 @@ class AstToDfgVisitor final : public VNVisitor { return {nullptr, 0}; } - // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. + // Convert assignment. Returns false if unsuccessful. bool convertAssignment(FileLine* flp, AstNode* lhsp, DfgVertex* vtxp) { - // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) - lhsp = V3Const::constifyExpensiveEdit(lhsp); + // Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'. + // There might be multiple of these if 'lhsp' is a concatenation. + struct Assignment final { + DfgVertexSplice* m_lhsp; + uint32_t m_idx; + DfgVertex* m_rhsp; + Assignment() = delete; + Assignment(DfgVertexSplice* lhsp, uint32_t idx, DfgVertex* rhsp) + : m_lhsp{lhsp} + , m_idx{idx} + , m_rhsp{rhsp} {} + }; - // Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part - if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { - AstNode* const cLhsp = concatp->lhsp(); - AstNode* const cRhsp = concatp->rhsp(); + // Convert each concatenation LHS separately, gather all assignments + // we need to do into 'assignments', return true if all LValues + // converted successfully. + std::vector assignments; + const std::function convertAllLValues + = [&](AstNode* lhsp, DfgVertex* vtxp) -> bool { + // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) + lhsp = V3Const::constifyExpensiveEdit(lhsp); - { // Convet LHS of concat + // Concatenation on the LHS, convert each parts + if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { + AstNode* const cLhsp = concatp->lhsp(); + AstNode* const cRhsp = concatp->rhsp(); + // Convert Left of concat FileLine* const lFlp = cLhsp->fileline(); DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(cLhsp)}; lVtxp->fromp(vtxp); lVtxp->lsb(cRhsp->width()); - if (!convertAssignment(flp, cLhsp, lVtxp)) return false; - } - - { // Convert RHS of concat + if (!convertAllLValues(cLhsp, lVtxp)) return false; + // Convert Rigth of concat FileLine* const rFlp = cRhsp->fileline(); DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(cRhsp)}; rVtxp->fromp(vtxp); rVtxp->lsb(0); - return convertAssignment(flp, cRhsp, rVtxp); + return convertAllLValues(cRhsp, rVtxp); + } + + // Non-concatenation, convert the LValue + const auto pair = convertLValue(lhsp); + if (!pair.first) return false; + assignments.emplace_back(pair.first, pair.second, vtxp); + return true; + }; + // Convert the given LHS assignment, give up if any LValues failed to convert + if (!convertAllLValues(lhsp, vtxp)) return false; + + // All successful, connect the drivers + for (const Assignment& a : assignments) { + if (DfgSplicePacked* const spp = a.m_lhsp->template cast()) { + spp->addDriver(flp, a.m_idx, a.m_rhsp); + } else if (DfgSpliceArray* const sap = a.m_lhsp->template cast()) { + sap->addDriver(flp, a.m_idx, a.m_rhsp); + } else { + a.m_lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE } } - - // Construct LHS - const auto pair = convertLValue(lhsp); - if (!pair.first) return false; - - // If successful connect the driver - if (DfgSplicePacked* const sPackedp = pair.first->template cast()) { - sPackedp->addDriver(flp, pair.second, vtxp); - } else if (DfgSpliceArray* const sArrayp = pair.first->template cast()) { - sArrayp->addDriver(flp, pair.second, vtxp); - } else { - lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - return true; } @@ -455,9 +476,8 @@ class AstToDfgVisitor final : public VNVisitor { const uint32_t bEnd = b.m_lsb + bWidth; const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - if (a.m_fileline->operatorCompare(*b.m_fileline) != 0 - && !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress - ) { + // Loop index often abused, so suppress + if (!varp->varp()->isUsedLoopIdx()) { AstNode* const vp = varp->varScopep() ? static_cast(varp->varScopep()) : static_cast(varp->varp()); @@ -608,22 +628,19 @@ class AstToDfgVisitor final : public VNVisitor { const uint32_t bEnd = b.m_idx + bElements; const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) { - AstNode* const vp = varp->varScopep() - ? static_cast(varp->varScopep()) - : static_cast(varp->varp()); + AstNode* const vp = varp->varScopep() ? static_cast(varp->varScopep()) + : static_cast(varp->varp()); - vp->v3warn( // - MULTIDRIVEN, - "Elements [" // - << overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName() - << sub << "' have multiple combinational drivers\n" - << a.m_fileline->warnOther() << "... Location of first driver\n" - << a.m_fileline->warnContextPrimary() << '\n' - << b.m_fileline->warnOther() << "... Location of other driver\n" - << b.m_fileline->warnContextSecondary() << vp->warnOther() - << "... Only the first driver will be respected"); - } + vp->v3warn( // + MULTIDRIVEN, + "Elements [" // + << overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName() + << sub << "' have multiple combinational drivers\n" + << a.m_fileline->warnOther() << "... Location of first driver\n" + << a.m_fileline->warnContextPrimary() << '\n' + << b.m_fileline->warnOther() << "... Location of other driver\n" + << b.m_fileline->warnContextSecondary() << vp->warnOther() + << "... Only the first driver will be respected"); // If the first driver completely covers the range of the second driver, // we can just delete the second driver completely, otherwise adjust the diff --git a/test_regress/t/t_hier_block_chained.v b/test_regress/t/t_hier_block_chained.v index 57ccbba99..d03d46706 100644 --- a/test_regress/t/t_hier_block_chained.v +++ b/test_regress/t/t_hier_block_chained.v @@ -20,7 +20,9 @@ module t (clk); reg [255:0] v1_5; reg [255:0] v1_6; reg [255:0] v1_7; + // verilator lint_off MULTIDRIVEN reg [255:0] dummy; + // verilator lint_on MULTIDRIVEN Calculate calc0(.clk(clk), .reset(reset), .v1_0(v1_0), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy)); Calculate calc1(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(v1_1), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy)); From 309129ebcf1b8b409e3651a92aca78f46c83bbc7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 3 Aug 2025 15:27:37 -0400 Subject: [PATCH 121/252] Add PARAMNODEFAULT error, for parameters without defaults. --- Changes | 1 + docs/gen/ex_PARAMNODEFAULT_faulty.rst | 7 +++++ docs/gen/ex_PARAMNODEFAULT_msg.rst | 6 ++++ docs/guide/warnings.rst | 21 ++++++++++++++ src/V3Error.h | 24 +++++++++------- src/verilog.y | 6 +++- test_regress/t/t_lint_paramnodefault.py | 18 ++++++++++++ test_regress/t/t_lint_paramnodefault.v | 18 ++++++++++++ test_regress/t/t_lint_paramnodefault_bad.out | 5 ++++ test_regress/t/t_lint_paramnodefault_bad.py | 30 ++++++++++++++++++++ test_regress/t/t_param_pattern2.v | 5 ++-- test_regress/t/t_strength_equal_strength.v | 5 ++-- 12 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 docs/gen/ex_PARAMNODEFAULT_faulty.rst create mode 100644 docs/gen/ex_PARAMNODEFAULT_msg.rst create mode 100755 test_regress/t/t_lint_paramnodefault.py create mode 100644 test_regress/t/t_lint_paramnodefault.v create mode 100644 test_regress/t/t_lint_paramnodefault_bad.out create mode 100755 test_regress/t/t_lint_paramnodefault_bad.py diff --git a/Changes b/Changes index 71fe31bf2..75d3f2c04 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,7 @@ Verilator 5.039 devel * Add ENUMITEMWIDTH error, and apply to X-extended and ranged values. * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. +* Add PARAMNODEFAULT error, for parameters without defaults. * Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. * Add error on missing forward declarations (#6206). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] diff --git a/docs/gen/ex_PARAMNODEFAULT_faulty.rst b/docs/gen/ex_PARAMNODEFAULT_faulty.rst new file mode 100644 index 000000000..e5eab758c --- /dev/null +++ b/docs/gen/ex_PARAMNODEFAULT_faulty.rst @@ -0,0 +1,7 @@ +.. comment: generated by t_lint_paramnodefault_bad +.. code-block:: sv + :linenos: + :emphasize-lines: 2 + + module sub; + parameter NODEF; //<--- Warning diff --git a/docs/gen/ex_PARAMNODEFAULT_msg.rst b/docs/gen/ex_PARAMNODEFAULT_msg.rst new file mode 100644 index 000000000..d1fa62f7c --- /dev/null +++ b/docs/gen/ex_PARAMNODEFAULT_msg.rst @@ -0,0 +1,6 @@ +.. comment: generated by t_lint_paramnodefault_bad +.. code-block:: + + %Error-PARAMNODEFAULT: example.v:1:13 Parameter without default requires ANSI-style parameter list (IEEE 1800-2023 6.20.1): 'NODEF' + 8 | parameter NODEF; + | ^~~~~ diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 9e6d534be..22e61d582 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1378,6 +1378,27 @@ List Of Warnings simulate correctly. +.. option:: PARAMNODEFAULT + + An error that a parameter is being declared that has no default value, + and this is being done in a non-ANSI block while this is only legal in + ANSI-style `#(...)` declarations. IEEE 1800-2023 6.20.1 requires this + error, but some simulators accept this syntax. + + Faulty example: + + .. include:: ../../docs/gen/ex_PARAMNODEFAULT_faulty.rst + + Results in: + + .. include:: ../../docs/gen/ex_PARAMNODEFAULT_msg.rst + + To fix the issue, move to an ANSI-style declaration. + + Suppressing this error will only suppress the IEEE-required check; it + will simulate correctly. + + .. option:: PINCONNECTEMPTY .. TODO better example diff --git a/src/V3Error.h b/src/V3Error.h index 4e2eadb64..ebb8e3fa5 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -129,6 +129,7 @@ public: NOLATCH, // No latch detected in always_latch block NONSTD, // Non-standard feature present in other sims NULLPORT, // Null port detected in module definition + PARAMNODEFAULT, // Parameter without default PINCONNECTEMPTY,// Cell pin connected by name with empty reference PINMISSING, // Cell pin not specified PINNOCONNECT, // Cell pin not connected @@ -210,15 +211,16 @@ public: "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", - "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY", - "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", - "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", - "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", "SPLITVAR", - "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", - "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", - "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", - "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", - "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; + "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PARAMNODEFAULT", + "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", + "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", + "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", + "SPECIFYIGN", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", + "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", + "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", + "USERERROR", "USERFATAL", "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", + "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", + " MAX"}; return names[m_e]; } // Warnings that default to off @@ -246,8 +248,8 @@ public: 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 == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == IMPURE - || m_e == MODMISSING || m_e == PINNOTFOUND || m_e == PKGNODECL - || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE + || m_e == MODMISSING || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND + || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE ); } // Warnings to mention manual diff --git a/src/verilog.y b/src/verilog.y index ce6933289..8f1242b03 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3276,7 +3276,11 @@ param_assignment: // ==IEEE: param_assignment // // constant_param_expression: '$' is in expr id/*new-parameter*/ variable_dimensionListE sigAttrListE exprOrDataTypeEqE { $$ = VARDONEA($1, *$1, $2, $3); - if ($4) $$->valuep($4); } + if ($4) $$->valuep($4); + else if (!GRAMMARP->m_pinAnsi) + $$->v3warn(PARAMNODEFAULT, "Parameter without default requires" + " ANSI-style parameter list (IEEE 1800-2023 6.20.1): " + << $$->prettyNameQ()); } ; list_of_param_assignments: // ==IEEE: list_of_param_assignments diff --git a/test_regress/t/t_lint_paramnodefault.py b/test_regress/t/t_lint_paramnodefault.py new file mode 100755 index 000000000..bedf95356 --- /dev/null +++ b/test_regress/t/t_lint_paramnodefault.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('vlt') + +test.compile(verilator_flags2=['-Wno-PARAMNODEFAULT']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lint_paramnodefault.v b/test_regress/t/t_lint_paramnodefault.v new file mode 100644 index 000000000..dcc4e23fa --- /dev/null +++ b/test_regress/t/t_lint_paramnodefault.v @@ -0,0 +1,18 @@ +// 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 sub; + parameter NODEF; //<--- Warning + initial begin + if (NODEF != 6) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module t; + sub #(6) sub(); +endmodule diff --git a/test_regress/t/t_lint_paramnodefault_bad.out b/test_regress/t/t_lint_paramnodefault_bad.out new file mode 100644 index 000000000..8ea5c9b84 --- /dev/null +++ b/test_regress/t/t_lint_paramnodefault_bad.out @@ -0,0 +1,5 @@ +%Error-PARAMNODEFAULT: t/t_lint_paramnodefault.v:8:13: Parameter without default requires ANSI-style parameter list (IEEE 1800-2023 6.20.1): 'NODEF' + 8 | parameter NODEF; + | ^~~~~ + ... For error description see https://verilator.org/warn/PARAMNODEFAULT?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_lint_paramnodefault_bad.py b/test_regress/t/t_lint_paramnodefault_bad.py new file mode 100755 index 000000000..a39dc08aa --- /dev/null +++ b/test_regress/t/t_lint_paramnodefault_bad.py @@ -0,0 +1,30 @@ +#!/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.top_filename = 't/t_lint_paramnodefault.v' + +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=root + "/docs/gen/ex_PARAMNODEFAULT_faulty.rst", + lines="7-8") + +test.extract(in_filename=test.golden_filename, + out_filename=root + "/docs/gen/ex_PARAMNODEFAULT_msg.rst", + lines="1-3") + +test.passes() diff --git a/test_regress/t/t_param_pattern2.v b/test_regress/t/t_param_pattern2.v index 672b40fd2..2bd1731c8 100644 --- a/test_regress/t/t_param_pattern2.v +++ b/test_regress/t/t_param_pattern2.v @@ -4,8 +4,9 @@ // without warranty, 2021 by Ryszard Rozak. // SPDX-License-Identifier: CC0-1.0 -module dut(output int x); - parameter int P [5]; +module dut + #(parameter int P [5]) + (output int x); assign x = P[2]; endmodule diff --git a/test_regress/t/t_strength_equal_strength.v b/test_regress/t/t_strength_equal_strength.v index fbbe8bdf1..eb834bc33 100644 --- a/test_regress/t/t_strength_equal_strength.v +++ b/test_regress/t/t_strength_equal_strength.v @@ -4,8 +4,9 @@ // any use, without warranty, 2022 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 -interface inter (input logic cond, output wire a); - parameter W; +interface inter + #(parameter W) + (input logic cond, output wire a); // Example: wire (weak0, weak1) [W-1:0] b = '1; assign (strong0, strong1) b = cond ? 'b0 : 'bz; From f106c1eaec0deccd42138f084b9584f8662e6a79 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 3 Aug 2025 16:57:12 -0400 Subject: [PATCH 122/252] Fix genvar check to be more strict about generate-for usage only --- src/V3LinkResolve.cpp | 20 +++++++++----- src/V3Unroll.cpp | 8 +++--- test_regress/t/t_genvar_for_bad.out | 22 ++++++++++++--- test_regress/t/t_genvar_for_bad.v | 37 ++++++++++++-------------- test_regress/t/t_genvar_misuse_bad.out | 15 ++++++----- test_regress/t/t_genvar_misuse_bad.v | 25 +++++++++++------ 6 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index c6506f530..0ad9e5e1d 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -52,7 +52,7 @@ class LinkResolveVisitor final : public VNVisitor { AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion int m_senitemCvtNum = 0; // Temporary signal counter - bool m_underGenFor = false; // Under GenFor + std::deque m_underGenFors; // Stack of GenFor underneath bool m_underGenerate = false; // Under GenFor/GenIf // VISITORS @@ -139,9 +139,16 @@ class LinkResolveVisitor final : public VNVisitor { if (nodep->varp()) { // Else due to dead code, might not have var pointer // VarRef: Resolve its reference nodep->varp()->usedParam(true); - // TODO should look for where genvar is valid, but for now catch - // just gross errors of using genvar outside any generate - if (nodep->varp()->isGenVar() && !m_underGenFor) { + // Look for where genvar is valid + bool ok = false; + for (AstGenFor* forp : m_underGenFors) { + if (ok) break; + if (forp->initsp()) + forp->initsp()->foreach([&](AstVarRef* refp) { // + if (refp->varp() == nodep->varp()) ok = true; + }); + } + if (nodep->varp()->isGenVar() && !ok) { nodep->v3error("Genvar " << nodep->prettyNameQ() << " used outside generate for loop (IEEE 1800-2023 27.4)"); @@ -503,11 +510,12 @@ class LinkResolveVisitor final : public VNVisitor { // We keep Modport's themselves around for XML dump purposes void visit(AstGenFor* nodep) override { - VL_RESTORER(m_underGenFor); VL_RESTORER(m_underGenerate); - m_underGenFor = true; m_underGenerate = true; + m_underGenFors.emplace_back(nodep); iterateChildren(nodep); + UASSERT_OBJ(!m_underGenFors.empty(), nodep, "Underflow"); + m_underGenFors.pop_back(); } void visit(AstGenIf* nodep) override { VL_RESTORER(m_underGenerate); diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 23084fe1e..a95406db9 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -124,10 +124,10 @@ class UnrollVisitor final : public VNVisitor { if (VN_IS(nodep, GenFor) && !m_forVarp->isGenVar()) { nodep->v3error("Non-genvar used in generate for: " << m_forVarp->prettyNameQ()); } else if (!VN_IS(nodep, GenFor) && m_forVarp->isGenVar()) { - nodep->v3error("Genvar not legal in non-generate for (IEEE 1800-2023 27.4): " - << m_forVarp->prettyNameQ() << '\n' - << nodep->warnMore() - << "... Suggest move for loop upwards to generate-level scope."); + // Likely impossible as V3LinkResolve will earlier throw bad genvar use error + nodep->v3error("Genvar not legal in non-generate for" // LCOV_EXCL_LINE + " (IEEE 1800-2023 27.4): " + << m_forVarp->prettyNameQ()); } if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change diff --git a/test_regress/t/t_genvar_for_bad.out b/test_regress/t/t_genvar_for_bad.out index a9b8abbad..f30acda92 100644 --- a/test_regress/t/t_genvar_for_bad.out +++ b/test_regress/t/t_genvar_for_bad.out @@ -1,6 +1,20 @@ -%Error: t/t_genvar_for_bad.v:25:13: Genvar not legal in non-generate for (IEEE 1800-2023 27.4): 't.i' - : ... Suggest move for loop upwards to generate-level scope. - 25 | for (i=0; i Date: Sun, 3 Aug 2025 17:26:51 -0400 Subject: [PATCH 123/252] Add error on too many pattern members for structure --- src/V3Width.cpp | 18 +++++++++--------- test_regress/t/t_struct_pat_toomany_bad.out | 6 ++++++ test_regress/t/t_struct_pat_toomany_bad.py | 16 ++++++++++++++++ test_regress/t/t_struct_pat_toomany_bad.v | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 test_regress/t/t_struct_pat_toomany_bad.out create mode 100755 test_regress/t/t_struct_pat_toomany_bad.py create mode 100644 test_regress/t/t_struct_pat_toomany_bad.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 7e2f4af83..109875ae2 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4680,17 +4680,17 @@ class WidthVisitor final : public VNVisitor { "Assignment pattern key not supported/understood: " << patp->keyp()->prettyTypeName()); } - } else { + } else if (memp) { // constant expr - if (memp) { - const std::pair ret - = patmap.emplace(memp, patp); - if (!ret.second) { - patp->v3error("Assignment pattern contains duplicate entry: " - << VN_AS(patp->keyp(), Text)->text()); - } - memp = VN_AS(memp->nextp(), MemberDType); + const std::pair ret = patmap.emplace(memp, patp); + if (!ret.second) { + patp->v3error("Assignment pattern contains duplicate entry: " + << VN_AS(patp->keyp(), Text)->text()); } + memp = VN_AS(memp->nextp(), MemberDType); + } else { + patp->v3error( + "Assignment pattern contains more entries than structure members"); } } while (false); diff --git a/test_regress/t/t_struct_pat_toomany_bad.out b/test_regress/t/t_struct_pat_toomany_bad.out new file mode 100644 index 000000000..b3789facf --- /dev/null +++ b/test_regress/t/t_struct_pat_toomany_bad.out @@ -0,0 +1,6 @@ +%Error: t/t_struct_pat_toomany_bad.v:12:18: Assignment pattern contains more entries than structure members + : ... note: In instance 't' + 12 | } sp = '{1, 2, 3}; + | ^ + ... 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_struct_pat_toomany_bad.py b/test_regress/t/t_struct_pat_toomany_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_struct_pat_toomany_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_struct_pat_toomany_bad.v b/test_regress/t/t_struct_pat_toomany_bad.v new file mode 100644 index 000000000..2bc918d09 --- /dev/null +++ b/test_regress/t/t_struct_pat_toomany_bad.v @@ -0,0 +1,18 @@ +// 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; + + struct packed { + int m_i; + byte m_b; + } sp = '{1, 2, 3}; // BAD, too many elements + + initial begin + $display("FAILED"); + end + +endmodule From 7d2b6bd9218110621e67fa26c7cf54885f3b695e Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 4 Aug 2025 14:29:56 +0200 Subject: [PATCH 124/252] Internals: Optimize updates of Vtogcov signals. No functional change intended. (#6110) --- include/verilated_cov.h | 54 +++++++ src/V3AstNodeOther.h | 148 +++++++++++------- src/V3AstNodes.cpp | 25 ++- src/V3Begin.cpp | 2 +- src/V3CCtors.cpp | 4 +- src/V3Class.cpp | 2 +- src/V3Clock.cpp | 4 +- src/V3Coverage.cpp | 152 ++++++++++--------- src/V3CoverageJoin.cpp | 2 +- src/V3EmitCFunc.h | 66 +++++++- src/V3EmitCHeaders.cpp | 8 + src/V3EmitCImp.cpp | 53 ++++++- src/V3EmitCSyms.cpp | 5 +- src/V3EmitV.cpp | 2 +- src/V3Inline.cpp | 2 +- src/V3Undriven.cpp | 2 +- test_regress/t/t_cover_line_wide_ternary.out | 6 +- test_regress/t/t_cover_toggle.out | 15 ++ test_regress/t/t_cover_toggle.py | 2 +- test_regress/t/t_cover_toggle.v | 15 ++ test_regress/t/t_cover_toggle__points.out | 35 +++++ test_regress/t/t_opt_const_cov.py | 2 +- 22 files changed, 448 insertions(+), 158 deletions(-) diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 0d27f76ac..31d4fac0f 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -75,6 +75,60 @@ class VerilatedCovImp; ccontextp->_insertp("hier", name, __VA_ARGS__); \ } while (false) +static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData, + const IData oldData) { + for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; +} + +static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const IData newData, + const IData oldData) { + for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; +} + +static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData, + WDataInP oldData) { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData changed = newData[i] ^ oldData[i]; + if (changed) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + *(covp + i * VL_EDATASIZE + j) += (changed >> j) & 1; + } + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic* covp, + const IData newData, const IData oldData) VL_MT_SAFE { + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_I((newData ^ oldData), i)) { + (covp + i)->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic* covp, + const IData newData, const IData oldData) VL_MT_SAFE { + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_Q((newData ^ oldData), i)) { + (covp + i)->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic* covp, + WDataInP newData, WDataInP oldData) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData changed = newData[i] ^ oldData[i]; + if (changed) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + if (VL_BITISSET_E(changed, j)) { + (covp + i * VL_EDATASIZE + j)->fetch_add(1, std::memory_order_relaxed); + } + } + } + } +} + //============================================================================= // VerilatedCov /// Per-VerilatedContext coverage data class. diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 16dcfb6fb..f18b9eb9b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -399,6 +399,52 @@ public: ASTGEN_MEMBERS_AstNodeCase; int instrCount() const override { return INSTR_COUNT_BRANCH; } }; +class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt { + // Coverage analysis point declaration + // + // [After V3CoverageJoin] Duplicate declaration to get data from instead + // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] + string m_page; // Coverage point's page tag + string m_text; // Coverage point's text + string m_hier; // Coverage point's hierarchy + int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment +public: + AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) + : AstNodeStmt(t, fl) + , m_page{page} + , m_text{comment} {} + ASTGEN_MEMBERS_AstNodeCoverDecl; + const char* broken() const override { + if (m_dataDeclp + && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing + v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); + } + return nullptr; + } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + int binNum() const { return m_binNum; } + void binNum(int flag) { m_binNum = flag; } + virtual int size() const = 0; + const string& comment() const { return m_text; } // text to insert in code + const string& page() const { return m_page; } + const string& hier() const { return m_hier; } + void hier(const string& flag) { m_hier = flag; } + void comment(const string& flag) { m_text = flag; } + bool sameNode(const AstNode* samep) const override { + const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); + return (fileline() == asamep->fileline() && hier() == asamep->hier() + && comment() == asamep->comment() && page() == asamep->page()); + } + bool isPredictOptimizable() const override { return false; } + void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } + // dataDecl nullptr means "use this one", but often you want "this" to + // indicate to get data from here + AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } + AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } +}; class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { // Cover or Assert // Parents: {statement list} @@ -2994,64 +3040,15 @@ public: return true; // SPECIAL: We don't process code after breaks } }; -class AstCoverDecl final : public AstNodeStmt { - // Coverage analysis point declaration - // - // [After V3CoverageJoin] Duplicate declaration to get data from instead - // @astgen ptr := m_dataDeclp : Optional[AstCoverDecl] - string m_page; - string m_text; - string m_hier; - string m_linescov; - int m_offset; // Offset column numbers to uniq-ify IFs - int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment -public: - AstCoverDecl(FileLine* fl, const string& page, const string& comment, const string& linescov, - int offset) - : ASTGEN_SUPER_CoverDecl(fl) - , m_page{page} - , m_text{comment} - , m_linescov{linescov} - , m_offset{offset} {} - ASTGEN_MEMBERS_AstCoverDecl; - const char* broken() const override { - if (m_dataDeclp - && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing - v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); - } - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - int binNum() const { return m_binNum; } - void binNum(int flag) { m_binNum = flag; } - int offset() const { return m_offset; } - const string& comment() const { return m_text; } // text to insert in code - const string& linescov() const { return m_linescov; } - const string& page() const { return m_page; } - const string& hier() const { return m_hier; } - void hier(const string& flag) { m_hier = flag; } - void comment(const string& flag) { m_text = flag; } - bool sameNode(const AstNode* samep) const override { - const AstCoverDecl* const asamep = VN_DBG_AS(samep, CoverDecl); - return (fileline() == asamep->fileline() && linescov() == asamep->linescov() - && hier() == asamep->hier() && comment() == asamep->comment()); - } - bool isPredictOptimizable() const override { return false; } - void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp = nodep; } - // dataDecl nullptr means "use this one", but often you want "this" to - // indicate to get data from here - AstCoverDecl* dataDeclNullp() const { return m_dataDeclp; } - AstCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } -}; class AstCoverInc final : public AstNodeStmt { // Coverage analysis point; increment coverage count + // @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock] + // @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock] + // These are expressions to which the node corresponds. Used only in toggle coverage // - // @astgen ptr := m_declp : AstCoverDecl // [After V3CoverageJoin] Declaration + // @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration public: - AstCoverInc(FileLine* fl, AstCoverDecl* declp) + AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp) : ASTGEN_SUPER_CoverInc(fl) , m_declp{declp} {} ASTGEN_MEMBERS_AstCoverInc; @@ -3065,7 +3062,7 @@ public: bool isPredictOptimizable() const override { return false; } bool isOutputter() override { return true; } bool isPure() override { return false; } - AstCoverDecl* declp() const { return m_declp; } // Where defined + AstNodeCoverDecl* declp() const { return m_declp; } // Where defined }; class AstCoverToggle final : public AstNodeStmt { // Toggle analysis of given signal @@ -3895,6 +3892,47 @@ public: : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} ASTGEN_MEMBERS_AstGenCase; }; +class AstCoverOtherDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for other than toggle types of coverage + string m_linescov; + int m_offset; // Offset column numbers to uniq-ify IFs +public: + AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, + const string& linescov, int offset) + : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) + , m_linescov{linescov} + , m_offset{offset} {} + ASTGEN_MEMBERS_AstCoverOtherDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int offset() const { return m_offset; } + int size() const override { return 1; } + const string& linescov() const { return m_linescov; } + bool sameNode(const AstNode* samep) const override { + const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); + return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); + } +}; +class AstCoverToggleDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for toggle coverage + const VNumRange m_range; // Packed array range covering each toggle bit +public: + AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, + const VNumRange& range) + : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) + , m_range{range} {} + ASTGEN_MEMBERS_AstCoverToggleDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int size() const override { return m_range.elements(); } + const VNumRange& range() const { return m_range; } + bool sameNode(const AstNode* samep) const override { + const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); + return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); + } +}; // === AstNodeCoverOrAssert === class AstAssert final : public AstNodeCoverOrAssert { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a8b488648..660c6a6c2 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2922,10 +2922,9 @@ void AstBegin::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, needProcess); dumpJsonGen(str); } -void AstCoverDecl::dump(std::ostream& str) const { +void AstNodeCoverDecl::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); if (!page().empty()) str << " page=" << page(); - if (!linescov().empty()) str << " lc=" << linescov(); if (this->dataDeclNullp()) { static bool s_recursing = false; str << " -> "; @@ -2940,12 +2939,30 @@ void AstCoverDecl::dump(std::ostream& str) const { if (binNum()) str << " bin" << std::dec << binNum(); } } -void AstCoverDecl::dumpJson(std::ostream& str) const { +void AstNodeCoverDecl::dumpJson(std::ostream& str) const { dumpJsonStrFunc(str, page); - dumpJsonStrFunc(str, linescov); dumpJsonNumFunc(str, binNum); dumpJsonGen(str); } +void AstCoverOtherDecl::dump(std::ostream& str) const { + this->AstNodeCoverDecl::dump(str); + if (!linescov().empty()) str << " lc=" << linescov(); +} +void AstCoverOtherDecl::dumpJson(std::ostream& str) const { + this->AstNodeCoverDecl::dumpJson(str); + dumpJsonStrFunc(str, linescov); +} +void AstCoverToggleDecl::dump(std::ostream& str) const { + this->AstNodeCoverDecl::dump(str); + if (range().ranged()) str << " range=[" << range().left() << ":" << range().right() << "]"; +} +void AstCoverToggleDecl::dumpJson(std::ostream& str) const { + this->AstNodeCoverDecl::dumpJson(str); + if (range().ranged()) { + dumpJsonStr(str, "range", + std::to_string(range().left()) + ":" + std::to_string(range().right())); + } +} void AstCoverInc::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); str << " -> "; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 2a9733bf1..cd6cd7fe9 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -289,7 +289,7 @@ class BeginVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Don't need to fix path in coverage statements, they're not under // any BEGINs, but V3Coverage adds them all under the module itself. iterateChildren(nodep); diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index b90f868c1..319ca6e87 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -161,10 +161,10 @@ class CCtorsVisitor final : public VNVisitor { if (v3Global.opt.coverage()) { V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE}; for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) { - if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) { + if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) { // ... else we don't have a static VlSym to be able to coverage insert UASSERT_OBJ(!VN_IS(nodep, Class), coverp, - "CoverDecl should be in class's package, not class itself"); + "NodeCoverDecl should be in class's package, not class itself"); np = coverp->backp(); configure_coverage.add(coverp->unlinkFrBack()); } diff --git a/src/V3Class.cpp b/src/V3Class.cpp index d0d6de7c2..6cd5d9b1e 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -175,7 +175,7 @@ class ClassVisitor final : public VNVisitor { // m_toScopeMoves.emplace_back(nodep, m_classScopep); //} } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Need to declare coverage in package, where we have access to symbol table iterateChildren(nodep); if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack()); diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 7dc65f903..ac7fa5ff8 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -100,11 +100,13 @@ class ClockVisitor final : public VNVisitor { // UINFOTREE(1, nodep, "", "ct"); // COVERTOGGLE(INC, ORIG, CHANGE) -> // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } - AstNode* const incp = nodep->incp()->unlinkFrBack(); + AstCoverInc* const incp = nodep->incp()->unlinkFrBack(); AstNodeExpr* const origp = nodep->origp()->unlinkFrBack(); AstNodeExpr* const changeWrp = nodep->changep()->unlinkFrBack(); AstNodeExpr* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false)); AstNodeExpr* comparedp = nullptr; + incp->toggleExprp(origp->cloneTree(false)); + incp->toggleCovExprp(changeRdp->cloneTree(false)); // Xor will optimize better than Eq, when CoverToggle has bit selects, // but can only use Xor with non-opaque types if (const AstBasicDType* const bdtypep diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 72adcf0a0..2c142ac0a 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -180,22 +180,8 @@ class CoverageVisitor final : public VNVisitor { return nullptr; } - AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& page_prefix, - const string& comment, const string& linescov, int offset, + AstCoverInc* newCoverInc(FileLine* fl, AstNodeCoverDecl* const declp, const string& trace_var_name) { - // We could use the basename of the filename to the page, but seems - // better for code from an include file to be listed under the - // module using it rather than the include file. - // Note the module name could have parameters appended, we'll consider this - // a feature as it allows for each parameterized block to be counted separately. - // Someday the user might be allowed to specify a different page suffix - const string page = page_prefix + "/" + m_modp->prettyName(); - - AstCoverDecl* const declp = new AstCoverDecl{fl, page, comment, linescov, offset}; - declp->hier(hier); - m_modp->addStmtsp(declp); - UINFO(9, "new " << declp); - AstCoverInc* const incp = new AstCoverInc{fl, declp}; if (!trace_var_name.empty() && v3Global.opt.traceCoverage() @@ -316,9 +302,12 @@ class CoverageVisitor final : public VNVisitor { iterateAndNextNull(nodep->stmtsp()); if (m_state.lineCoverageOn(nodep)) { lineTrack(nodep); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "block", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); AstNode* const newp - = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), - 0, traceNameForLine(nodep, "block")); + = newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block")); insertProcStatement(nodep, newp); } } @@ -354,9 +343,12 @@ class CoverageVisitor final : public VNVisitor { iterateChildren(nodep); if (m_state.lineCoverageOn(nodep)) { lineTrack(nodep); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "block", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); AstNode* const newp - = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), - 0, traceNameForLine(nodep, "block")); + = newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block")); insertProcStatement(nodep, newp); } } @@ -402,35 +394,23 @@ class CoverageVisitor final : public VNVisitor { } } - void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) { + void toggleVarBottom(const ToggleEnt& above, const AstVar* varp, const VNumRange& range) { const std::string hierPrefix = (m_beginHier != "") ? AstNode::prettyName(m_beginHier) + "." : ""; + AstCoverToggleDecl* const declp + = new AstCoverToggleDecl{varp->fileline(), "v_toggle/" + m_modp->prettyName(), + hierPrefix + varp->name() + above.m_comment, range}; + m_modp->addStmtsp(declp); AstCoverToggle* const newp = new AstCoverToggle{ - varp->fileline(), - newCoverInc(varp->fileline(), "", "v_toggle", - hierPrefix + varp->name() + above.m_comment, "", 0, ""), + varp->fileline(), newCoverInc(varp->fileline(), declp, ""), above.m_varRefp->cloneTree(false), above.m_chgRefp->cloneTree(false)}; m_modp->addStmtsp(newp); } void toggleVarRecurse(const AstNodeDType* const dtypep, const int depth, // per-iteration const ToggleEnt& above, const AstVar* const varp) { // Constant - if (const AstBasicDType* const bdtypep = VN_CAST(dtypep, BasicDType)) { - if (bdtypep->isRanged()) { - for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1; - ++index_docs) { - const int index_code = index_docs - bdtypep->lo(); - ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", - new AstSel{varp->fileline(), - above.m_varRefp->cloneTree(false), index_code, 1}, - new AstSel{varp->fileline(), - above.m_chgRefp->cloneTree(false), index_code, 1}}; - toggleVarBottom(newent, varp); - newent.cleanup(); - } - } else { - toggleVarBottom(above, varp); - } + if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) { + toggleVarBottom(above, varp, basicp->nrange()); } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) { const int index_code = index_docs - adtypep->lo(); @@ -543,20 +523,26 @@ class CoverageVisitor final : public VNVisitor { iterate(nodep->thenp()); lineTrack(nodep); AstNodeExpr* const thenp = nodep->thenp()->unlinkFrBack(); - nodep->thenp(new AstExprStmt{thenp->fileline(), - newCoverInc(nodep->fileline(), "", "v_branch", - "cond_then", linesCov(m_state, nodep), 0, - traceNameForLine(nodep, "cond_then")), - thenp}); + AstCoverOtherDecl* const thenDeclp + = new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(), + "cond_then", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(thenDeclp); + nodep->thenp(new AstExprStmt{ + thenp->fileline(), + newCoverInc(nodep->fileline(), thenDeclp, traceNameForLine(nodep, "cond_then")), + thenp}); m_state = lastState; createHandle(nodep); iterate(nodep->elsep()); AstNodeExpr* const elsep = nodep->elsep()->unlinkFrBack(); - nodep->elsep(new AstExprStmt{elsep->fileline(), - newCoverInc(nodep->fileline(), "", "v_branch", - "cond_else", linesCov(m_state, nodep), 1, - traceNameForLine(nodep, "cond_else")), - elsep}); + AstCoverOtherDecl* const elseDeclp + = new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(), + "cond_else", linesCov(m_state, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + nodep->elsep(new AstExprStmt{ + elsep->fileline(), + newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "cond_else")), + elsep}); m_state = lastState; } else { @@ -613,22 +599,32 @@ class CoverageVisitor final : public VNVisitor { // Normal if. Linecov shows what's inside the if (not condition that is // always executed) UINFO(4, " COVER-branch: " << nodep); - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_branch", "if", - linesCov(ifState, nodep), 0, - traceNameForLine(nodep, "if"))); + AstCoverOtherDecl* const ifDeclp + = new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(), + "if", linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(ifDeclp); + nodep->addThensp( + newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if"))); // The else has a column offset of 1 to uniquify it relative to the if // As "if" and "else" are more than one character wide, this won't overlap // another token - nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_branch", "else", - linesCov(elseState, nodep), 1, - traceNameForLine(nodep, "else"))); + AstCoverOtherDecl* const elseDeclp + = new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(), + "else", linesCov(elseState, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + nodep->addElsesp( + newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "else"))); } // If/else attributes to each block as non-branch coverage else if (first_elsif || cont_elsif) { UINFO(4, " COVER-elsif: " << nodep); if (ifState.lineCoverageOn(nodep)) { - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "elsif", - linesCov(ifState, nodep), 0, + AstCoverOtherDecl* const elsifDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "elsif", + linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(elsifDeclp); + + nodep->addThensp(newCoverInc(nodep->fileline(), elsifDeclp, traceNameForLine(nodep, "elsif"))); } // and we don't insert the else as the child if-else will do so @@ -636,14 +632,21 @@ class CoverageVisitor final : public VNVisitor { // Cover as separate blocks (not a branch as is not two-legged) if (ifState.lineCoverageOn(nodep)) { UINFO(4, " COVER-half-if: " << nodep); - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "if", - linesCov(ifState, nodep), 0, - traceNameForLine(nodep, "if"))); + AstCoverOtherDecl* const ifDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "if", + linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(ifDeclp); + nodep->addThensp( + newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if"))); } if (elseState.lineCoverageOn(nodep)) { UINFO(4, " COVER-half-el: " << nodep); - nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_line", "else", - linesCov(elseState, nodep), 1, + AstCoverOtherDecl* const elseDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "else", + linesCov(elseState, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + + nodep->addElsesp(newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "else"))); } } @@ -665,9 +668,12 @@ class CoverageVisitor final : public VNVisitor { if (m_state.lineCoverageOn(nodep)) { // if the case body didn't disable it lineTrack(nodep); UINFO(4, " COVER: " << nodep); - nodep->addStmtsp(newCoverInc(nodep->fileline(), "", "v_line", "case", - linesCov(m_state, nodep), 0, - traceNameForLine(nodep, "case"))); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "case", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); + nodep->addStmtsp( + newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "case"))); } } } @@ -680,9 +686,13 @@ class CoverageVisitor final : public VNVisitor { if (!nodep->coverincsp() && v3Global.opt.coverageUser()) { // Note the name may be overridden by V3Assert processing lineTrack(nodep); - nodep->addCoverincsp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover", - linesCov(m_state, nodep), 0, - m_beginHier + "_vlCoverageUserTrace")); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_user/" + m_modp->prettyName(), + "cover", linesCov(m_state, nodep), 0}; + declp->hier(m_beginHier); + m_modp->addStmtsp(declp); + nodep->addCoverincsp( + newCoverInc(nodep->fileline(), declp, m_beginHier + "_vlCoverageUserTrace")); } } void visit(AstStop* nodep) override { @@ -754,8 +764,10 @@ class CoverageVisitor final : public VNVisitor { } comment += ") => "; comment += (m_objective ? '1' : '0'); - AstNode* const newp - = newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name)); + AstCoverOtherDecl* const declp = new AstCoverOtherDecl{ + nodep->fileline(), "v_expr/" + m_modp->prettyName(), comment, "", 0}; + m_modp->addStmtsp(declp); + AstNode* const newp = newCoverInc(fl, declp, traceNameForLine(nodep, name)); UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch"); AstIf* const ifp = new AstIf{fl, condp, newp, nullptr}; ifp->user2(true); diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index bcdf767cb..39d73bd10 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -75,7 +75,7 @@ class CoverageJoinVisitor final : public VNVisitor { // The CoverDecl the duplicate pointed to now needs to point to the // original's data. I.e. the duplicate will get the coverage number // from the non-duplicate - AstCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp(); + AstNodeCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp(); removep->incp()->declp()->dataDeclp(datadeclp); UINFO(8, " new " << removep->incp()->declp()); // Mark the found node as a duplicate of the first node diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e489ae07c..9172013b5 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -697,7 +697,7 @@ public: } iterateChildrenConst(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstCoverOtherDecl* nodep) override { putns(nodep, "vlSelf->__vlCoverInsert("); // As Declared in emitCoverageDecl puts("&(vlSymsp->__Vcoverage["); puts(cvtToStr(nodep->dataDeclThisp()->binNum())); @@ -725,15 +725,67 @@ public: putsQuoted(nodep->linescov()); puts(");\n"); } + void visit(AstCoverToggleDecl* nodep) override { + putns(nodep, "vlSelf->__vlCoverToggleInsert("); // As Declared in emitCoverageDecl + puts(cvtToStr(nodep->range().right())); + puts(", "); + puts(cvtToStr(nodep->range().left())); + puts(", "); + puts(cvtToStr(nodep->range().ranged())); + puts(", "); + puts("&(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->dataDeclThisp()->binNum())); + puts("])"); + // If this isn't the first instantiation of this module under this + // design, don't really count the bucket, and rely on verilator_cov to + // aggregate counts. This is because Verilator combines all + // hierarchies itself, and if verilator_cov also did it, you'd end up + // with (number-of-instant) times too many counts in this bin. + puts(", first"); // Enable, passed from __Vconfigure parameter + puts(", "); + putsQuoted(protect(nodep->fileline()->filename())); + puts(", "); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(", "); + puts(cvtToStr(nodep->fileline()->firstColumn())); + puts(", "); + putsQuoted((!nodep->hier().empty() ? "." : "") + + protectWordsIf(nodep->hier(), nodep->protect())); + puts(", "); + putsQuoted(protectWordsIf(nodep->page(), nodep->protect())); + puts(", "); + putsQuoted(protectWordsIf(nodep->comment(), nodep->protect())); + puts(");\n"); + } void visit(AstCoverInc* nodep) override { - if (v3Global.opt.threads() > 1) { - putns(nodep, "vlSymsp->__Vcoverage["); - puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); - puts("].fetch_add(1, std::memory_order_relaxed);\n"); + if (nodep->declp()->size() == 1) { + if (v3Global.opt.threads() > 1) { + putns(nodep, "vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); + puts("].fetch_add(1, std::memory_order_relaxed);\n"); + } else { + putns(nodep, "++(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); + puts("]);\n"); + } } else { - putns(nodep, "++(vlSymsp->__Vcoverage["); + puts("VL_COV_TOGGLE_CHG_"); + if (v3Global.opt.threads() > 1) { + puts("MT_"); + } else { + puts("ST_"); + } + emitIQW(nodep->toggleExprp()); + puts("("); + puts(cvtToStr(nodep->declp()->size())); + puts(", "); + puts("vlSymsp->__Vcoverage + "); puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); - puts("]);\n"); + puts(", "); + iterateConst(nodep->toggleExprp()); + puts(", "); + iterateConst(nodep->toggleCovExprp()); + puts(");\n"); } } void visit(AstDisableFork* nodep) override { putns(nodep, "vlProcess->disableFork();\n"); } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index fd23589fd..ce420095f 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -181,6 +181,14 @@ class EmitCHeader final : public EmitCConstInit { "linescovp);\n"); } + if (v3Global.opt.coverageToggle() && !VN_IS(modp, Class)) { + decorateFirst(first, section); + puts("void __vlCoverToggleInsert(int begin, int end, bool ranged, "); + puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp);\n"); + } + if (v3Global.opt.savable()) { decorateFirst(first, section); puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index f8e0990bf..e2b9e0196 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -103,7 +103,7 @@ class EmitCGatherDependencies final : VNVisitorConst { addSelfDependency(nodep->selfPointer(), nodep->varp()); iterateChildrenConst(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { addSymsDependency(); iterateChildrenConst(nodep); } @@ -307,17 +307,16 @@ class EmitCImp final : EmitCFunc { splitSizeInc(10); } void emitCoverageImp() { + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this + // function. This gets around gcc slowness constructing all of the template + // arguments. if (v3Global.opt.coverage()) { puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this - // function. This gets around gcc slowness constructing all of the template - // arguments. puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); puts("const char* hierp, const char* pagep, const char* commentp, const char* " - "linescovp) " - "{\n"); + "linescovp) {\n"); if (v3Global.opt.threads() > 1) { puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); puts("uint32_t* count32p = reinterpret_cast(countp);\n"); @@ -343,6 +342,48 @@ class EmitCImp final : EmitCFunc { puts("}\n"); splitSizeInc(10); } + if (v3Global.opt.coverageToggle()) { + puts("\n// Toggle Coverage\n"); + puts("void " + prefixNameProtect(m_modp) + "::__vlCoverToggleInsert("); + puts("int begin, int end, bool ranged, "); + puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp) {\n"); + if (v3Global.opt.threads() > 1) { + puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); + } + puts("int step = (end >= begin) ? 1 : -1;\n"); + // range is inclusive + puts("for (int i = begin; i != end + step; i += step) {\n"); + if (v3Global.opt.threads() > 1) { + puts("uint32_t* count32p = reinterpret_cast(countp);\n"); + } else { + puts("uint32_t* count32p = countp;\n"); + } + // static doesn't need save-restore as is constant + puts("static uint32_t fake_zero_count = 0;\n"); + puts("std::string fullhier = std::string{VerilatedModule::name()} + hierp;\n"); + puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n"); + puts("std::string commentWithIndex = commentp;\n"); + puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n"); + // Used for second++ instantiation of identical bin + puts("if (!enable) count32p = &fake_zero_count;\n"); + puts("*count32p = 0;\n"); + puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), VerilatedModule::name(), " + "count32p,"); + puts(" \"filename\",filenamep,"); + puts(" \"lineno\",lineno,"); + puts(" \"column\",column,\n"); + puts("\"hier\",fullhier,"); + puts(" \"page\",pagep,"); + puts(" \"comment\",commentWithIndex.c_str(),"); + puts(" \"\", \"\");\n"); // linescov argument, but in toggle coverage it is always + // empty + puts("++countp;\n"); + puts("}\n"); + puts("}\n"); + splitSizeInc(10); + } } void emitDestructorImp(const AstNodeModule* modp) { puts("\n"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index c81020b64..7ad0a7f23 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -376,10 +376,11 @@ class EmitCSyms final : EmitCBaseVisitorConst { iterateChildrenConst(nodep); m_statVarScopeBytes += nodep->varp()->dtypep()->widthTotalBytes(); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Assign numbers to all bins, so we know how big of an array to use if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for - nodep->binNum(m_coverBins++); + nodep->binNum(m_coverBins); + m_coverBins += nodep->size(); } } void visit(AstCFunc* nodep) override { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index e9121517b..468417706 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -234,7 +234,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { putbs("continue"); if (!m_suppressSemi) puts(";\n"); } - void visit(AstCoverDecl*) override {} // N/A + void visit(AstNodeCoverDecl*) override {} // N/A void visit(AstCoverInc*) override {} // N/A void visit(AstCoverToggle*) override {} // N/A diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index d998bb922..82db5844d 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -458,7 +458,7 @@ class InlineRelinkVisitor final : public VNVisitor { if (afterp) nodep->addScopeEntrp(afterp); iterateChildren(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Fix path in coverage statements nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier())); iterateChildren(nodep); diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 7f1d5b21c..d1abee768 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -545,7 +545,7 @@ class UndrivenVisitor final : public VNVisitorConst { void visit(AstPrimitive*) override {} // Coverage artifacts etc shouldn't count as a sink - void visit(AstCoverDecl*) override {} + void visit(AstNodeCoverDecl*) override {} void visit(AstCoverInc*) override {} void visit(AstCoverToggle*) override {} void visit(AstTraceDecl*) override {} diff --git a/test_regress/t/t_cover_line_wide_ternary.out b/test_regress/t/t_cover_line_wide_ternary.out index 71e8134ec..d9b631b41 100644 --- a/test_regress/t/t_cover_line_wide_ternary.out +++ b/test_regress/t/t_cover_line_wide_ternary.out @@ -41,9 +41,9 @@ -000009 point: comment=block hier=top.t %000009 cyc[0] ? -000009 point: comment=cond_then hier=top.t --000009 point: comment=cond_else hier=top.t %000009 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : -000009 point: comment=cond_then hier=top.t +-000009 point: comment=cond_else hier=top.t %000009 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; -000009 point: comment=cond_else hier=top.t end @@ -54,9 +54,9 @@ +000010 point: comment=block hier=top.t 000010 cyc[0] ? +000010 point: comment=cond_then hier=top.t -+000010 point: comment=cond_else hier=top.t 000010 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : +000010 point: comment=cond_then hier=top.t ++000010 point: comment=cond_else hier=top.t 000010 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; +000010 point: comment=cond_else hier=top.t end @@ -65,9 +65,9 @@ {intf_sel_assign.foo, intf_sel_assign.bar, intf_sel_assign.baz} = 000010 cyc[0] ? +000010 point: comment=cond_then hier=top.t -+000010 point: comment=cond_else hier=top.t 000010 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : +000010 point: comment=cond_then hier=top.t ++000010 point: comment=cond_else hier=top.t 000010 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; +000010 point: comment=cond_else hier=top.t diff --git a/test_regress/t/t_cover_toggle.out b/test_regress/t/t_cover_toggle.out index 29a7dc0a1..cb27dbf59 100644 --- a/test_regress/t/t_cover_toggle.out +++ b/test_regress/t/t_cover_toggle.out @@ -49,6 +49,15 @@ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; +%000002 str_bit_t str_bit; +%000002 str_bit_t [5:2] str_bit_arr; + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -108,10 +117,16 @@ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_cover_toggle.py b/test_regress/t/t_cover_toggle.py index 45bf2c21c..6f37d0c84 100755 --- a/test_regress/t/t_cover_toggle.py +++ b/test_regress/t/t_cover_toggle.py @@ -21,7 +21,7 @@ test.inline_checks() test.file_grep_not(test.obj_dir + "/coverage.dat", "largeish") if test.vlt_all: - test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 27) + test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 13) test.run(cmd=[ os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v index e41aa437a..456df1d08 100644 --- a/test_regress/t/t_cover_toggle.v +++ b/test_regress/t/t_cover_toggle.v @@ -48,6 +48,15 @@ module t (/*AUTOARG*/ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; + str_bit_t str_bit; + str_bit_t [5:2] str_bit_arr; + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -107,10 +116,16 @@ module t (/*AUTOARG*/ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_cover_toggle__points.out b/test_regress/t/t_cover_toggle__points.out index 144f94d9f..6dc8fc51d 100644 --- a/test_regress/t/t_cover_toggle__points.out +++ b/test_regress/t/t_cover_toggle__points.out @@ -67,6 +67,35 @@ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; +%000002 str_bit_t str_bit; +-000002 point: comment=str_bit.x[3] hier=top.t +-000001 point: comment=str_bit.x[4] hier=top.t +-000001 point: comment=str_bit.x[5] hier=top.t +-000002 point: comment=str_bit.y[0] hier=top.t +%000002 str_bit_t [5:2] str_bit_arr; +-000000 point: comment=str_bit_arr[2].x[3] hier=top.t +-000000 point: comment=str_bit_arr[2].x[4] hier=top.t +-000000 point: comment=str_bit_arr[2].x[5] hier=top.t +-000000 point: comment=str_bit_arr[2].y[0] hier=top.t +-000000 point: comment=str_bit_arr[3].x[3] hier=top.t +-000000 point: comment=str_bit_arr[3].x[4] hier=top.t +-000000 point: comment=str_bit_arr[3].x[5] hier=top.t +-000000 point: comment=str_bit_arr[3].y[0] hier=top.t +-000002 point: comment=str_bit_arr[4].x[3] hier=top.t +-000001 point: comment=str_bit_arr[4].x[4] hier=top.t +-000001 point: comment=str_bit_arr[4].x[5] hier=top.t +-000000 point: comment=str_bit_arr[4].y[0] hier=top.t +-000000 point: comment=str_bit_arr[5].x[3] hier=top.t +-000000 point: comment=str_bit_arr[5].x[4] hier=top.t +-000000 point: comment=str_bit_arr[5].x[5] hier=top.t +-000000 point: comment=str_bit_arr[5].y[0] hier=top.t + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -150,10 +179,16 @@ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_opt_const_cov.py b/test_regress/t/t_opt_const_cov.py index 862db88cc..e2403b28b 100755 --- a/test_regress/t/t_opt_const_cov.py +++ b/test_regress/t/t_opt_const_cov.py @@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "-- test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 478) test.passes() From d415757b0edabf2186fc292ebc5930b5b7f1803b Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 05:05:31 +0100 Subject: [PATCH 125/252] Remove unhelpful DFG debug dumps --- src/V3Dfg.cpp | 95 ++++++++++++--------------------------------- src/V3Dfg.h | 5 --- src/V3DfgPasses.cpp | 2 - 3 files changed, 24 insertions(+), 78 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index a619ee9a7..e57f5095c 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -407,41 +407,6 @@ void DfgGraph::dumpDotFilePrefixed(const string& label) const { dumpDotFile(v3Global.debugFilename(filename) + ".dot", label); } -// Dump upstream logic cone starting from given vertex -static void dumpDotUpstreamConeFromVertex(std::ostream& os, const DfgVertex& vtx) { - // Work queue for depth first traversal starting from this vertex - std::vector queue{&vtx}; - - // Set of already visited vertices - std::unordered_set visited; - - // Depth first traversal - while (!queue.empty()) { - // Pop next work item - const DfgVertex* const itemp = queue.back(); - queue.pop_back(); - - // Mark vertex as visited - const bool isFirstEncounter = visited.insert(itemp).second; - - // If we have already visited this vertex during the traversal, then move on. - if (!isFirstEncounter) continue; - - // Enqueue all sources of this vertex. - itemp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); - - // Emit this vertex and all of its source edges - dumpDotVertexAndSourceEdges(os, *itemp); - } - - // Emit all DfgVarPacked vertices that have external references driven by this vertex - vtx.forEachSink([&](const DfgVertex& dst) { - if (const DfgVarPacked* const varVtxp = dst.cast()) { - if (varVtxp->hasNonLocalRefs()) dumpDotVertexAndSourceEdges(os, dst); - } - }); -} - // LCOV_EXCL_START // Debug function for developer use only void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, const string& name) const { @@ -454,8 +419,30 @@ void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, if (!name.empty()) *os << "graph [label=\"" << name << "\", labelloc=t, labeljust=l]\n"; *os << "graph [rankdir=LR]\n"; - // Dump the cone - dumpDotUpstreamConeFromVertex(*os, vtx); + // Work queue for depth first traversal starting from this vertex + std::vector queue{&vtx}; + + // Set of already visited vertices + std::unordered_set visited; + + // Depth first traversal + while (!queue.empty()) { + // Pop next work item + const DfgVertex* const vtxp = queue.back(); + queue.pop_back(); + + // Mark vertex as visited + const bool isFirstEncounter = visited.insert(vtxp).second; + + // If we have already visited this vertex during the traversal, then move on. + if (!isFirstEncounter) continue; + + // Enqueue all sources of this vertex. + vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); + + // Emit this vertex and all of its source edges + dumpDotVertexAndSourceEdges(*os, *vtxp); + } // Footer *os << "}\n"; @@ -465,40 +452,6 @@ void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, } // LCOV_EXCL_STOP -void DfgGraph::dumpDotAllVarConesPrefixed(const string& label) const { - const string prefix = label.empty() ? name() + "-cone-" : name() + "-" + label + "-cone-"; - forEachVertex([&](const DfgVertex& vtx) { - // Check if this vertex drives a variable referenced outside the DFG. - const DfgVarPacked* const sinkp - = vtx.findSink([](const DfgVarPacked& sink) { // - return sink.hasNonLocalRefs(); - }); - - // We only dump cones driving an externally referenced variable - if (!sinkp) return; - - // Open output file - const string coneName{prefix + sinkp->nodep()->name()}; - const string fileName{v3Global.debugFilename(coneName) + ".dot"}; - const std::unique_ptr os{V3File::new_ofstream(fileName)}; - if (os->fail()) v3fatal("Can't write file: " << fileName); - - // Header - *os << "digraph dfg {\n"; - *os << "graph [label=\"" << coneName << "\", labelloc=t, labeljust=l]\n"; - *os << "graph [rankdir=LR]\n"; - - // Dump this cone - dumpDotUpstreamConeFromVertex(*os, vtx); - - // Footer - *os << "}\n"; - - // Done with this logic cone - os->close(); - }); -} - //------------------------------------------------------------------------------ // DfgEdge //------------------------------------------------------------------------------ diff --git a/src/V3Dfg.h b/src/V3Dfg.h index cef0b5e72..6af0531ee 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -756,11 +756,6 @@ public: // 'filename'. 'name' is the name of the graph, which is included in the output. void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx, const string& name = "") const VL_MT_DISABLED; - // Dump all individual logic cones driving external variables in Graphviz format into separate - // new automatically numbered debug files. 'label' is added to the name of the graph, which is - // included in the file names and the output. This is useful for very large graphs that are - // otherwise difficult to browse visually due to their size. - void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED; }; // Specializations of privateTypeTest diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index d349551d4..9a9058090 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -523,7 +523,6 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) { ++passNumber; }; - if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input"); apply(3, "input ", [&]() {}); apply(4, "inlineVars ", [&]() { inlineVars(dfg); }); apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); }); @@ -538,5 +537,4 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) { // Accumulate patterns for reporting if (v3Global.opt.stats()) ctx.m_patternStats.accumulate(dfg); apply(4, "regularize", [&]() { regularize(dfg, ctx.m_regularizeContext); }); - if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized"); } From d2edab458e8e6c7222eca3de35a3f9ec60aa0afc Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 10:24:54 +0100 Subject: [PATCH 126/252] Refactor Dfg variable flags (#6259) Store all flags in a DfgVertexVar relating to the underlying AstVar/AstVarScope stored via AstNode::user1(). user2/user3/user4 are then usable by DFG algorithms as needed. --- src/V3Dfg.cpp | 27 ++++++------ src/V3DfgAstToDfg.cpp | 85 +++++++++++--------------------------- src/V3DfgDecomposition.cpp | 5 +-- src/V3DfgDfgToAst.cpp | 14 +++---- src/V3DfgOptimizer.cpp | 25 ++++++----- src/V3DfgPasses.cpp | 27 ++++++------ src/V3DfgVertices.h | 47 +++++++++++---------- src/astgen | 8 ++-- 8 files changed, 102 insertions(+), 136 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index e57f5095c..5f56a80d4 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -77,10 +77,6 @@ std::unique_ptr DfgGraph::clone() const { break; } } - - if (vp->hasDfgRefs()) cp->setHasDfgRefs(); - if (vp->hasModRefs()) cp->setHasModRefs(); - if (vp->hasExtRefs()) cp->setHasExtRefs(); } // Clone operation vertices for (const DfgVertex& vtx : m_opVertices) { @@ -262,8 +258,6 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange } else if (varVtxp->hasDfgRefs()) { os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow - } else if (varVtxp->keep()) { - os << ", shape=box, style=filled, fillcolor=grey"; } else { os << ", shape=box"; } @@ -290,8 +284,6 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange } else if (arrVtxp->hasDfgRefs()) { os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow - } else if (arrVtxp->keep()) { - os << ", shape=box3d, style=filled, fillcolor=grey"; } else { os << ", shape=box3d"; } @@ -597,16 +589,23 @@ DfgVertexVar* DfgVertex::getResultVar() { resp = varp; return; } + // Prefer those variables that must be kept anyway - const bool keepOld = resp->keep() || resp->hasDfgRefs(); - const bool keepNew = varp->keep() || varp->hasDfgRefs(); - if (keepOld != keepNew) { - if (!keepOld) resp = varp; + if (resp->hasExtRefs() != varp->hasExtRefs()) { + if (!resp->hasExtRefs()) resp = varp; + return; + } + if (resp->hasModWrRefs() != varp->hasModWrRefs()) { + if (!resp->hasModWrRefs()) resp = varp; + return; + } + if (resp->hasDfgRefs() != varp->hasDfgRefs()) { + if (!resp->hasDfgRefs()) resp = varp; return; } // Prefer those that already have module references - if (resp->hasModRefs() != varp->hasModRefs()) { - if (!resp->hasModRefs()) resp = varp; + if (resp->hasModRdRefs() != varp->hasModRdRefs()) { + if (!resp->hasModRdRefs()) resp = varp; return; } // Prefer the earlier one in source order diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index ce9604523..760efd9b3 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -87,8 +87,8 @@ template class AstToDfgVisitor final : public VNVisitor { // NODE STATE - // AstNode::user1p // DfgVertex for this AstNode - const VNUser1InUse m_user1InUse; + // AstNode::user2p // DfgVertex for this AstNode + const VNUser2InUse m_user2InUse; // TYPES using RootType = std::conditional_t; @@ -124,15 +124,12 @@ class AstToDfgVisitor final : public VNVisitor { } void markReferenced(AstNode* nodep) { - nodep->foreach([this](const AstVarRef* refp) { - // No need to (and in fact cannot) mark variables if: - if (!DfgVertex::isSupportedDType(refp->varp()->dtypep())) return; // unsupported type - if (refp->varp()->isSc()) return; // SystemC + nodep->foreach([](const AstVarRef* refp) { VariableType* const tgtp = getTarget(refp); - // Mark vertex as having a module reference outside current DFG - getNet(tgtp)->setHasModRefs(); - // Mark variable as written from non-DFG logic - if (refp->access().isWriteOrRW()) tgtp->user3(true); + // Mark as read from non-DFG logic + if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp); + // Mark as written from non-DFG logic + if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp); }); } @@ -144,27 +141,27 @@ class AstToDfgVisitor final : public VNVisitor { } DfgVertexVar* getNet(VariableType* vp) { - if (!vp->user1p()) { + if (!vp->user2p()) { // vp DfgVertexVar vertices are not added to m_uncommittedVertices, because we - // want to hold onto them via AstVar::user1p, and the AstVar might be referenced via + // want to hold onto them via AstVar::user2p, and the AstVar might be referenced via // multiple AstVarRef instances, so we will never revert a DfgVertexVar once // created. We will delete unconnected variable vertices at the end. if (VN_IS(vp->dtypep()->skipRefp(), UnpackArrayDType)) { DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, vp}; - vp->user1p(); + vp->user2p(); m_varArrayps.push_back(vtxp); - vp->user1p(vtxp); + vp->user2p(vtxp); } else { DfgVarPacked* const vtxp = new DfgVarPacked{*m_dfgp, vp}; m_varPackedps.push_back(vtxp); - vp->user1p(vtxp); + vp->user2p(vtxp); } } - return vp->user1u().template to(); + return vp->user2u().template to(); } DfgVertex* getVertex(AstNode* nodep) { - DfgVertex* vtxp = nodep->user1u().to(); + DfgVertex* vtxp = nodep->user2u().to(); UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex"); return vtxp; } @@ -713,42 +710,6 @@ class AstToDfgVisitor final : public VNVisitor { void visit(AstCell* nodep) override { markReferenced(nodep); } void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); } - void visit(AstVar* nodep) override { - if VL_CONSTEXPR_CXX17 (T_Scoped) { - return; - } else { - if (nodep->isSc()) return; - // No need to (and in fact cannot) handle variables with unsupported dtypes - if (!DfgVertex::isSupportedDType(nodep->dtypep())) return; - - // Mark variables with external references - if (nodep->isIO() // Ports - || nodep->user2() // Target of a hierarchical reference - || nodep->isForced() // Forced - ) { - getNet(reinterpret_cast(nodep))->setHasExtRefs(); - } - } - } - - void visit(AstVarScope* nodep) override { - if VL_CONSTEXPR_CXX17 (!T_Scoped) { - return; - } else { - if (nodep->varp()->isSc()) return; - // No need to (and in fact cannot) handle variables with unsupported dtypes - if (!DfgVertex::isSupportedDType(nodep->dtypep())) return; - - // Mark variables with external references - if (nodep->varp()->isIO() // Ports - || nodep->user2() // Target of a hierarchical reference - || nodep->varp()->isForced() // Forced - ) { - getNet(reinterpret_cast(nodep))->setHasExtRefs(); - } - } - } - void visit(AstAssignW* nodep) override { ++m_ctx.m_inputEquations; @@ -820,7 +781,7 @@ class AstToDfgVisitor final : public VNVisitor { } void visit(AstVarRef* nodep) override { - UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); if (unhandled(nodep)) return; if (nodep->access().isRW() // Cannot represent read-write references @@ -853,19 +814,19 @@ class AstToDfgVisitor final : public VNVisitor { return; } - nodep->user1p(getNet(getTarget(nodep))); + nodep->user2p(getNet(getTarget(nodep))); } void visit(AstConst* nodep) override { - UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); if (unhandled(nodep)) return; DfgVertex* const vtxp = new DfgConst{*m_dfgp, nodep->fileline(), nodep->num()}; m_uncommittedVertices.push_back(vtxp); - nodep->user1p(vtxp); + nodep->user2p(vtxp); } void visit(AstSel* nodep) override { - UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); if (unhandled(nodep)) return; iterate(nodep->fromp()); @@ -875,19 +836,19 @@ class AstToDfgVisitor final : public VNVisitor { DfgVertex* vtxp = nullptr; if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { DfgSel* const selp = new DfgSel{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; - selp->fromp(nodep->fromp()->user1u().to()); + selp->fromp(nodep->fromp()->user2u().to()); selp->lsb(constp->toUInt()); vtxp = selp; } else { iterate(nodep->lsbp()); if (m_foundUnhandled) return; DfgMux* const muxp = new DfgMux{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; - muxp->fromp(nodep->fromp()->user1u().to()); - muxp->lsbp(nodep->lsbp()->user1u().to()); + muxp->fromp(nodep->fromp()->user2u().to()); + muxp->lsbp(nodep->lsbp()->user2u().to()); vtxp = muxp; } m_uncommittedVertices.push_back(vtxp); - nodep->user1p(vtxp); + nodep->user2p(vtxp); } // The rest of the 'visit' methods are generated by 'astgen' diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 38f31288e..8a1e96095 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -330,13 +330,10 @@ class ExtractCyclicComponents final { } } UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type"); - if (vtx.hasModRefs()) clonep->setHasModRefs(); - if (vtx.hasExtRefs()) clonep->setHasExtRefs(); VertexState& cloneStatep = allocState(*clonep); cloneStatep.component = component; - // We need to mark both the original and the clone as having references in other DFGs + // Mark variable as having references in other DFGs vtx.setHasDfgRefs(); - clonep->setHasDfgRefs(); } return *clonep; } diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 7b1cab73a..6a557e088 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -129,8 +129,8 @@ template class DfgToAstVisitor final : DfgVisitor { // NODE STATE - // AstScope::user1p // The combinational AstActive under this scope - const VNUser1InUse m_user1InUse; + // AstScope::user2p // The combinational AstActive under this scope + const VNUser2InUse m_user2InUse; // TYPES using VariableType = std::conditional_t; @@ -152,28 +152,28 @@ class DfgToAstVisitor final : DfgVisitor { } static AstActive* getCombActive(AstScope* scopep) { - if (!scopep->user1p()) { + if (!scopep->user2p()) { // Try to find the existing combinational AstActive for (AstNode* nodep = scopep->blocksp(); nodep; nodep = nodep->nextp()) { AstActive* const activep = VN_CAST(nodep, Active); if (!activep) continue; if (activep->hasCombo()) { - scopep->user1p(activep); + scopep->user2p(activep); break; } } // If there isn't one, create a new one - if (!scopep->user1p()) { + if (!scopep->user2p()) { FileLine* const flp = scopep->fileline(); AstSenTree* const senTreep = new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}; AstActive* const activep = new AstActive{flp, "", senTreep}; activep->sensesStorep(senTreep); scopep->addBlocksp(activep); - scopep->user1p(activep); + scopep->user2p(activep); } } - return VN_AS(scopep->user1p(), Active); + return VN_AS(scopep->user2p(), Active); } AstNodeExpr* convertDfgVertexToAstNodeExpr(DfgVertex* vtxp) { diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 2db9a8265..dcf2a76ab 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -296,16 +296,15 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { UINFO(2, __FUNCTION__ << ":"); // NODE STATE - // AstVar::user1 -> Used by: - // - V3DfgPasses::astToDfg, - // - V3DfgPasses::eliminateVars, - // - V3DfgPasses::dfgToAst, - // AstVar::user2 -> bool: Flag indicating referenced by AstVarXRef (set just below) - // AstVar::user3, AstVarScope::user3 - // -> bool: Flag indicating written by logic not representable as DFG - // (set by V3DfgPasses::astToDfg) - const VNUser2InUse user2InUse; - const VNUser3InUse user3InUse; + // AstVar::user1, AstVarScope::user1 -> int, used as a bit-field + // - bit0: Read via AstVarXRef (hierarchical reference) + // - bit1: Written via AstVarXRef (hierarchical reference) + // - bit2: Read by logic in same module/netlist not represented in DFG + // - bit3: Written by logic in same module/netlist not represented in DFG + // - bit4: Referenced by other DFG in same module/netlist + // + // AstNode::user2/user3/user4 can be used by various DFG algorithms + const VNUser1InUse user1InUse; V3DfgContext ctx{label}; @@ -313,7 +312,11 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // Pre V3Scope application. Run on each module separately. // Mark cross-referenced variables - netlistp->foreach([](const AstVarXRef* xrefp) { xrefp->varp()->user2(true); }); + netlistp->foreach([](const AstVarXRef* xrefp) { + AstVar* const tgtp = xrefp->varp(); + if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasRdXRefs(tgtp); + if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasWrXRefs(tgtp); + }); // Run the optimization phase for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) { diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 9a9058090..664ba9420 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -294,7 +294,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { varp->varp()->isInternal(true); varp->srcp(srcp); } - varp->setHasModRefs(); + varp->setHasModRdRefs(); return varp; }(); // The previous index variable - we don't need a vertex for this @@ -314,7 +314,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { = dfg.makeNewVar(flp, name, tabDTypep, nullptr)->as(); varp->varp()->isInternal(true); varp->varp()->noReset(true); - varp->setHasModRefs(); + varp->setHasModWrRefs(); return varp; }(); @@ -417,9 +417,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // List of variables (AstVar or AstVarScope) we are replacing std::vector replacedVariables; - // AstVar::user1p() : AstVar* -> The replacement variables - // AstVarScope::user1p() : AstVarScope* -> The replacement variables - const VNUser1InUse user1InUse; + // AstVar::user2p() : AstVar* -> The replacement variables + // AstVarScope::user2p() : AstVarScope* -> The replacement variables + const VNUser2InUse user2InUse; // Process the work list while (workListp != sentinelp) { @@ -448,10 +448,11 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // Can't remove if it has external drivers if (!varp->isDrivenFullyByDfg()) continue; - // Can't remove if must be kept (including external, non module references) - if (varp->keep()) continue; - - // Can't remove if referenced in other DFGs of the same module (otherwise might rm twice) + // Can't remove if referenced external to the module/netlist + if (varp->hasExtRefs()) continue; + // Can't remove if written in the module + if (varp->hasModWrRefs()) continue; + // Can't remove if referenced in other DFGs of the same module if (varp->hasDfgRefs()) continue; // If it has multiple sinks, it can't be eliminated @@ -469,9 +470,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { UASSERT_OBJ(!varp->hasSinks(), varp, "Variable inlining should make this impossible"); // Grab the AstVar/AstVarScope AstNode* const nodep = varp->nodep(); - UASSERT_OBJ(!nodep->user1p(), nodep, "Replacement already exists"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Replacement already exists"); replacedVariables.emplace_back(nodep); - nodep->user1p(driverp->nodep()); + nodep->user2p(driverp->nodep()); } else { // Otherwise this *is* the canonical var continue; @@ -490,13 +491,13 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { if (AstModule* const modp = dfg.modulep()) { modp->foreach([&](AstVarRef* refp) { AstVar* varp = refp->varp(); - while (AstVar* const replacep = VN_AS(varp->user1p(), Var)) varp = replacep; + while (AstVar* const replacep = VN_AS(varp->user2p(), Var)) varp = replacep; refp->varp(varp); }); } else { v3Global.rootp()->foreach([&](AstVarRef* refp) { AstVarScope* vscp = refp->varScopep(); - while (AstVarScope* const replacep = VN_AS(vscp->user1p(), VarScope)) vscp = replacep; + while (AstVarScope* const replacep = VN_AS(vscp->user2p(), VarScope)) vscp = replacep; refp->varScopep(vscp); refp->varp(vscp->varp()); }); diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 11a04e5a1..6ecc63e6a 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -41,9 +41,6 @@ class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary { AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex) AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned) - bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module - bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module - bool m_hasExtRefs = false; // This AstVar is referenced from outside the module // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr. FileLine* m_driverFileLine = nullptr; @@ -62,13 +59,6 @@ public: AstNode* nodep() const { return m_varScopep ? static_cast(m_varScopep) : static_cast(m_varp); } - bool hasDfgRefs() const { return m_hasDfgRefs; } - void setHasDfgRefs() { m_hasDfgRefs = true; } - bool hasModRefs() const { return m_hasModRefs; } - void setHasModRefs() { m_hasModRefs = true; } - bool hasExtRefs() const { return m_hasExtRefs; } - void setHasExtRefs() { m_hasExtRefs = true; } - bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); } FileLine* driverFileLine() const { return m_driverFileLine; } void driverFileLine(FileLine* flp) { m_driverFileLine = flp; } @@ -77,17 +67,32 @@ public: return srcp() && !srcp()->is() && !varp()->isForced(); } - // Variable cannot be removed, even if redundant in the DfgGraph (might be used externally) - bool keep() const { - // Keep if referenced outside this module - if (hasExtRefs()) return true; - // Keep if traced - if (v3Global.opt.trace() && varp()->isTrace()) return true; - // Keep if public - if (varp()->isSigPublic()) return true; - // Keep if written in non-DFG code - if (nodep()->user3()) return true; - // Otherwise it can be removed + // Variable referenced via an AstVarXRef (hierarchical reference) + bool hasXRefs() const { return nodep()->user1() & 0x03; } + static void setHasRdXRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); } + static void setHasWrXRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); } + + // Variable referenced from Ast code in the same module/netlist + bool hasModRdRefs() const { return nodep()->user1() & 0x04; } + bool hasModWrRefs() const { return nodep()->user1() & 0x08; } + bool hasModRefs() const { return nodep()->user1() & 0x0c; } + static void setHasModRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x04); } + static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); } + void setHasModRdRefs() const { setHasModRdRefs(nodep()); } + void setHasModWrRefs() const { setHasModWrRefs(nodep()); } + + // Variable referenced from other DFG in the same module/netlist + bool hasDfgRefs() const { return nodep()->user1() & 0x10; } + void setHasDfgRefs() { nodep()->user1(nodep()->user1() | 0x10); } + + // Variable referenced outside the containing module/netlist. + bool hasExtRefs() const { + if (m_varp->isIO()) return true; // Ports + if (m_varp->isTrace()) return true; // Traced + if (m_varp->isForced()) return true; // Forced + if (hasXRefs()) return true; // Target of a hierarchical reference + if (m_varp->isPrimaryIO()) return true; // Top level ports + if (m_varp->isSigPublic()) return true; // Public return false; } }; diff --git a/src/astgen b/src/astgen index 03493426a..a6fd71e1c 100755 --- a/src/astgen +++ b/src/astgen @@ -1250,13 +1250,13 @@ def write_dfg_ast_to_dfg(filename): continue fh.write("void visit(Ast{t}* nodep) override {{\n".format(t=node.name)) - fh.write(' UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");\n\n') + fh.write(' UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");\n\n') fh.write(" if (unhandled(nodep)) return;\n\n") for i in range(node.arity): fh.write(" iterate(nodep->op{j}p());\n".format(j=i + 1)) fh.write(" if (m_foundUnhandled) return;\n") fh.write( - ' UASSERT_OBJ(nodep->op{j}p()->user1p(), nodep, "Child {j} missing Dfg vertex");\n' + ' UASSERT_OBJ(nodep->op{j}p()->user2p(), nodep, "Child {j} missing Dfg vertex");\n' .format(j=i + 1)) fh.write("\n") fh.write(" Dfg{t}* const vtxp = makeVertex(nodep, *m_dfgp);\n".format( @@ -1268,11 +1268,11 @@ def write_dfg_ast_to_dfg(filename): fh.write(" }\n\n") for i in range(node.arity): fh.write( - " vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to());\n". + " vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to());\n". format(i=i, j=i + 1)) fh.write("\n") fh.write(" m_uncommittedVertices.push_back(vtxp);\n") - fh.write(" nodep->user1p(vtxp);\n") + fh.write(" nodep->user2p(vtxp);\n") fh.write("}\n") From 78c9e7773a3a027b3c24294869b4cd46ba975732 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 11:18:33 +0100 Subject: [PATCH 127/252] Allow more variable removal in scoped DFG (#6260) --- src/V3DfgVertices.h | 11 +++++++---- test_regress/t/t_cover_toggle.py | 2 +- test_regress/t/t_inst_tree_inl0_pub1.py | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 6ecc63e6a..13370c807 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -87,10 +87,13 @@ public: // Variable referenced outside the containing module/netlist. bool hasExtRefs() const { - if (m_varp->isIO()) return true; // Ports - if (m_varp->isTrace()) return true; // Traced - if (m_varp->isForced()) return true; // Forced - if (hasXRefs()) return true; // Target of a hierarchical reference + // In scoped mode, we can ignrore some of these as they were made explicit by then + if (!m_varScopep) { + if (m_varp->isIO()) return true; // Ports + if (m_varp->isTrace()) return true; // Traced + if (m_varp->isForced()) return true; // Forced + if (hasXRefs()) return true; // Target of a hierarchical reference + } if (m_varp->isPrimaryIO()) return true; // Top level ports if (m_varp->isSigPublic()) return true; // Public return false; diff --git a/test_regress/t/t_cover_toggle.py b/test_regress/t/t_cover_toggle.py index 6f37d0c84..f8d39fb82 100755 --- a/test_regress/t/t_cover_toggle.py +++ b/test_regress/t/t_cover_toggle.py @@ -21,7 +21,7 @@ test.inline_checks() test.file_grep_not(test.obj_dir + "/coverage.dat", "largeish") if test.vlt_all: - test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 13) + test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 14) test.run(cmd=[ os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", diff --git a/test_regress/t/t_inst_tree_inl0_pub1.py b/test_regress/t/t_inst_tree_inl0_pub1.py index 4e35f9ef5..5aca960d1 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.py +++ b/test_regress/t/t_inst_tree_inl0_pub1.py @@ -42,7 +42,7 @@ if test.vlt_all: # We expect to combine sequent functions across multiple instances of # l2, l3, l4, l5. If this number drops, please confirm this has not broken. test.file_grep(test.stats, r'Optimizations, Combined CFuncs\s+(\d+)', - (91 if test.vltmt else 87)) + (107 if test.vltmt else 114)) # Everything should use relative references check_relative_refs("t", True) From 4a5935d2484478641ad114519451dded627baa93 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 12:11:02 +0100 Subject: [PATCH 128/252] Merge DFG components made acyclic into the original acyclic sub-graph. (#6261) After making a cyclic DFG component acyclic, merge that component back into the acyclic sub-graph of the input graph. This enables optimizing through the components that were made acyclic in their larger context. --- src/V3Dfg.cpp | 60 +++++++++++++++++++++++++++----------- src/V3Dfg.h | 22 +++++++++++--- src/V3DfgDecomposition.cpp | 2 -- src/V3DfgOptimizer.cpp | 28 ++++++++++-------- src/V3DfgVertices.h | 4 +-- 5 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 5f56a80d4..2a76b9c44 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -171,25 +171,51 @@ std::unique_ptr DfgGraph::clone() const { return std::unique_ptr{clonep}; } -void DfgGraph::addGraph(DfgGraph& other) { - m_size += other.m_size; - other.m_size = 0; +void DfgGraph::mergeGraphs(std::vector>&& otherps) { + if (otherps.empty()) return; - for (DfgVertexVar& vtx : other.m_varVertices) { - vtx.m_userCnt = 0; - vtx.m_graphp = this; + // NODE STATE + // AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph + const VNUser2InUse user2InUse; + + // Set up Ast Variable -> DfgVertexVar map for 'this' graph + for (DfgVertexVar& vtx : m_varVertices) vtx.nodep()->user2p(&vtx); + + // Merge in each of the other graphs + for (const std::unique_ptr& otherp : otherps) { + // Process variables + for (DfgVertexVar* const vtxp : otherp->m_varVertices.unlinkable()) { + // Variabels that are present in 'this', make them use the DfgVertexVar in 'this'. + if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to()) { + DfgVertex* const srcp = vtxp->srcp(); + UASSERT_OBJ(!srcp || !altp->srcp(), vtxp, "At most one alias should be driven"); + vtxp->replaceWith(altp); + if (srcp) altp->srcp(srcp); + VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp); + continue; + } + // Otherwise they will be moved + vtxp->nodep()->user2p(vtxp); + vtxp->m_userCnt = 0; + vtxp->m_graphp = this; + } + m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices); + // Process constants + for (DfgConst& vtx : otherp->m_constVertices) { + vtx.m_userCnt = 0; + vtx.m_graphp = this; + } + m_constVertices.splice(m_constVertices.end(), otherp->m_constVertices); + // Process operations + for (DfgVertex& vtx : otherp->m_opVertices) { + vtx.m_userCnt = 0; + vtx.m_graphp = this; + } + m_opVertices.splice(m_opVertices.end(), otherp->m_opVertices); + // Update graph sizes + m_size += otherp->m_size; + otherp->m_size = 0; } - m_varVertices.splice(m_varVertices.end(), other.m_varVertices); - for (DfgConst& vtx : other.m_constVertices) { - vtx.m_userCnt = 0; - vtx.m_graphp = this; - } - m_constVertices.splice(m_constVertices.end(), other.m_constVertices); - for (DfgVertex& vtx : other.m_opVertices) { - vtx.m_userCnt = 0; - vtx.m_graphp = this; - } - m_opVertices.splice(m_opVertices.end(), other.m_opVertices); } std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) { diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 6af0531ee..d9675b627 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -714,8 +714,9 @@ public: // Return an identical, independent copy of this graph. Vertex and edge order might differ. std::unique_ptr clone() const VL_MT_DISABLED; - // Add contents of other graph to this graph. Leaves other graph empty. - void addGraph(DfgGraph& other) VL_MT_DISABLED; + // Merge contents of other graphs into this graph. Deletes the other graphs. + // DfgVertexVar instances representing the same Ast variable are unified. + void mergeGraphs(std::vector>&& otherps) VL_MT_DISABLED; // Genarete a unique name. The provided 'prefix' and 'n' values will be part of the name, and // must be unique (as a pair) in each invocation for this graph. @@ -920,6 +921,9 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) , m_varScopep{nullptr} { UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph"); UASSERT_OBJ(!m_varp->isSc(), varp, "SystemC variable is not representable by DfgVertexVar"); + // Increment reference count + varp->user1(varp->user1() + 0x10); + UASSERT_OBJ((varp->user1() >> 4) > 0, varp, "Reference count overflow"); } DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) : DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)} @@ -927,6 +931,16 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) , m_varScopep{vscp} { UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph"); UASSERT_OBJ(!m_varp->isSc(), vscp, "SystemC variable is not representable by DfgVertexVar"); + // Increment reference count + vscp->user1(vscp->user1() + 0x10); + UASSERT_OBJ((vscp->user1() >> 4) > 0, vscp, "Reference count overflow"); +} + +DfgVertexVar::~DfgVertexVar() { + // Decrement reference count + AstNode* const nodep = this->nodep(); + nodep->user1(nodep->user1() - 0x10); + UASSERT_OBJ((nodep->user1() >> 4) >= 0, nodep, "Reference count underflow"); } //------------------------------------------------------------------------------ @@ -934,7 +948,7 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) //------------------------------------------------------------------------------ void DfgGraph::addVertex(DfgVertex& vtx) { - // Note: changes here need to be replicated in DfgGraph::addGraph + // Note: changes here need to be replicated in DfgGraph::mergeGraph ++m_size; if (DfgConst* const cVtxp = vtx.cast()) { m_constVertices.linkBack(cVtxp); @@ -948,7 +962,7 @@ void DfgGraph::addVertex(DfgVertex& vtx) { } void DfgGraph::removeVertex(DfgVertex& vtx) { - // Note: changes here need to be replicated in DfgGraph::addGraph + // Note: changes here need to be replicated in DfgGraph::mergeGraph --m_size; if (DfgConst* const cVtxp = vtx.cast()) { m_constVertices.unlink(cVtxp); diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 8a1e96095..3bb4d4581 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -332,8 +332,6 @@ class ExtractCyclicComponents final { UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type"); VertexState& cloneStatep = allocState(*clonep); cloneStatep.component = component; - // Mark variable as having references in other DFGs - vtx.setHasDfgRefs(); } return *clonep; } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index dcf2a76ab..1f5cfab20 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -243,11 +243,7 @@ static void process(DfgGraph& dfg, V3DfgContext& ctx) { std::vector> cyclicComponents = dfg.extractCyclicComponents("cyclic"); - // Split the remaining acyclic DFG into [weakly] connected components - std::vector> acyclicComponents = dfg.splitIntoComponents("acyclic"); - - // Quick sanity check - UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty"); + std::vector> madeAcyclicComponents; // Attempt to convert cyclic components into acyclic ones if (v3Global.opt.fDfgBreakCyckes()) { @@ -261,21 +257,29 @@ static void process(DfgGraph& dfg, V3DfgContext& ctx) { *it = std::move(result.first); ++it; } else { - // Result became acyclic. Move to acyclicComponents, delete original. - acyclicComponents.emplace_back(std::move(result.first)); + // Result became acyclic. Move to madeAcyclicComponents, delete original. + madeAcyclicComponents.emplace_back(std::move(result.first)); it = cyclicComponents.erase(it); } } } + // Merge those that were made acyclic back into the acyclic graph, this enables optimizing more + dfg.mergeGraphs(std::move(madeAcyclicComponents)); + + // Split the acyclic DFG into [weakly] connected components + std::vector> acyclicComponents = dfg.splitIntoComponents("acyclic"); + + // Quick sanity check + UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty"); // For each acyclic component for (auto& component : acyclicComponents) { if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); // Optimize the component V3DfgPasses::optimize(*component, ctx); - // Add back under the main DFG (we will convert everything back in one go) - dfg.addGraph(*component); } + // Merge back under the main DFG (we will convert everything back in one go) + dfg.mergeGraphs(std::move(acyclicComponents)); // Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse // the module/netlist to perform variable substitutions. Doing this by component would do @@ -287,9 +291,9 @@ static void process(DfgGraph& dfg, V3DfgContext& ctx) { if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); // Converting back to Ast assumes the 'regularize' pass was run, so we must run it V3DfgPasses::regularize(*component, ctx.m_regularizeContext); - // Add back under the main DFG (we will convert everything back in one go) - dfg.addGraph(*component); } + // Merge back under the main DFG (we will convert everything back in one go) + dfg.mergeGraphs(std::move(cyclicComponents)); } void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { @@ -301,7 +305,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // - bit1: Written via AstVarXRef (hierarchical reference) // - bit2: Read by logic in same module/netlist not represented in DFG // - bit3: Written by logic in same module/netlist not represented in DFG - // - bit4: Referenced by other DFG in same module/netlist + // - bit31-4: Reference count, how many DfgVertexVar represent this variable // // AstNode::user2/user3/user4 can be used by various DFG algorithms const VNUser1InUse user1InUse; diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 13370c807..bb6306f26 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -50,6 +50,7 @@ class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary { public: inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp); inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp); + inline ~DfgVertexVar(); ASTGEN_MEMBERS_DfgVertexVar; const std::string srcName(size_t) const override { return ""; } @@ -82,8 +83,7 @@ public: void setHasModWrRefs() const { setHasModWrRefs(nodep()); } // Variable referenced from other DFG in the same module/netlist - bool hasDfgRefs() const { return nodep()->user1() & 0x10; } - void setHasDfgRefs() { nodep()->user1(nodep()->user1() | 0x10); } + bool hasDfgRefs() const { return nodep()->user1() >> 5; } // I.e.: (nodep()->user1() >> 4) > 1 // Variable referenced outside the containing module/netlist. bool hasExtRefs() const { From 53332ae03e27d670d76867d251e17541b9828da4 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 13:03:30 +0100 Subject: [PATCH 129/252] Unify the two DFG cycle finding algorithms. (#6262) Both V3DfgBreakCycles.cpp and V3DfgDecomposition.cpp used to contain an implementation of the same algorithm to color strongly connected components. Now there is only one, and it lives in V3DfgColorSCCs.cpp. --- src/CMakeLists.txt | 1 + src/Makefile_obj.in | 1 + src/V3DfgBreakCycles.cpp | 187 +++++--------------------------- src/V3DfgColorSCCs.cpp | 156 +++++++++++++++++++++++++++ src/V3DfgDecomposition.cpp | 213 +++++++------------------------------ src/V3DfgPasses.h | 8 ++ 6 files changed, 235 insertions(+), 331 deletions(-) create mode 100644 src/V3DfgColorSCCs.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0d44b037..cd985af00 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -230,6 +230,7 @@ set(COMMON_SOURCES V3DfgAstToDfg.cpp V3DfgBreakCycles.cpp V3DfgCache.cpp + V3DfgColorSCCs.cpp V3DfgDecomposition.cpp V3DfgDfgToAst.cpp V3DfgOptimizer.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index ee5cb7617..cdec03d91 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -242,6 +242,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3DfgAstToDfg.o \ V3DfgBreakCycles.o \ V3DfgCache.o \ + V3DfgColorSCCs.o \ V3DfgDecomposition.o \ V3DfgDfgToAst.o \ V3DfgOptimizer.o \ diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 1a009d586..533d76030 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -28,135 +28,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; -// Similar algorithm used in ExtractCyclicComponents. -// This one sets DfgVertex::user(). See the static 'apply' method below. -class ColorStronglyConnectedComponents final { - static constexpr uint32_t UNASSIGNED = std::numeric_limits::max(); - - // TYPES - struct VertexState final { - uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC) - uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs - VertexState() = default; - VertexState(uint32_t i, uint32_t n) - : component{n} - , index{i} {} - }; - - // STATE - DfgGraph& m_dfg; // The input graph - uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph - uint32_t m_index = 0; // Visitation index counter - std::vector m_stack; // The stack used by the algorithm - - // METHODS - void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) { - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex");); - - // Visiting vertex - const size_t rootIndex = vtxState.index = ++m_index; - - // Visit children - vtx.forEachSink([&](DfgVertex& child) { - VertexState& childSatate = child.user(); - // If the child has not yet been visited, then continue traversal - if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); - // If the child is not in an SCC - if (childSatate.component == UNASSIGNED) { - if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; - } - }); - - if (vtxState.index == rootIndex) { - // This is the 'root' of an SCC - - // A trivial SCC contains only a single vertex - const bool isTrivial = m_stack.empty() // - || m_stack.back()->getUser().index < rootIndex; - // We also need a separate component for vertices that drive themselves (which can - // happen for input like 'assign a = a'), as we want to extract them (they are cyclic). - const bool drivesSelf = vtx.findSink([&vtx](const DfgVertex& sink) { // - return &vtx == &sink; - }); - - if (!isTrivial || drivesSelf) { - // Allocate new component - ++m_nonTrivialSCCs; - vtxState.component = m_nonTrivialSCCs; - while (!m_stack.empty()) { - VertexState& topState = m_stack.back()->getUser(); - // Only higher nodes belong to the same SCC - if (topState.index < rootIndex) break; - m_stack.pop_back(); - topState.component = m_nonTrivialSCCs; - } - } else { - // Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph. - vtxState.component = 0; - } - } else { - // Not the root of an SCC - m_stack.push_back(&vtx); - } - } - - void colorSCCs() { - // Implements Pearce's algorithm to color the strongly connected components. For reference - // see "An Improved Algorithm for Finding the Strongly Connected Components of a Directed - // Graph", David J.Pearce, 2005. - - // We know constant nodes have no input edges, so they cannot be part - // of a non-trivial SCC. Mark them as such without any real traversals. - for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0}); - - // Start traversals through variables - for (DfgVertexVar& vtx : m_dfg.varVertices()) { - VertexState& vtxState = vtx.user(); - // If it has no input or no outputs, it cannot be part of a non-trivial SCC. - if (vtx.arity() == 0 || !vtx.hasSinks()) { - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, - &vtx, "Non circular variable must be in a trivial SCC");); - vtxState.index = 0; - vtxState.component = 0; - continue; - } - // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); - } - - // Start traversals through operations - for (DfgVertex& vtx : m_dfg.opVertices()) { - VertexState& vtxState = vtx.user(); - // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); - } - } - - ColorStronglyConnectedComponents(DfgGraph& dfg) - : m_dfg{dfg} { - UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name()); - // Yet another implementation of Pearce's algorithm. - colorSCCs(); - // Re-assign user values - m_dfg.forEachVertex([](DfgVertex& vtx) { - const size_t component = vtx.getUser().component; - vtx.setUser(component); - }); - } - -public: - // Sets DfgVertex::user() for all vertext to: - // - 0, if the vertex is not part of a non-trivial strongly connected component - // and is not part of a self-loop. That is: the Vertex is not part of any cycle. - // - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. - // That is: each set of vertices that are reachable from each other will have the same - // non-zero value assigned. - // Returns the number of non-trivial SCCs (distinct cycles) - static uint32_t apply(DfgGraph& dfg) { - return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs; - } -}; - class TraceDriver final : public DfgVisitor { // TYPES @@ -191,7 +62,7 @@ class TraceDriver final : public DfgVisitor { // STATE DfgGraph& m_dfg; // The graph being processed // The strongly connected component we are trying to escape - const uint32_t m_component; + const uint64_t m_component; const bool m_aggressive; // Trace aggressively, creating intermediate ops uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex @@ -211,7 +82,7 @@ class TraceDriver final : public DfgVisitor { // taken from 'refp', but 'refp' is otherwise not used. You should // always use this to create new vertices, so unused ones (if a trace // eventually fails) can be cleaned up at the end. This also sets the - // vertex user to 0, indicating the new vertex is not part of a + // vertex user to 0, indicating the new vertex is not part of a // strongly connected component. This should always be true, as all the // vertices we create here are driven from outside the component we are // trying to escape, and will sink into that component. Given those are @@ -234,7 +105,7 @@ class TraceDriver final : public DfgVisitor { if VL_CONSTEXPR_CXX17 (std::is_same::value) { DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width}; - vtxp->template setUser(0); + vtxp->template setUser(0); m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } else { @@ -245,7 +116,7 @@ class TraceDriver final : public DfgVisitor { Vertex>::type; AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; - vtxp->template setUser(0); + vtxp->template setUser(0); m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } @@ -283,7 +154,7 @@ class TraceDriver final : public DfgVisitor { // Trace the vertex onStackr = true; - if (vtxp->getUser() != m_component) { + if (vtxp->getUser() != m_component) { // If the currently traced vertex is in a different component, // then we found what we were looking for. if (msb != vtxp->width() - 1 || lsb != 0) { @@ -564,7 +435,7 @@ class TraceDriver final : public DfgVisitor { #undef SET_RESULT // CONSTRUCTOR - TraceDriver(DfgGraph& dfg, uint32_t component, bool aggressive) + TraceDriver(DfgGraph& dfg, uint64_t component, bool aggressive) : m_dfg{dfg} , m_component{component} , m_aggressive{aggressive} { @@ -577,7 +448,7 @@ class TraceDriver final : public DfgVisitor { } public: - // Given a Vertex that is part of an SCC denoted by vtxp->user(), + // Given a Vertex that is part of an SCC denoted by vtxp->user(), // return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not // part of the same SCC. Returns nullptr if such a vertex cannot be // computed. This can add new vertices to the graph. The 'aggressive' flag @@ -586,7 +457,7 @@ public: // waste a lot of compute. static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width, bool aggressive) { - TraceDriver traceDriver{dfg, vtxp->getUser(), aggressive}; + TraceDriver traceDriver{dfg, vtxp->getUser(), aggressive}; // Find the out-of-component driver of the given vertex DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb); // Delete unused newly created vertices (these can be created if a @@ -608,7 +479,7 @@ public: class IndependentBits final : public DfgVisitor { // STATE - const uint32_t m_component; // The component the start vertex is part of + const uint64_t m_component; // The component the start vertex is part of // Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'. std::unordered_map m_vtxp2Mask; @@ -623,7 +494,7 @@ class IndependentBits final : public DfgVisitor { std::forward_as_tuple(vtxp), // std::forward_as_tuple(vtxp->fileline(), static_cast(vtxp->width()), 0)); // Initialize to all ones if the vertex is part of the same component, otherwise zeroes - if (pair.second && vtxp->getUser() == m_component) { + if (pair.second && vtxp->getUser() == m_component) { pair.first->second.setAllBits1(); } return pair.first->second; @@ -779,7 +650,7 @@ class IndependentBits final : public DfgVisitor { // CONSTRUCTOR IndependentBits(DfgGraph& dfg, DfgVertex* vtxp) - : m_component{vtxp->getUser()} { + : m_component{vtxp->getUser()} { if (v3Global.opt.debugCheck()) { m_lineCoverageFile.open( // v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() @@ -792,7 +663,7 @@ class IndependentBits final : public DfgVisitor { // Enqueue every operation vertex in the analysed component for (DfgVertex& vtx : dfg.opVertices()) { - if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + if (vtx.getUser() == m_component) workList.emplace_back(&vtx); } // While there is an item on the worklist ... @@ -833,7 +704,7 @@ class IndependentBits final : public DfgVisitor { } public: - // Given a Vertex that is part of an SCC denoted by vtxp->user(), + // Given a Vertex that is part of an SCC denoted by vtxp->user(), // compute which bits of this vertex have a value that is independent of // the current value of the Vertex itself (simple forward dataflow // analysis). Returns a bit mask where a set bit indicates that bit is @@ -852,10 +723,10 @@ public: class FixUpSelDrivers final { static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) { size_t nImprovements = 0; - const uint32_t component = vtxp->getUser(); + const uint64_t component = vtxp->getUser(); vtxp->forEachSink([&](DfgVertex& sink) { // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return; + if (sink.getUser() != component) return; // Only handle Sel DfgSel* const selp = sink.cast(); if (!selp) return; @@ -873,10 +744,10 @@ class FixUpSelDrivers final { static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) { size_t nImprovements = 0; - const uint32_t component = vtxp->getUser(); + const uint64_t component = vtxp->getUser(); vtxp->forEachSink([&](DfgVertex& sink) { // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return; + if (sink.getUser() != component) return; // Only handle ArraySels DfgArraySel* const aselp = sink.cast(); if (!aselp) return; @@ -976,7 +847,7 @@ class FixUpIndependentRanges final { AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; // Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp' - selp->setUser(vtxp->getUser()); + selp->setUser(vtxp->getUser()); // Do not connect selp->fromp yet, need to do afer replacing 'vtxp' selp->lsb(lsb); termp = selp; @@ -1032,7 +903,7 @@ class FixUpIndependentRanges final { } else { // The range is not used, just use constant 0 as a placeholder DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1}; - constp->setUser(0); + constp->setUser(0); termps.emplace_back(constp); } // Next iteration @@ -1044,7 +915,7 @@ class FixUpIndependentRanges final { if (nImprovements) { // Concatenate all the terms to create the replacement DfgVertex* replacementp = termps.front(); - const uint32_t vComp = vtxp->getUser(); + const uint64_t vComp = vtxp->getUser(); for (size_t i = 1; i < termps.size(); ++i) { DfgVertex* const termp = termps[i]; const uint32_t catWidth = replacementp->width() + termp->width(); @@ -1057,9 +928,9 @@ class FixUpIndependentRanges final { // component, it's part of that component, otherwise its not // cyclic (all terms are from outside the original component, // and feed into the original component). - const uint32_t tComp = termp->getUser(); - const uint32_t rComp = replacementp->getUser(); - catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); + const uint64_t tComp = termp->getUser(); + const uint64_t rComp = replacementp->getUser(); + catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); replacementp = catp; } @@ -1089,10 +960,10 @@ public: nImprovements += fixUpPacked(dfg, varp); } else if (varp->is()) { // For array variables, fix up element-wise - const uint32_t component = varp->getUser(); + const uint64_t component = varp->getUser(); varp->forEachSink([&](DfgVertex& sink) { // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return; + if (sink.getUser() != component) return; // Only handle ArraySels with constant index DfgArraySel* const aselp = sink.cast(); if (!aselp) return; @@ -1141,9 +1012,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Iterate while an improvement can be made and the graph is still cyclic do { - // Color SCCs (populates DfgVertex::user()) + // Color SCCs (populates DfgVertex::user()) const auto userDataInUse = res.userDataInUse(); - const uint32_t numNonTrivialSCCs = ColorStronglyConnectedComponents::apply(res); + const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(res); // Congrats if it has become acyclic if (!numNonTrivialSCCs) { @@ -1160,7 +1031,7 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Method 1: FixUpSelDrivers for (DfgVertexVar& vtx : res.varVertices()) { // If Variable is not part of a cycle, move on - const uint32_t component = vtx.getUser(); + const uint64_t component = vtx.getUser(); if (!component) continue; const size_t nFixed = FixUpSelDrivers::apply(res, &vtx); @@ -1177,7 +1048,7 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Method 2. FixUpIndependentRanges for (DfgVertexVar& vtx : res.varVertices()) { // If Variable is not part of a cycle, move on - const uint32_t component = vtx.getUser(); + const uint64_t component = vtx.getUser(); if (!component) continue; const size_t nFixed = FixUpIndependentRanges::apply(res, &vtx); diff --git a/src/V3DfgColorSCCs.cpp b/src/V3DfgColorSCCs.cpp new file mode 100644 index 000000000..95da70dbc --- /dev/null +++ b/src/V3DfgColorSCCs.cpp @@ -0,0 +1,156 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Cycle finding algorithm for DfgGraph +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Implements Pearce's algorithm to color the strongly connected components. For reference +// see "An Improved Algorithm for Finding the Strongly Connected Components of a Directed +// Graph", David J.Pearce, 2005. +// +//************************************************************************* + +#include "V3Dfg.h" +#include "V3DfgPasses.h" + +#include +#include + +// Similar algorithm used in ExtractCyclicComponents. +// This one sets DfgVertex::user(). See the static 'apply' method below. +class ColorStronglyConnectedComponents final { + static constexpr uint32_t UNASSIGNED = std::numeric_limits::max(); + + // TYPES + struct VertexState final { + uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC) + uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs + VertexState() = default; + VertexState(uint32_t i, uint32_t n) + : component{n} + , index{i} {} + }; + + // STATE + DfgGraph& m_dfg; // The input graph + uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph + uint32_t m_index = 0; // Visitation index counter + std::vector m_stack; // The stack used by the algorithm + + // METHODS + void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) { + UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex");); + + // Visiting vertex + const size_t rootIndex = vtxState.index = ++m_index; + + // Visit children + vtx.forEachSink([&](DfgVertex& child) { + VertexState& childSatate = child.user(); + // If the child has not yet been visited, then continue traversal + if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); + // If the child is not in an SCC + if (childSatate.component == UNASSIGNED) { + if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; + } + }); + + if (vtxState.index == rootIndex) { + // This is the 'root' of an SCC + + // A trivial SCC contains only a single vertex + const bool isTrivial = m_stack.empty() // + || m_stack.back()->getUser().index < rootIndex; + // We also need a separate component for vertices that drive themselves (which can + // happen for input like 'assign a = a'), as we want to extract them (they are cyclic). + const bool drivesSelf = vtx.findSink([&vtx](const DfgVertex& sink) { // + return &vtx == &sink; + }); + + if (!isTrivial || drivesSelf) { + // Allocate new component + ++m_nonTrivialSCCs; + vtxState.component = m_nonTrivialSCCs; + while (!m_stack.empty()) { + VertexState& topState = m_stack.back()->getUser(); + // Only higher nodes belong to the same SCC + if (topState.index < rootIndex) break; + m_stack.pop_back(); + topState.component = m_nonTrivialSCCs; + } + } else { + // Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph. + vtxState.component = 0; + } + } else { + // Not the root of an SCC + m_stack.push_back(&vtx); + } + } + + void colorSCCs() { + // We know constant nodes have no input edges, so they cannot be part + // of a non-trivial SCC. Mark them as such without any real traversals. + for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0}); + + // Start traversals through variables + for (DfgVertexVar& vtx : m_dfg.varVertices()) { + VertexState& vtxState = vtx.user(); + // If it has no input or no outputs, it cannot be part of a non-trivial SCC. + if (vtx.arity() == 0 || !vtx.hasSinks()) { + UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, + &vtx, "Non circular variable must be in a trivial SCC");); + vtxState.index = 0; + vtxState.component = 0; + continue; + } + // If not yet visited, start a traversal + if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + } + + // Start traversals through operations + for (DfgVertex& vtx : m_dfg.opVertices()) { + VertexState& vtxState = vtx.user(); + // If not yet visited, start a traversal + if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + } + } + + ColorStronglyConnectedComponents(DfgGraph& dfg) + : m_dfg{dfg} { + UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name()); + // Yet another implementation of Pearce's algorithm. + colorSCCs(); + // Re-assign user values + m_dfg.forEachVertex([](DfgVertex& vtx) { + const uint64_t component = vtx.getUser().component; + vtx.setUser(component); + }); + } + +public: + // Sets DfgVertex::user() for all vertext to: + // - 0, if the vertex is not part of a non-trivial strongly connected component + // and is not part of a self-loop. That is: the Vertex is not part of any cycle. + // - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. + // That is: each set of vertices that are reachable from each other will have the same + // non-zero value assigned. + // Returns the number of non-trivial SCCs (~distinct cycles) + static uint32_t apply(DfgGraph& dfg) { + return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs; + } +}; + +uint32_t V3DfgPasses::colorStronglyConnectedComponents(DfgGraph& dfg) { + return ColorStronglyConnectedComponents::apply(dfg); +} diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 3bb4d4581..214dd13c8 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -21,6 +21,7 @@ #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT #include "V3Dfg.h" +#include "V3DfgPasses.h" #include "V3File.h" #include @@ -117,173 +118,42 @@ std::vector> DfgGraph::splitIntoComponents(std::string } class ExtractCyclicComponents final { - static constexpr size_t UNASSIGNED = std::numeric_limits::max(); - // TYPES - struct VertexState final { - size_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs - size_t component = UNASSIGNED; // Result component number (0 stays in input graph) - bool merged = false; // Visited in the merging pass - VertexState(){}; + // We reuse the DfgVertex::user state set by V3DfgPasses::colorStronglyConnectedComponents. + // We sneak an extra flag into the MSB to indicate the vertex was merged already. + class VertexState final { + uint64_t& m_userr; + + public: + VertexState(DfgVertex& vtx) + : m_userr{vtx.getUser()} {} + bool merged() const { return m_userr >> 63; } + void setMerged() { m_userr |= 1ULL << 63; } + uint64_t component() const { return m_userr & ~(1ULL << 63); } + void component(uint64_t value) { m_userr = (m_userr & (1ULL << 63)) | value; } }; // STATE - - //========================================================================== - // Shared state - DfgGraph& m_dfg; // The input graph - std::deque m_stateStorage; // Container for VertexState instances const std::string m_prefix; // Component name prefix - size_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph const bool m_doExpensiveChecks = v3Global.opt.debugCheck(); - - //========================================================================== - // State for Pearce's algorithm for detecting SCCs - - size_t m_index = 0; // Visitation index counter - std::vector m_stack; // The stack used by the algorithm - - //========================================================================== - // State for extraction - // The extracted cyclic components std::vector> m_components; // Map from 'variable vertex' -> 'component index' -> 'clone in that component' - std::unordered_map> m_clones; + std::unordered_map> m_clones; // METHODS - - //========================================================================== - // Shared methods - - VertexState& state(DfgVertex& vtx) const { return *vtx.getUser(); } - - VertexState& allocState(DfgVertex& vtx) { - VertexState*& statep = vtx.user(); - UASSERT_OBJ(!statep, &vtx, "Vertex state already allocated " << cvtToHex(statep)); - m_stateStorage.emplace_back(); - statep = &m_stateStorage.back(); - return *statep; - } - - VertexState& getOrAllocState(DfgVertex& vtx) { - VertexState*& statep = vtx.user(); - if (!statep) { - m_stateStorage.emplace_back(); - statep = &m_stateStorage.back(); - } - return *statep; - } - - //========================================================================== - // Methods for Pearce's algorithm to detect strongly connected components - - void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) { - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex");); - - // Visiting vertex - const size_t rootIndex = vtxState.index = ++m_index; - - // Visit children - vtx.forEachSink([&](DfgVertex& child) { - VertexState& childSatate = getOrAllocState(child); - // If the child has not yet been visited, then continue traversal - if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); - // If the child is not in an SCC - if (childSatate.component == UNASSIGNED) { - if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; - } - }); - - if (vtxState.index == rootIndex) { - // This is the 'root' of an SCC - - // A trivial SCC contains only a single vertex - const bool isTrivial = m_stack.empty() || state(*m_stack.back()).index < rootIndex; - // We also need a separate component for vertices that drive themselves (which can - // happen for input like 'assign a = a'), as we want to extract them (they are cyclic). - const bool drivesSelf = vtx.findSink([&vtx](const DfgVertex& sink) { // - return &vtx == &sink; - }); - - if (!isTrivial || drivesSelf) { - // Allocate new component - ++m_nonTrivialSCCs; - vtxState.component = m_nonTrivialSCCs; - while (!m_stack.empty()) { - VertexState& topState = state(*m_stack.back()); - // Only higher nodes belong to the same SCC - if (topState.index < rootIndex) break; - m_stack.pop_back(); - topState.component = m_nonTrivialSCCs; - } - } else { - // Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph. - vtxState.component = 0; - } - } else { - // Not the root of an SCC - m_stack.push_back(&vtx); - } - } - - void colorSCCs() { - // Implements Pearce's algorithm to color the strongly connected components. For reference - // see "An Improved Algorithm for Finding the Strongly Connected Components of a Directed - // Graph", David J.Pearce, 2005. - - // We can leverage some properties of the input graph to gain a bit of speed. Firstly, we - // know constant nodes have no in edges, so they cannot be part of a non-trivial SCC. Mark - // them as such without starting a whole traversal. - for (DfgConst& vtx : m_dfg.constVertices()) { - VertexState& vtxState = allocState(vtx); - vtxState.index = 0; - vtxState.component = 0; - } - - // Next, we know that all SCCs must include a variable (as the input graph was converted - // from an AST, we can only have a cycle by going through a variable), so we only start - // traversals through them, and only if we know they have both in and out edges. - for (DfgVertexVar& vtx : m_dfg.varVertices()) { - if (vtx.arity() > 0 && vtx.hasSinks()) { - VertexState& vtxState = getOrAllocState(vtx); - // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); - } else { - VertexState& vtxState = getOrAllocState(vtx); - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, - &vtx, "Non circular variable must be in a trivial SCC");); - vtxState.index = 0; - vtxState.component = 0; - } - } - - // Finally, everything we did not visit through the traversal of a variable cannot be in an - // SCC, (otherwise we would have found it from a variable). - for (DfgVertex& vtx : m_dfg.opVertices()) { - VertexState& vtxState = getOrAllocState(vtx); - if (vtxState.index == UNASSIGNED) { - vtxState.index = 0; - vtxState.component = 0; - } - } - } - - //========================================================================== - // Methods for merging - - void visitMergeSCCs(DfgVertex& vtx, size_t targetComponent) { - VertexState& vtxState = state(vtx); + void visitMergeSCCs(DfgVertex& vtx, uint64_t targetComponent) { + VertexState vtxState{vtx}; // Move on if already visited - if (vtxState.merged) return; + if (vtxState.merged()) return; // Visiting vertex - vtxState.merged = true; + vtxState.setMerged(); // Assign vertex to the target component - vtxState.component = targetComponent; + vtxState.component(targetComponent); // Visit all neighbors. We stop at variable boundaries, // which is where we will split the graphs @@ -304,16 +174,14 @@ class ExtractCyclicComponents final { for (DfgVertex& vtx : m_dfg.opVertices()) { // Start DFS from each vertex that is in a non-trivial SCC, and merge everything // that is reachable from it into this component. - if (const size_t target = state(vtx).component) visitMergeSCCs(vtx, target); + if (const uint64_t target = VertexState{vtx}.component()) visitMergeSCCs(vtx, target); } } - //========================================================================== - // Methods for extraction - // Retrieve clone of vertex in the given component - DfgVertexVar& getClone(DfgVertexVar& vtx, size_t component) { - UASSERT_OBJ(state(vtx).component != component, &vtx, "Vertex is in that component"); + DfgVertexVar& getClone(DfgVertexVar& vtx, uint64_t component) { + UASSERT_OBJ(VertexState{vtx}.component() != component, &vtx, + "Vertex is in that component"); DfgVertexVar*& clonep = m_clones[&vtx][component]; if (!clonep) { if (DfgVarPacked* const pVtxp = vtx.cast()) { @@ -330,21 +198,20 @@ class ExtractCyclicComponents final { } } UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type"); - VertexState& cloneStatep = allocState(*clonep); - cloneStatep.component = component; + clonep->setUser(component); } return *clonep; } // Fix edges that cross components void fixEdges(DfgVertexVar& vtx) { - const size_t component = state(vtx).component; + const uint64_t component = VertexState{vtx}.component(); // Fix up sources in a different component vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) { DfgVertex* const srcp = edge.sourcep(); if (!srcp) return; - const size_t sourceComponent = state(*srcp).component; + const uint64_t sourceComponent = VertexState{*srcp}.component(); // Same component is OK if (sourceComponent == component) return; // Relink the source to write the clone @@ -354,7 +221,7 @@ class ExtractCyclicComponents final { // Fix up sinks in a different component vtx.forEachSinkEdge([&](DfgEdge& edge) { - const size_t sinkComponent = state(*edge.sinkp()).component; + const uint64_t sinkComponent = VertexState{*edge.sinkp()}.component(); // Same component is OK if (sinkComponent == component) return; // Relink the sink to read the clone @@ -366,7 +233,7 @@ class ExtractCyclicComponents final { void moveVertices(DfgVertex::List& list) { for (DfgVertex* const vtxp : list.unlinkable()) { DfgVertex& vtx = *vtxp; - if (const size_t component = state(vtx).component) { + if (const uint64_t component = VertexState{vtx}.component()) { m_dfg.removeVertex(vtx); m_components[component - 1]->addVertex(vtx); } @@ -378,15 +245,15 @@ class ExtractCyclicComponents final { // - Edges only cross components at variable boundaries // - Variable vertex sources are all connected. dfg.forEachVertex([&](DfgVertex& vtx) { - const size_t component = state(vtx).component; + const uint64_t component = VertexState{vtx}.component(); vtx.forEachSource([&](DfgVertex& src) { if (src.is()) return; // OK to cross at variables - UASSERT_OBJ(component == state(src).component, &vtx, + UASSERT_OBJ(component == VertexState{src}.component(), &vtx, "Edge crossing components without variable involvement"); }); vtx.forEachSink([&](DfgVertex& snk) { if (snk.is()) return; // OK to cross at variables - UASSERT_OBJ(component == state(snk).component, &vtx, + UASSERT_OBJ(component == VertexState{snk}.component(), &vtx, "Edge crossing components without variable involvement"); }); }); @@ -408,10 +275,10 @@ class ExtractCyclicComponents final { }); } - void extractComponents() { + void extractComponents(uint32_t numNonTrivialSCCs) { // Allocate result graphs - m_components.resize(m_nonTrivialSCCs); - for (size_t i = 0; i < m_nonTrivialSCCs; ++i) { + m_components.resize(numNonTrivialSCCs); + for (uint32_t i = 0; i < numNonTrivialSCCs; ++i) { m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)}); } @@ -450,17 +317,17 @@ class ExtractCyclicComponents final { explicit ExtractCyclicComponents(DfgGraph& dfg, const std::string& label) : m_dfg{dfg} , m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} { - // VertexState is stored as user data + // DfgVertex::user is set to the SCC number by colorStronglyConnectedComponents, + // Then we use VertexState to handle the MSB as an extra flag. const auto userDataInUse = dfg.userDataInUse(); // Find all the non-trivial SCCs (and trivial cycles) in the graph - colorSCCs(); - // If the graph was acyclic (which should be the common case), - // there will be no non-trivial SCCs, so we are done. - if (!m_nonTrivialSCCs) return; + const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg); + // If the graph was acyclic (which should be the common case), then we are done. + if (!numNonTrivialSCCs) return; // Ensure that component boundaries are always at variables, by merging SCCs mergeSCCs(); // Extract the components - extractComponents(); + extractComponents(numNonTrivialSCCs); } public: diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index b17fa1002..21311ae8f 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -60,6 +60,14 @@ void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED; // Construct binary to oneHot decoders void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED; +// Sets DfgVertex::user() for all vertext to: +// - 0, if the vertex is not part of a non-trivial strongly connected component +// and is not part of a self-loop. That is: the Vertex is not part of any cycle. +// - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. +// That is: each set of vertices that are reachable from each other will have the same +// non-zero value assigned. +// Returns the number of non-trivial SCCs (distinct cycles) +uint32_t colorStronglyConnectedComponents(DfgGraph&) VL_MT_DISABLED; // Common subexpression elimination void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED; // Inline fully driven variables From 8c9a4fec83d3ce42f4c02b2bb6d71cfe3bdfcfd8 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 11:12:01 +0100 Subject: [PATCH 130/252] Emit memory addresses of vertices in Dfg dumps --- src/V3Dfg.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 2a76b9c44..60167ed2e 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -268,7 +268,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { AstNode* const nodep = varVtxp->nodep(); AstVar* const varp = varVtxp->varp(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << "\n"; + os << " [label=\"" << nodep->name() << '\n'; + os << cvtToHex(varVtxp) << '\n'; varVtxp->dtypep()->dumpSmall(os); os << " / F" << varVtxp->fanout() << '"'; @@ -295,7 +296,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { AstNode* const nodep = arrVtxp->nodep(); AstVar* const varp = arrVtxp->varp(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << "\n"; + os << " [label=\"" << nodep->name() << '\n'; + os << cvtToHex(arrVtxp) << '\n'; arrVtxp->dtypep()->dumpSmall(os); os << " / F" << arrVtxp->fanout() << '"'; if (varp->direction() == VDirection::INPUT) { @@ -323,11 +325,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { os << toDotId(vtx); os << " [label=\""; if (num.width() <= 32 && !num.isSigned()) { - os << constVtxp->width() << "'d" << num.toUInt() << "\n"; - os << constVtxp->width() << "'h" << std::hex << num.toUInt() << std::dec; + os << constVtxp->width() << "'d" << num.toUInt() << '\n'; + os << constVtxp->width() << "'h" << std::hex << num.toUInt() << std::dec << '\n'; } else { - os << num.ascii(); + os << num.ascii() << '\n'; } + os << cvtToHex(constVtxp) << '\n'; os << '"'; os << ", shape=plain"; os << "]\n"; @@ -338,7 +341,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { const uint32_t lsb = selVtxp->lsb(); const uint32_t msb = lsb + selVtxp->width() - 1; os << toDotId(vtx); - os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\n"; + os << " [label=\"SEL _[" << msb << ":" << lsb << "]\n"; + os << cvtToHex(selVtxp) << '\n'; vtx.dtypep()->dumpSmall(os); os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { @@ -352,7 +356,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { if (vtx.is()) { os << toDotId(vtx); - os << " [label=\"" << vtx.typeName() << "\n"; + os << " [label=\"" << vtx.typeName() << '\n'; + os << cvtToHex(&vtx) << '\n'; vtx.dtypep()->dumpSmall(os); os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { @@ -365,7 +370,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } os << toDotId(vtx); - os << " [label=\"" << vtx.typeName() << "\n"; + os << " [label=\"" << vtx.typeName() << '\n'; + os << cvtToHex(&vtx) << '\n'; vtx.dtypep()->dumpSmall(os); os << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { @@ -380,7 +386,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, const string& headlabel) { os << toDotId(*edge.sourcep()) << " -> " << toDotId(*edge.sinkp()); if (!headlabel.empty()) os << " [headlabel=\"" << headlabel << "\"]"; - os << "\n"; + os << '\n'; } // Dump one DfgVertex and all of its source DfgEdges in Graphviz format From 158a54a7ffab913d10d6864d119856fe604f9263 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 14:29:33 +0100 Subject: [PATCH 131/252] Defer deletion of eliminated Ast variables until end of Dfg pass. (#6263) This is currently unnecessary but future patch will depend on it. --- src/V3DfgContext.h | 4 + src/V3DfgOptimizer.cpp | 210 +++++++++++++++++++++-------------------- src/V3DfgPasses.cpp | 13 +-- 3 files changed, 118 insertions(+), 109 deletions(-) diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index 459aae2fd..27e406b6b 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -180,6 +180,7 @@ class V3DfgEliminateVarsContext final : public V3DfgSubContext { public: // STATE + std::vector m_deleteps; // AstVar/AstVarScope that can be deleted at the end VDouble0 m_varsReplaced; // Number of variables replaced VDouble0 m_varsRemoved; // Number of variables removed @@ -187,6 +188,9 @@ private: V3DfgEliminateVarsContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "EliminateVars"} {} ~V3DfgEliminateVarsContext() { + for (AstNode* const nodep : m_deleteps) { + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + } addStat("variables replaced", m_varsReplaced); addStat("variables removed", m_varsRemoved); } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 1f5cfab20..327dd4787 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -236,69 +236,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) { V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeEitherLevel() >= 3); } -static void process(DfgGraph& dfg, V3DfgContext& ctx) { - // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a - // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of - // small cycles. - std::vector> cyclicComponents - = dfg.extractCyclicComponents("cyclic"); - - std::vector> madeAcyclicComponents; - - // Attempt to convert cyclic components into acyclic ones - if (v3Global.opt.fDfgBreakCyckes()) { - for (auto it = cyclicComponents.begin(); it != cyclicComponents.end();) { - auto result = V3DfgPasses::breakCycles(**it, ctx); - if (!result.first) { - // No improvement, moving on. - ++it; - } else if (!result.second) { - // Improved, but still cyclic. Replace the original cyclic component. - *it = std::move(result.first); - ++it; - } else { - // Result became acyclic. Move to madeAcyclicComponents, delete original. - madeAcyclicComponents.emplace_back(std::move(result.first)); - it = cyclicComponents.erase(it); - } - } - } - // Merge those that were made acyclic back into the acyclic graph, this enables optimizing more - dfg.mergeGraphs(std::move(madeAcyclicComponents)); - - // Split the acyclic DFG into [weakly] connected components - std::vector> acyclicComponents = dfg.splitIntoComponents("acyclic"); - - // Quick sanity check - UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty"); - - // For each acyclic component - for (auto& component : acyclicComponents) { - if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); - // Optimize the component - V3DfgPasses::optimize(*component, ctx); - } - // Merge back under the main DFG (we will convert everything back in one go) - dfg.mergeGraphs(std::move(acyclicComponents)); - - // Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse - // the module/netlist to perform variable substitutions. Doing this by component would do - // redundant traversals and can be extremely slow when we have many components. - V3DfgPasses::eliminateVars(dfg, ctx.m_eliminateVarsContext); - - // For each cyclic component - for (auto& component : cyclicComponents) { - if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); - // Converting back to Ast assumes the 'regularize' pass was run, so we must run it - V3DfgPasses::regularize(*component, ctx.m_regularizeContext); - } - // Merge back under the main DFG (we will convert everything back in one go) - dfg.mergeGraphs(std::move(cyclicComponents)); -} - -void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { - UINFO(2, __FUNCTION__ << ":"); - +class DataflowOptimize final { // NODE STATE // AstVar::user1, AstVarScope::user1 -> int, used as a bit-field // - bit0: Read via AstVarXRef (hierarchical reference) @@ -308,55 +246,121 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // - bit31-4: Reference count, how many DfgVertexVar represent this variable // // AstNode::user2/user3/user4 can be used by various DFG algorithms - const VNUser1InUse user1InUse; + const VNUser1InUse m_user1InUse; - V3DfgContext ctx{label}; + // STATE + V3DfgContext m_ctx; // The context holding values that need to persist across multiple graphs - if (!netlistp->topScopep()) { - // Pre V3Scope application. Run on each module separately. + void optimize(DfgGraph& dfg) { + if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "whole-input"); - // Mark cross-referenced variables - netlistp->foreach([](const AstVarXRef* xrefp) { - AstVar* const tgtp = xrefp->varp(); - if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasRdXRefs(tgtp); - if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasWrXRefs(tgtp); - }); + // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a + // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of + // small cycles. + std::vector> cyclicComponents + = dfg.extractCyclicComponents("cyclic"); - // Run the optimization phase - for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) { - // Only optimize proper modules - AstModule* const modp = VN_CAST(nodep, Module); - if (!modp) continue; - - UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'"); - ++ctx.m_modules; - - // Build the DFG of this module - const std::unique_ptr dfg{V3DfgPasses::astToDfg(*modp, ctx)}; - if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input"); - - // Actually process the graph - process(*dfg, ctx); - - // Convert back to Ast - if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized"); - V3DfgPasses::dfgToAst(*dfg, ctx); + // Attempt to convert cyclic components into acyclic ones + std::vector> madeAcyclicComponents; + if (v3Global.opt.fDfgBreakCyckes()) { + for (auto it = cyclicComponents.begin(); it != cyclicComponents.end();) { + auto result = V3DfgPasses::breakCycles(**it, m_ctx); + if (!result.first) { + // No improvement, moving on. + ++it; + } else if (!result.second) { + // Improved, but still cyclic. Replace the original cyclic component. + *it = std::move(result.first); + ++it; + } else { + // Result became acyclic. Move to madeAcyclicComponents, delete original. + madeAcyclicComponents.emplace_back(std::move(result.first)); + it = cyclicComponents.erase(it); + } + } } - } else { - // Post V3Scope application. Run on whole netlist. - UINFO(4, "Applying DFG optimization to entire netlist"); + // Merge those that were made acyclic back to the graph, this enables optimizing more + dfg.mergeGraphs(std::move(madeAcyclicComponents)); - // Build the DFG of the whole design - const std::unique_ptr dfg{V3DfgPasses::astToDfg(*netlistp, ctx)}; - if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input"); + // Split the acyclic DFG into [weakly] connected components + std::vector> acyclicComponents + = dfg.splitIntoComponents("acyclic"); - // Actually process the graph - process(*dfg, ctx); + // Quick sanity check + UASSERT(dfg.size() == 0, "DfgGraph should have become empty"); - // Convert back to Ast - if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized"); - V3DfgPasses::dfgToAst(*dfg, ctx); + // For each acyclic component + for (const std::unique_ptr& component : acyclicComponents) { + // Optimize the component + V3DfgPasses::optimize(*component, m_ctx); + } + // Merge back under the main DFG (we will convert everything back in one go) + dfg.mergeGraphs(std::move(acyclicComponents)); + + // Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse + // the module/netlist to perform variable substitutions. Doing this by component would do + // redundant traversals and can be extremely slow when we have many components. + V3DfgPasses::eliminateVars(dfg, m_ctx.m_eliminateVarsContext); + + // For each cyclic component + for (const std::unique_ptr& component : cyclicComponents) { + // Converting back to Ast assumes the 'regularize' pass was run, so we must run it + V3DfgPasses::regularize(*component, m_ctx.m_regularizeContext); + } + // Merge back under the main DFG (we will convert everything back in one go) + dfg.mergeGraphs(std::move(cyclicComponents)); + + if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "whole-optimized"); } + DataflowOptimize(AstNetlist* netlistp, const string& label) + : m_ctx{label} { + if (!netlistp->topScopep()) { + // Pre V3Scope application. Run on each module separately. + + // Mark cross-referenced variables + netlistp->foreach([](const AstVarXRef* xrefp) { + AstVar* const tgtp = xrefp->varp(); + if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasRdXRefs(tgtp); + if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasWrXRefs(tgtp); + }); + + // Run the optimization + for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) { + // Only optimize proper modules + AstModule* const modp = VN_CAST(nodep, Module); + if (!modp) continue; + + // Pre V3Scope application. Run on module. + UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'"); + ++m_ctx.m_modules; + // Build the DFG of this module or netlist + const std::unique_ptr dfgp{V3DfgPasses::astToDfg(*modp, m_ctx)}; + // Actually process the graph + optimize(*dfgp); + // Convert back to Ast + V3DfgPasses::dfgToAst(*dfgp, m_ctx); + } + } else { + // Post V3Scope application. Run on whole netlist. + UINFO(4, "Applying DFG optimization to entire netlist"); + // Build the DFG of the entire netlist + const std::unique_ptr dfgp{V3DfgPasses::astToDfg(*netlistp, m_ctx)}; + // Actually process the graph + optimize(*dfgp); + // Convert back to Ast + V3DfgPasses::dfgToAst(*dfgp, m_ctx); + } + } + +public: + static void apply(AstNetlist* netlistp, const string& label) { + DataflowOptimize{netlistp, label}; + } +}; + +void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { + UINFO(2, __FUNCTION__ << ":"); + DataflowOptimize::apply(netlistp, label); V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 664ba9420..0e6e6c9ac 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -421,6 +421,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // AstVarScope::user2p() : AstVarScope* -> The replacement variables const VNUser2InUse user2InUse; + // Whether we need to apply variable replacements + bool doReplace = false; + // Process the work list while (workListp != sentinelp) { // Pick up the head of the work list @@ -462,7 +465,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // If it is only referenced in this DFG, it can be removed ++ctx.m_varsRemoved; varp->replaceWith(varp->srcp()); - varp->nodep()->unlinkFrBack()->deleteTree(); + ctx.m_deleteps.push_back(varp->nodep()); // Delete variable at the end } else if (DfgVarPacked* const driverp = varp->srcp()->cast()) { // If it's driven from another variable, it can be replaced by that. // Mark it for replacement @@ -471,7 +474,8 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // Grab the AstVar/AstVarScope AstNode* const nodep = varp->nodep(); UASSERT_OBJ(!nodep->user2p(), nodep, "Replacement already exists"); - replacedVariables.emplace_back(nodep); + doReplace = true; + ctx.m_deleteps.push_back(nodep); // Delete variable at the end nodep->user2p(driverp->nodep()); } else { // Otherwise this *is* the canonical var @@ -485,7 +489,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { } // Job done if no replacements possible - if (replacedVariables.empty()) return; + if (!doReplace) return; // Apply variable replacements if (AstModule* const modp = dfg.modulep()) { @@ -502,9 +506,6 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { refp->varp(vscp->varp()); }); } - - // Remove the replaced variables - for (AstNode* const nodep : replacedVariables) nodep->unlinkFrBack()->deleteTree(); } void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) { From 2c41a7bbf6ba673398f9c7938e440a94dc2e2e6e Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 08:02:18 +0100 Subject: [PATCH 132/252] Adjust make test-snap / make test-diff rules --- test_regress/Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test_regress/Makefile b/test_regress/Makefile index 6204221c0..f0839a65b 100644 --- a/test_regress/Makefile +++ b/test_regress/Makefile @@ -106,7 +106,9 @@ TEST_SNAP_IGNORE := \ *.status *.log *.dat *.d *.o *.a *.so *stats*.txt *.html \ *.includecache *.out *.fst *.fst.vcd *.tree *.tree*.json \ *.dot *.csv *.xml *.hash *.cmake gmon.out.* CMakeFiles \ - profile_exec.vcd t_pgo_threads + profile_exec.vcd t_pgo_threads *line-coverage*.txt \ + profile.vlt *linkdot.txt *linkcells.txt *.log.sort *.vpp \ + *.sarif define TEST_SNAP_template mkdir -p $(TEST_SNAP_DIR) @@ -124,9 +126,9 @@ test-snap: .PHONY: impl-test-diff impl-test-diff: - $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_vlt obj_vlt - $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_vltmt obj_vltmt - $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_dist obj_dist + $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_vlt obj_vlt || true + $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_vltmt obj_vltmt || true + $(TEST_DIFF_TOOL) $(TEST_SNAP_DIR)/obj_dist obj_dist || true .PHONY: test-diff test-diff: From 25d968b8336d405ea3f74aa809515464cb97154c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 14:09:20 +0100 Subject: [PATCH 133/252] Fix infinite loop in VString::replaceSubstr If the replaced string was a suffix of the replacement, we used to get an infinite loop. --- src/V3String.cpp | 13 +++++++++---- src/V3String.h | 2 ++ src/Verilator.cpp | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/V3String.cpp b/src/V3String.cpp index 6a0e54215..4ce8482e7 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -310,10 +310,11 @@ double VString::parseDouble(const string& str, bool* successp) { 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); + const size_t fromLen = from.size(); + const size_t toLen = to.size(); + UASSERT_STATIC(fromLen > 0, "Cannot replace empty string"); + for (size_t pos = 0; (pos = result.find(from, pos)) != string::npos; pos += toLen) { + result.replace(pos, fromLen, to); } return result; } @@ -396,6 +397,10 @@ uint64_t VString::hashMurmur(const string& str) VL_PURE { return h; } +void VString::selfTest() { + UASSERT_SELFTEST(const string&, VString::replaceSubstr("aa", "a", "ba"), "baba"); +} + //###################################################################### // VHashSha256 diff --git a/src/V3String.h b/src/V3String.h index 537dea287..a8d55ea01 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -143,6 +143,8 @@ public: static string aOrAn(const string& word) { return aOrAn(word.c_str()); } // Hash the string static uint64_t hashMurmur(const string& str) VL_PURE; + + static void selfTest(); // Test this class }; //###################################################################### diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 143b7956c..fbd1648e2 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -692,6 +692,7 @@ static bool verilate(const string& argString) { if (v3Global.opt.debugSelfTest()) { V3Os::selfTest(); V3Number::selfTest(); + VString::selfTest(); VHashSha256::selfTest(); VSpellCheck::selfTest(); V3Graph::selfTest(); From 86c56e8e144ea791e18f3496592b5a9d8e57bc2c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 5 Aug 2025 14:47:51 +0100 Subject: [PATCH 134/252] Fix true cycle detection in DFG --- src/V3DfgBreakCycles.cpp | 2 +- test_regress/t/t_dfg_true_cycle_bad.v | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 533d76030..1cdb26033 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -54,7 +54,7 @@ class TraceDriver final : public DfgVisitor { struct Equal final { bool operator()(const Visited& a, const Visited& b) const { - return a.m_vtxp == b.m_vtxp && a.m_lsb == b.m_lsb && a.m_msb == b.m_lsb; + return a.m_vtxp == b.m_vtxp && a.m_lsb == b.m_lsb && a.m_msb == b.m_msb; } }; }; diff --git a/test_regress/t/t_dfg_true_cycle_bad.v b/test_regress/t/t_dfg_true_cycle_bad.v index b9f7e8b56..26791e7d0 100644 --- a/test_regress/t/t_dfg_true_cycle_bad.v +++ b/test_regress/t/t_dfg_true_cycle_bad.v @@ -10,7 +10,7 @@ module t( output wire [9:0] o ); assign o[1:0] = o[9:8]; - assign o[3:2] = {o[0], o[1]}; + assign o[3:2] = o[1:0]; assign o[7:4] = 4'(o[3:2]); assign o[9:8] = o[5:4]; endmodule From 870c398094de256937cdfea7125e9028d37a7ef7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 16:33:11 -0400 Subject: [PATCH 135/252] Fix incorrect Non-ANSI I/O declaration conflict error (#6258) broke with #bd1ac038 --- src/V3Ast.h | 3 +- src/V3LinkDot.cpp | 37 ++++++++-------- src/V3Width.cpp | 17 +++++++ test_regress/t/t_func_nansi_bad.out | 44 ------------------- test_regress/t/t_func_nansi_dup_bad.out | 14 ++++++ ...c_nansi_bad.py => t_func_nansi_dup_bad.py} | 0 test_regress/t/t_func_nansi_dup_bad.v | 23 ++++++++++ test_regress/t/t_func_nansi_mism_bad.out | 20 +++++++++ ..._nansi_bad.py => t_func_nansi_mism_bad.py} | 0 ...nc_nansi_bad.v => t_func_nansi_mism_bad.v} | 12 ----- test_regress/t/t_inst_nansi_bad.out | 32 -------------- test_regress/t/t_inst_nansi_dup_bad.out | 14 ++++++ test_regress/t/t_inst_nansi_dup_bad.py | 16 +++++++ test_regress/t/t_inst_nansi_dup_bad.v | 22 ++++++++++ test_regress/t/t_inst_nansi_mism_bad.out | 26 +++++++++++ test_regress/t/t_inst_nansi_mism_bad.py | 16 +++++++ ...st_nansi_bad.v => t_inst_nansi_mism_bad.v} | 13 ++---- test_regress/t/t_inst_nansi_param.py | 16 +++++++ test_regress/t/t_inst_nansi_param.v | 16 +++++++ 19 files changed, 224 insertions(+), 117 deletions(-) delete mode 100644 test_regress/t/t_func_nansi_bad.out create mode 100644 test_regress/t/t_func_nansi_dup_bad.out rename test_regress/t/{t_func_nansi_bad.py => t_func_nansi_dup_bad.py} (100%) create mode 100644 test_regress/t/t_func_nansi_dup_bad.v create mode 100644 test_regress/t/t_func_nansi_mism_bad.out rename test_regress/t/{t_inst_nansi_bad.py => t_func_nansi_mism_bad.py} (100%) rename test_regress/t/{t_func_nansi_bad.v => t_func_nansi_mism_bad.v} (73%) delete mode 100644 test_regress/t/t_inst_nansi_bad.out create mode 100644 test_regress/t/t_inst_nansi_dup_bad.out create mode 100755 test_regress/t/t_inst_nansi_dup_bad.py create mode 100644 test_regress/t/t_inst_nansi_dup_bad.v create mode 100644 test_regress/t/t_inst_nansi_mism_bad.out create mode 100755 test_regress/t/t_inst_nansi_mism_bad.py rename test_regress/t/{t_inst_nansi_bad.v => t_inst_nansi_mism_bad.v} (65%) create mode 100755 test_regress/t/t_inst_nansi_param.py create mode 100644 test_regress/t/t_inst_nansi_param.v diff --git a/src/V3Ast.h b/src/V3Ast.h index 13790a5cd..f0528a961 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -514,6 +514,7 @@ public: VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes VAR_CLOCK_ENABLE, // Ignored, accepted for compatibility VAR_FORCEABLE, // V3LinkParse moves to AstVar::isForceable + VAR_PORT_DTYPE, // V3LinkDot for V3Width to check port dtype VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic @@ -538,7 +539,7 @@ public: "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID", "TYPEID", "TYPENAME", - "VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_FORCEABLE", "VAR_PUBLIC", + "VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_FORCEABLE", "VAR_PORT_DTYPE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER", "VAR_NO_CLOCKER", "VAR_SPLIT_VAR" diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 7df735e67..2f85156be 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1512,33 +1512,32 @@ class LinkDotFindVisitor final : public VNVisitor { && !findvarp->subDTypep()->numeric().isSigned()) { findvarp->subDTypep()->numeric(VSigning{true}); } - AstBasicDType* const bdtypep - = VN_CAST(findvarp->childDTypep(), BasicDType); + AstNodeDType* varDtp = findvarp->subDTypep(); + AstNodeDType* otherDtp = nodep->subDTypep(); + AstBasicDType* const bdtypep = VN_CAST(varDtp, BasicDType); if (bdtypep && bdtypep->implicit()) { // Then have "input foo" and "real foo" so the // dtype comes from the other side. - AstNodeDType* const newdtypep = nodep->subDTypep(); - UASSERT_OBJ(newdtypep && nodep->childDTypep(), findvarp, - "No child type?"); + AstNodeDType* const newdtypep = otherDtp; + otherDtp = varDtp; + varDtp = newdtypep; VL_DO_DANGLING(bdtypep->unlinkFrBack()->deleteTree(), bdtypep); newdtypep->unlinkFrBack(); findvarp->childDTypep(newdtypep); } - if (nodep->childDTypep() && findvarp->childDTypep() - && !(VN_IS(nodep->childDTypep(), BasicDType) - && VN_AS(nodep->childDTypep(), BasicDType)->keyword() + if (otherDtp && varDtp + && !(VN_IS(otherDtp, BasicDType) + && VN_AS(otherDtp, BasicDType)->keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT) - && !(VN_IS(findvarp->childDTypep(), BasicDType) - && VN_AS(findvarp->childDTypep(), BasicDType)->keyword() - == VBasicDTypeKwd::LOGIC_IMPLICIT) - && !nodep->sameTree(findvarp)) { - nodep->v3error("Non-ANSI I/O declaration of signal " - "conflicts with type declaration: " - << nodep->prettyNameQ() << '\n' - << nodep->warnContextPrimary() << '\n' - << findvarp->warnOther() - << "... Location of other declaration\n" - << findvarp->warnContextSecondary()); + && !(VN_IS(varDtp, BasicDType) + && VN_AS(varDtp, BasicDType)->keyword() + == VBasicDTypeKwd::LOGIC_IMPLICIT)) { + // Can't compare dtypes now as might contain parameters, + // defer to V3Width + AstAttrOf* const newp + = new AstAttrOf{otherDtp->fileline(), VAttrType::VAR_PORT_DTYPE, + otherDtp->unlinkFrBack()}; + findvarp->addAttrsp(newp); } } VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 109875ae2..269006aaf 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2356,6 +2356,23 @@ class WidthVisitor final : public VNVisitor { // Make sure dtype is sized nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var"); + if (nodep->attrsp()) { + nodep->attrsp()->foreach([this, nodep](AstAttrOf* attrp) { + if (attrp->attrType() == VAttrType::VAR_PORT_DTYPE) { + V3Const::constifyParamsEdit(attrp->fromp()); // fromp may change + if (!similarDTypeRecurse(nodep->dtypep(), VN_AS(attrp->fromp(), NodeDType))) { + nodep->dtypep()->v3error("Non-ANSI I/O declaration of signal " + "conflicts with type declaration: " + << nodep->prettyNameQ() << '\n' + << nodep->dtypep()->warnContextPrimary() << '\n' + << attrp->warnOther() + << "... Location of other declaration\n" + << attrp->warnContextSecondary()); + } + VL_DO_DANGLING(pushDeletep(attrp->unlinkFrBack()), attrp); + } + }); + } if (m_ftaskp && m_ftaskp->dpiImport()) { AstNodeDType* dtp = nodep->dtypep(); AstNodeDType* np = nullptr; diff --git a/test_regress/t/t_func_nansi_bad.out b/test_regress/t/t_func_nansi_bad.out deleted file mode 100644 index 6ba6f4330..000000000 --- a/test_regress/t/t_func_nansi_bad.out +++ /dev/null @@ -1,44 +0,0 @@ -%Error: t/t_func_nansi_bad.v:13:14: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' - 13 | shortint bad1; - | ^~~~ - t/t_func_nansi_bad.v:12:18: ... Location of other declaration - 12 | input [15:0] bad1; - | ^~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_func_nansi_bad.v:18:7: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' - 18 | T bad2; - | ^~~~ - t/t_func_nansi_bad.v:17:18: ... Location of other declaration - 17 | input [31:0] bad2; - | ^~~~ -%Error: t/t_func_nansi_bad.v:23:15: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' - 23 | reg [3:0] bad3; - | ^~~~ - t/t_func_nansi_bad.v:22:17: ... Location of other declaration - 22 | input [7:0] bad3; - | ^~~~ -%Error: t/t_func_nansi_bad.v:28:9: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad4' - 28 | reg bad4; - | ^~~~ - t/t_func_nansi_bad.v:27:17: ... Location of other declaration - 27 | input [7:0] bad4; - | ^~~~ -%Error: t/t_func_nansi_bad.v:29:9: Duplicate declaration of signal: 'bad4' - 29 | reg bad4; - | ^~~~ - t/t_func_nansi_bad.v:27:17: ... Location of original declaration - 27 | input [7:0] bad4; - | ^~~~ -%Error: t/t_func_nansi_bad.v:34:17: Duplicate declaration of signal: 'bad5' - 34 | input [7:0] bad5; - | ^~~~ - t/t_func_nansi_bad.v:33:17: ... Location of original declaration - 33 | input [7:0] bad5; - | ^~~~ -%Error: t/t_func_nansi_bad.v:35:9: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad5' - 35 | reg bad5; - | ^~~~ - t/t_func_nansi_bad.v:33:17: ... Location of other declaration - 33 | input [7:0] bad5; - | ^~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_func_nansi_dup_bad.out b/test_regress/t/t_func_nansi_dup_bad.out new file mode 100644 index 000000000..cf407f0aa --- /dev/null +++ b/test_regress/t/t_func_nansi_dup_bad.out @@ -0,0 +1,14 @@ +%Error: t/t_func_nansi_dup_bad.v:14:9: Duplicate declaration of signal: 'bad4' + 14 | reg bad4; + | ^~~~ + t/t_func_nansi_dup_bad.v:12:17: ... Location of original declaration + 12 | input [7:0] bad4; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_func_nansi_dup_bad.v:19:17: Duplicate declaration of signal: 'bad5' + 19 | input [7:0] bad5; + | ^~~~ + t/t_func_nansi_dup_bad.v:18:17: ... Location of original declaration + 18 | input [7:0] bad5; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_func_nansi_bad.py b/test_regress/t/t_func_nansi_dup_bad.py similarity index 100% rename from test_regress/t/t_func_nansi_bad.py rename to test_regress/t/t_func_nansi_dup_bad.py diff --git a/test_regress/t/t_func_nansi_dup_bad.v b/test_regress/t/t_func_nansi_dup_bad.v new file mode 100644 index 000000000..06efe7e21 --- /dev/null +++ b/test_regress/t/t_func_nansi_dup_bad.v @@ -0,0 +1,23 @@ +// 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 + +typedef int T; + +module test; + + task t4; + input [7:0] bad4; + reg bad4; + reg bad4; // <--- Error (duplicate) + endtask + + task t5; + input [7:0] bad5; + input [7:0] bad5; // <--- Error (duplicate) + reg bad5; + endtask + +endmodule diff --git a/test_regress/t/t_func_nansi_mism_bad.out b/test_regress/t/t_func_nansi_mism_bad.out new file mode 100644 index 000000000..2ccad4f9a --- /dev/null +++ b/test_regress/t/t_func_nansi_mism_bad.out @@ -0,0 +1,20 @@ +%Error: t/t_func_nansi_mism_bad.v:12:11: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' + 12 | input [15:0] bad1; + | ^ + t/t_func_nansi_mism_bad.v:13:5: ... Location of other declaration + 13 | shortint bad1; + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_func_nansi_mism_bad.v:17:11: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' + 17 | input [31:0] bad2; + | ^ + t/t_func_nansi_mism_bad.v:18:5: ... Location of other declaration + 18 | T bad2; + | ^ +%Error: t/t_func_nansi_mism_bad.v:22:11: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' + 22 | input [7:0] bad3; + | ^ + t/t_func_nansi_mism_bad.v:23:5: ... Location of other declaration + 23 | reg [3:0] bad3; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_inst_nansi_bad.py b/test_regress/t/t_func_nansi_mism_bad.py similarity index 100% rename from test_regress/t/t_inst_nansi_bad.py rename to test_regress/t/t_func_nansi_mism_bad.py diff --git a/test_regress/t/t_func_nansi_bad.v b/test_regress/t/t_func_nansi_mism_bad.v similarity index 73% rename from test_regress/t/t_func_nansi_bad.v rename to test_regress/t/t_func_nansi_mism_bad.v index c69df2b8c..b5d0038a3 100644 --- a/test_regress/t/t_func_nansi_bad.v +++ b/test_regress/t/t_func_nansi_mism_bad.v @@ -23,16 +23,4 @@ module test; reg [3:0] bad3; // <--- Error (type doesn't match above) endtask - task t4; - input [7:0] bad4; - reg bad4; - reg bad4; // <--- Error (duplicate) - endtask - - task t5; - input [7:0] bad5; - input [7:0] bad5; // <--- Error (duplicate) - reg bad5; - endtask - endmodule diff --git a/test_regress/t/t_inst_nansi_bad.out b/test_regress/t/t_inst_nansi_bad.out deleted file mode 100644 index 3576c08e5..000000000 --- a/test_regress/t/t_inst_nansi_bad.out +++ /dev/null @@ -1,32 +0,0 @@ -%Error: t/t_inst_nansi_bad.v:14:12: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' - 14 | shortint bad1; - | ^~~~ - t/t_inst_nansi_bad.v:13:17: ... Location of other declaration - 13 | output [15:0] bad1; - | ^~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_inst_nansi_bad.v:17:5: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' - 17 | T bad2; - | ^~~~ - t/t_inst_nansi_bad.v:16:17: ... Location of other declaration - 16 | output [31:0] bad2; - | ^~~~ -%Error: t/t_inst_nansi_bad.v:20:13: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' - 20 | reg [7:0] bad3; - | ^~~~ - t/t_inst_nansi_bad.v:19:16: ... Location of other declaration - 19 | output [3:0] bad3; - | ^~~~ -%Error: t/t_inst_nansi_bad.v:24:7: Duplicate declaration of signal: 'bad4' - 24 | reg bad4; - | ^~~~ - t/t_inst_nansi_bad.v:22:10: ... Location of original declaration - 22 | output bad4; - | ^~~~ -%Error: t/t_inst_nansi_bad.v:27:10: Duplicate declaration of signal: 'bad5' - 27 | output bad5; - | ^~~~ - t/t_inst_nansi_bad.v:26:10: ... Location of original declaration - 26 | output bad5; - | ^~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_inst_nansi_dup_bad.out b/test_regress/t/t_inst_nansi_dup_bad.out new file mode 100644 index 000000000..97999019b --- /dev/null +++ b/test_regress/t/t_inst_nansi_dup_bad.out @@ -0,0 +1,14 @@ +%Error: t/t_inst_nansi_dup_bad.v:16:7: Duplicate declaration of signal: 'bad4' + 16 | reg bad4; + | ^~~~ + t/t_inst_nansi_dup_bad.v:14:10: ... Location of original declaration + 14 | output bad4; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_inst_nansi_dup_bad.v:19:10: Duplicate declaration of signal: 'bad5' + 19 | output bad5; + | ^~~~ + t/t_inst_nansi_dup_bad.v:18:10: ... Location of original declaration + 18 | output bad5; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_inst_nansi_dup_bad.py b/test_regress/t/t_inst_nansi_dup_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_inst_nansi_dup_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_inst_nansi_dup_bad.v b/test_regress/t/t_inst_nansi_dup_bad.v new file mode 100644 index 000000000..386b23154 --- /dev/null +++ b/test_regress/t/t_inst_nansi_dup_bad.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 + +typedef int T; + +module test ( /*AUTOARG*/ + // Outputs + bad4, bad5 + ); + + output bad4; + reg bad4; + reg bad4; // <--- Error (duplicate) + + output bad5; + output bad5; // <--- Error (duplicate) + reg bad5; // <--- Error (duplicate) + +endmodule diff --git a/test_regress/t/t_inst_nansi_mism_bad.out b/test_regress/t/t_inst_nansi_mism_bad.out new file mode 100644 index 000000000..690ee8feb --- /dev/null +++ b/test_regress/t/t_inst_nansi_mism_bad.out @@ -0,0 +1,26 @@ +%Error: t/t_inst_nansi_mism_bad.v:13:10: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad1' + 13 | output [15:0] bad1; + | ^ + t/t_inst_nansi_mism_bad.v:14:3: ... Location of other declaration + 14 | shortint bad1; + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_inst_nansi_mism_bad.v:16:10: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad2' + 16 | output [31:0] bad2; + | ^ + t/t_inst_nansi_mism_bad.v:17:3: ... Location of other declaration + 17 | T bad2; + | ^ +%Error: t/t_inst_nansi_mism_bad.v:19:10: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad3' + 19 | output [3:0] bad3; + | ^ + t/t_inst_nansi_mism_bad.v:20:3: ... Location of other declaration + 20 | reg [7:0] bad3; + | ^~~ +%Error: t/t_inst_nansi_mism_bad.v:22:3: Non-ANSI I/O declaration of signal conflicts with type declaration: 'bad4' + 22 | reg [7:0] bad4; + | ^~~ + t/t_inst_nansi_mism_bad.v:23:10: ... Location of other declaration + 23 | output [3:0] bad4; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_inst_nansi_mism_bad.py b/test_regress/t/t_inst_nansi_mism_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_inst_nansi_mism_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_inst_nansi_bad.v b/test_regress/t/t_inst_nansi_mism_bad.v similarity index 65% rename from test_regress/t/t_inst_nansi_bad.v rename to test_regress/t/t_inst_nansi_mism_bad.v index b87a602a1..d5fbb4f8f 100644 --- a/test_regress/t/t_inst_nansi_bad.v +++ b/test_regress/t/t_inst_nansi_mism_bad.v @@ -8,7 +8,7 @@ typedef int T; module test ( /*AUTOARG*/ // Outputs - bad1, bad2, bad3, bad4, bad5 + bad1, bad2, bad3, bad4 ); output [15:0] bad1; shortint bad1; // <--- Error (type doesn't match above) @@ -17,14 +17,9 @@ module test ( /*AUTOARG*/ T bad2; // <--- Error (type doesn't match above due to range) output [3:0] bad3; - reg [7:0] bad3; // <--- Error (range doesn't match) + reg [7:0] bad3; // <--- Error (range doesn't match) (output-before-reg) - output bad4; - reg bad4; - reg bad4; // <--- Error (duplicate) - - output bad5; - output bad5; // <--- Error (duplicate) - reg bad5; // <--- Error (duplicate) + reg [7:0] bad4; // <--- Error (range doesn't match) (reg-before-output) + output [3:0] bad4; endmodule diff --git a/test_regress/t/t_inst_nansi_param.py b/test_regress/t/t_inst_nansi_param.py new file mode 100755 index 000000000..f2f5d0201 --- /dev/null +++ b/test_regress/t/t_inst_nansi_param.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('linter') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_inst_nansi_param.v b/test_regress/t/t_inst_nansi_param.v new file mode 100644 index 000000000..7d4c1baa0 --- /dev/null +++ b/test_regress/t/t_inst_nansi_param.v @@ -0,0 +1,16 @@ +// 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 sub(i); + parameter N = 3; + input [N : 0] i; // Note 3:0 conflicts until parameterize + wire [2:0] i; +endmodule + +module t; + wire [2:0] i; + sub #(.N(2)) sub(.i); +endmodule From 4bd17f0a6f255126c0b817c32a038f8c4a9d5a21 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 17:08:09 -0400 Subject: [PATCH 136/252] Commentary: Changes update --- Changes | 6 ++++++ docs/spelling.txt | 1 + 2 files changed, 7 insertions(+) diff --git a/Changes b/Changes index 75d3f2c04..e7e2da74c 100644 --- a/Changes +++ b/Changes @@ -22,6 +22,7 @@ Verilator 5.039 devel * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] +* Support unassigned virtual interfaces (#5265) (#6245). [Szymon Gizler, Antmicro Ltd.] * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support bit queue streaming (#5830) (#6103). [Paul Swirhun] @@ -37,6 +38,8 @@ Verilator 5.039 devel * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] * Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] * Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] +* Optimize variable removal in scoped DFG (#6260). [Geza Lore] +* Optimize acyclic DFG components into the original acyclic sub-graph. (#6261). [Geza Lore] * Fix loop initialization visibility outside loop (#4237). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). @@ -61,6 +64,9 @@ Verilator 5.039 devel * Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] * Fix queue typedef with unbounded slice (#6236). * Fix error when force assignment is used with ref function args (#6244). [Ryszard Rozak, Antmicro Ltd.] +* Fix write of 0 in '%c' (#6248) (#6249). [Rodrigo Batista de Moraes] +* Fix coverage of variables of complex types (#6250). [Ryszard Rozak, Antmicro Ltd.] +* Fix partial DFG conversion of concat assignments (#6255). [Geza Lore] Verilator 5.038 2025-07-08 diff --git a/docs/spelling.txt b/docs/spelling.txt index c04bc2350..806748456 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -513,6 +513,7 @@ Zhang abirkmanis accessor accessors +acyclic adrienlemasle agrobman ahouska From 6467351752f1a2e4286b07d1fcd650262485f709 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 17:12:00 -0400 Subject: [PATCH 137/252] Add error on class 'function static'. --- Changes | 1 + src/V3AstNodeOther.h | 4 ++++ src/V3LinkParse.cpp | 10 ++++++++-- test_regress/t/t_class_func_static_bad.out | 10 ++++++++++ test_regress/t/t_class_func_static_bad.py | 16 +++++++++++++++ test_regress/t/t_class_func_static_bad.v | 23 ++++++++++++++++++++++ test_regress/t/t_class_static_member_sel.v | 7 +++---- test_regress/t/t_var_static.v | 6 ------ 8 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 test_regress/t/t_class_func_static_bad.out create mode 100755 test_regress/t/t_class_func_static_bad.py create mode 100644 test_regress/t/t_class_func_static_bad.v diff --git a/Changes b/Changes index e7e2da74c..29b972f3b 100644 --- a/Changes +++ b/Changes @@ -20,6 +20,7 @@ Verilator 5.039 devel * Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. * Add error on missing forward declarations (#6206). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] +* Add error on class 'function static'. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support unassigned virtual interfaces (#5265) (#6245). [Szymon Gizler, Antmicro Ltd.] diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index f18b9eb9b..8f3ebc539 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2480,6 +2480,7 @@ public: AstNodeFTask* cloneType(const string& name) override { return new AstFunc{fileline(), name, nullptr, nullptr}; } + string verilogKwd() const override { return "function"; } }; class AstLet final : public AstNodeFTask { // Verilog "let" statement @@ -2495,6 +2496,7 @@ public: return nullptr; } AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; } + string verilogKwd() const override { return "let"; } }; class AstProperty final : public AstNodeFTask { // A property inside a module @@ -2506,6 +2508,7 @@ public: AstNodeFTask* cloneType(const string& name) override { return new AstProperty{fileline(), name, nullptr}; } + string verilogKwd() const override { return "property"; } }; class AstTask final : public AstNodeFTask { // A task inside a module @@ -2516,6 +2519,7 @@ public: AstNodeFTask* cloneType(const string& name) override { return new AstTask{fileline(), name, nullptr}; } + string verilogKwd() const override { return "task"; } }; // === AstNodeFile === diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 98ee38a5c..5db065de1 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -198,10 +198,11 @@ class LinkParseVisitor final : public VNVisitor { // DPI-imported functions and properties don't have lifetime specifiers m_lifetime = VLifetime::NONE; } + nodep->lifetime(m_lifetime); for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { AstVar* const varp = VN_CAST(itemp, Var); if (varp && varp->valuep() && varp->lifetime().isNone() - && m_lifetime.isStatic() && !varp->isIO()) { + && nodep->lifetime().isStatic() && !varp->isIO()) { if (VN_IS(m_modp, Module)) { nodep->v3warn(IMPLICITSTATIC, "Function/task's lifetime implicitly set to static\n" @@ -223,7 +224,12 @@ class LinkParseVisitor final : public VNVisitor { } } } - nodep->lifetime(m_lifetime); + } + if (nodep->classMethod() && nodep->lifetime().isStatic()) { + nodep->v3error("Class function/task cannot be static lifetime ('" + << nodep->verilogKwd() << " static') (IEEE 1800-2023 6.21)\n" + << nodep->warnMore() << "... May have intended 'static " + << nodep->verilogKwd() << "'"); } iterateChildren(nodep); } diff --git a/test_regress/t/t_class_func_static_bad.out b/test_regress/t/t_class_func_static_bad.out new file mode 100644 index 000000000..ffad20e2e --- /dev/null +++ b/test_regress/t/t_class_func_static_bad.out @@ -0,0 +1,10 @@ +%Error: t/t_class_func_static_bad.v:10:17: Class function/task cannot be static lifetime ('task static') (IEEE 1800-2023 6.21) + : ... May have intended 'static task' + 10 | task static task_st(int x); + | ^~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_class_func_static_bad.v:14:25: Class function/task cannot be static lifetime ('function static') (IEEE 1800-2023 6.21) + : ... May have intended 'static function' + 14 | function static int func_st(int x); + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_func_static_bad.py b/test_regress/t/t_class_func_static_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_class_func_static_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_func_static_bad.v b/test_regress/t/t_class_func_static_bad.v new file mode 100644 index 000000000..b5c675fab --- /dev/null +++ b/test_regress/t/t_class_func_static_bad.v @@ -0,0 +1,23 @@ +// 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; + + class C; + task static task_st(int x); // BAD - methods have automatic lifetime + int y; + y = 2 * x; + endtask + function static int func_st(int x); // BAD - methods have automatic lifetime + int y; + y = 2 * x; + return y; + endfunction + endclass + + initial $stop; + +endmodule diff --git a/test_regress/t/t_class_static_member_sel.v b/test_regress/t/t_class_static_member_sel.v index 9e0b542c5..2e1594c8b 100644 --- a/test_regress/t/t_class_static_member_sel.v +++ b/test_regress/t/t_class_static_member_sel.v @@ -16,7 +16,7 @@ class Bar; endclass class Baz; - function static Bar get_bar; + static function Bar get_bar; Bar b = new; return b; endfunction @@ -37,7 +37,7 @@ class ExtendCls extends Cls; endclass class Getter1; - function static int get_1; + static function int get_1; return 1; endfunction endclass @@ -54,8 +54,7 @@ class uvm_root; endfunction endclass -module t (/*AUTOARG*/ - ); +module t (/*AUTOARG*/); initial begin Foo foo = new; diff --git a/test_regress/t/t_var_static.v b/test_regress/t/t_var_static.v index 18f56ebdf..dd79f5703 100644 --- a/test_regress/t/t_var_static.v +++ b/test_regress/t/t_var_static.v @@ -22,10 +22,6 @@ class my_cls; static int cnt = 0; return ++cnt; endfunction - static function static int get_cnt2; - int cnt = 0; - return ++cnt; - endfunction endclass module t (/*AUTOARG*/ @@ -103,8 +99,6 @@ module t (/*AUTOARG*/ // v = my_cls::get_cnt1(); `checkh(v, 1); v = my_cls::get_cnt1(); `checkh(v, 2); - v = my_cls::get_cnt2(); `checkh(v, 1); - v = my_cls::get_cnt2(); `checkh(v, 2); // end From c005486acf0901115ae24bd5d88653e8a61b216a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 17:34:42 -0400 Subject: [PATCH 138/252] Support by ignoring delay2 on UDPs --- src/V3LinkDot.cpp | 6 +++--- test_regress/t/t_udp_delay.py | 16 ++++++++++++++++ test_regress/t/t_udp_delay.v | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_udp_delay.py create mode 100644 test_regress/t/t_udp_delay.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 2f85156be..641e57355 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2889,9 +2889,9 @@ class LinkDotResolveVisitor final : public VNVisitor { VSymEnt* const foundp = m_pinSymp->findIdFlat(nodep->name()); const char* const whatp = nodep->param() ? "parameter" : "pin"; if (!foundp) { - if (nodep->name() == "__paramNumber1" && m_cellp - && VN_IS(m_cellp->modp(), Primitive)) { - // Primitive parameter is really a delay we can just ignore + if (m_cellp && VN_IS(m_cellp->modp(), Primitive) + && (nodep->name() == "__paramNumber1" || nodep->name() == "__paramNumber2")) { + // Primitive parameter is really a delay2 we can just ignore VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); return; } else { diff --git a/test_regress/t/t_udp_delay.py b/test_regress/t/t_udp_delay.py new file mode 100755 index 000000000..055e14291 --- /dev/null +++ b/test_regress/t/t_udp_delay.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('linter') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_udp_delay.v b/test_regress/t/t_udp_delay.v new file mode 100644 index 000000000..238a514ab --- /dev/null +++ b/test_regress/t/t_udp_delay.v @@ -0,0 +1,21 @@ +// 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 + +primitive not_u(out, in); + output out; + input in; + table + 0 : 1; + 1 : 0; + endtable +endprimitive + +module t (out, in); + input in; + output wire out; + real v = 0.34; + not_u #(1.145, v) dut_u (out, in); +endmodule From 06c570e15997ff184dab2787823aae9dd05060b4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 17:52:34 -0400 Subject: [PATCH 139/252] Commentary --- docs/internals.rst | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/internals.rst b/docs/internals.rst index 816d457f7..a572a67f6 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1143,7 +1143,7 @@ is appreciated if you could match our style: calls. (This convention has not been applied retroactively.) C and Python indentation is automatically maintained with "make format" -using clang-format version 10.0.0, and yapf for python, and is +using clang-format version 14.0.0, and yapf for Python, and is automatically corrected in the CI actions. For those manually formatting C code: @@ -1158,6 +1158,50 @@ code: parenthesis (only applies to functions; if/else has a following space). +Commit messages +--------------- + +Pull requests do not typically edit ``Changes`` in order to reduce +potential merge conflicts. Instead, contibutors should use an appropriate +first line of the git commit message. Maintainers periodically run +``nodist/log_changes`` which analyzes the commit messages to suggest edits +to the ``Changes`` file. + +The format of a commit message should be typically of the forms below. The +github issue number, and github pull number if different should be +included. + +"Add (#1234)" + For adding a new feature. +"Fix (#1234)" + For a bug fix. +"Improve (#1234)" + For improving something that previously existed. +"Optimize (#1234)" + For improving performance. +"Support (#1234)" + For features that are described by IEEE were previously not supported. +"Commentary" + For a super-trivial change to e.g. documentation. + These will typically not get described in the Changes file. +"CI: (#1234)" + For changes that only concern github actions. + These will typically not get described in the Changes file. +"Tests: (#1234)" + For changes that only concern tests. + These will typically not get described in the Changes file. +"Internals: (#1234)" + For changes that only concern developers, e.g. code cleanups. + These will typically not get described in the Changes file. + +The line's grammar should match the phrasing used in the Changes file. + +If the change does not affect user-visible function, also add "No +functional change." if it's 99% certain not to change behavior, or "No +functional change expected." if no change was expected but there may be +uncertanty. + + The ``astgen`` Script --------------------- From fbaff52668a3f1acb85bec7e98d564f8b1ef74bf Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 5 Aug 2025 18:43:29 -0400 Subject: [PATCH 140/252] Change runtime to exit() instead of abort(), unless under +verilated+debug. --- Changes | 1 + include/verilated.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 29b972f3b..c0c5f28ca 100644 --- a/Changes +++ b/Changes @@ -34,6 +34,7 @@ Verilator 5.039 devel * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). +* Change runtime to exit() instead of abort(), unless under +verilated+debug. * Improve `--skip-identical` to skip on identical input file contents (#6109). * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] diff --git a/include/verilated.cpp b/include/verilated.cpp index 33cf917d4..3fe16f361 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -168,7 +168,12 @@ void vl_fatal(const char* filename, int linenum, const char* hier, const char* m // Callbacks prior to termination Verilated::runExitCallbacks(); - std::abort(); + + if (Verilated::debug()) { + std::abort(); + } else { + std::exit(1); + } } #endif From 6c1cfc68cf4242f78cabbbbcbf10ef3d7bf4a913 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Wed, 6 Aug 2025 13:09:44 +0200 Subject: [PATCH 141/252] Fix dynamic cast purity (#6267) Signed-off-by: Igor Zaworski --- src/V3AstNodeExpr.h | 2 +- test_regress/t/t_class_dyn_cast_empty_if.py | 18 +++++++++++ test_regress/t/t_class_dyn_cast_empty_if.v | 33 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_class_dyn_cast_empty_if.py create mode 100644 test_regress/t/t_class_dyn_cast_empty_if.v diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index dbc9932f3..300aea817 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -2465,7 +2465,7 @@ public: bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } int instrCount() const override { return widthInstrs() * 20; } - bool isPure() override { return true; } + bool isPure() override { return false; } }; class AstCompareNN final : public AstNodeBiop { // Verilog str.compare() and str.icompare() diff --git a/test_regress/t/t_class_dyn_cast_empty_if.py b/test_regress/t/t_class_dyn_cast_empty_if.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_dyn_cast_empty_if.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_dyn_cast_empty_if.v b/test_regress/t/t_class_dyn_cast_empty_if.v new file mode 100644 index 000000000..f11e35bd4 --- /dev/null +++ b/test_regress/t/t_class_dyn_cast_empty_if.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +typedef class Derived; +class Base; + function Derived cast(); + if ($cast(cast,this)) begin end + endfunction +endclass + +class Derived extends Base; + string x; + function new(string xval); + x = xval; + endfunction + function string get(); + return x; + endfunction +endclass + +module t; + initial begin + Derived d = new("Hello"); + Base b = d; + Derived c = b.cast(); + if (d.get() != c.get()) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 218659f4e81768b7bd303295a967efb311b5f7fd Mon Sep 17 00:00:00 2001 From: Michael Bedford Taylor Date: Wed, 6 Aug 2025 14:29:40 -0700 Subject: [PATCH 142/252] Support parameter resolution of 1D unpacked array slices (#6257) (#6268) --- docs/CONTRIBUTORS | 1 + src/V3Const.cpp | 12 ++++ src/V3Param.cpp | 8 +++ src/V3Simulate.h | 34 ++++++++++++ test_regress/t/t_param_slice.py | 17 ++++++ test_regress/t/t_param_slice.v | 98 +++++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+) create mode 100644 test_regress/t/t_param_slice.py create mode 100644 test_regress/t/t_param_slice.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 9b75ecf02..e30c878d2 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -161,6 +161,7 @@ Martin Stadler Mateusz Gancarz Matthew Ballance Max Wipfli +Michael Bedford Taylor Michael Bikovitsky Michael Killough Michal Czyz diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 3c5e568c1..179f856b4 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2787,6 +2787,18 @@ class ConstVisitor final : public VNVisitor { } m_selp = nullptr; } + + // Evaluate a slice of an unpacked array. If constantification is + // required (m_required=true), call replaceWithSimulation() to compute + // the slice via simulation. Otherwise just iterate the children. + void visit(AstSliceSel* nodep) override { + // First constify or width any child nodes + iterateChildren(nodep); + if (!m_required) return; // Do nothing unless we are in parameter mode + // Fallback to simulation: this will invoke SimulateVisitor::visit(AstSliceSel*) + replaceWithSimulation(nodep); + } + void visit(AstCAwait* nodep) override { m_hasJumpDelay = true; iterateChildren(nodep); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 7bf2a98b3..a76fb208d 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -732,6 +732,14 @@ class ParamProcessor final { AstNode* const exprp = pinp->exprp(); longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); any_overridesr = true; + } else if (VN_IS(pinp->exprp(), InitArray)) { + // Array assigned to scalar parameter. Treat the InitArray as a constant + // integer array and include it in the module name. Constantify nested + // expressions before mangling the value number. + V3Const::constifyParamsEdit(pinp->exprp()); + longnamer += "_" + paramSmallName(srcModp, modvarp) + + paramValueNumber(pinp->exprp()); + any_overridesr = true; } else { V3Const::constifyParamsEdit(pinp->exprp()); // String constants are parsed as logic arrays and converted to strings in V3Const. diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 190b3580f..236dd442a 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -894,6 +894,40 @@ private: clearOptimizable(nodep, "Array select of non-array"); } } + + // Evaluate a slice of an unpacked array. If the base value is a constant + // AstInitArray, build a new AstInitArray representing the slice and assign + // it as this node's value. New index 0 corresponds to the lowest index of + // the slice. Otherwise, mark this node as unoptimizable. + void visit(AstSliceSel* nodep) override { + checkNodeInfo(nodep); + iterateChildrenConst(nodep); + if (m_checkOnly || !optimizable()) return; + // Fetch the base constant array + if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { + const VNumRange& sliceRange = nodep->declRange(); + const uint32_t sliceElements = sliceRange.elements(); + const int sliceLo = sliceRange.lo(); + // Use this node's dtype for the slice array + AstNodeDType* const dtypep = nodep->dtypep()->skipRefp(); + // Clone the default value from the base array, if present + AstNodeExpr* defaultp = nullptr; + if (initp->defaultp()) defaultp = initp->defaultp()->cloneTree(false); + AstInitArray* const newInitp = new AstInitArray{nodep->fileline(), dtypep, defaultp}; + // Copy slice elements in ascending order + for (uint32_t idx = 0; idx < sliceElements; ++idx) { + const uint32_t baseIdx = sliceLo + idx; + AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(baseIdx); + if (itemp) newInitp->addIndexValuep(idx, itemp->cloneTree(false)); + } + // Assign the new constant array and track it for later deletion + setValue(nodep, newInitp); + m_reclaimValuesp.push_back(newInitp); + } else { + clearOptimizable(nodep, "Slice select of non-array"); + } + } + void visit(AstBegin* nodep) override { checkNodeInfo(nodep); iterateChildrenConst(nodep); diff --git a/test_regress/t/t_param_slice.py b/test_regress/t/t_param_slice.py new file mode 100644 index 000000000..125f07bb2 --- /dev/null +++ b/test_regress/t/t_param_slice.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Test constant parameter slicing of unpacked arrays (issue #6257) +# +# 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.compile(verilator_flags2=['--exe','--main','--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_param_slice.v b/test_regress/t/t_param_slice.v new file mode 100644 index 000000000..96adbdf0b --- /dev/null +++ b/test_regress/t/t_param_slice.v @@ -0,0 +1,98 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps +// Test constant parameter slicing of unpacked arrays with various slice ranges. + +module issue_desc #( + parameter int els_p = 1, + parameter int val_p [els_p+1:2], + parameter int orig_els = 1 +) (); + // Drop the lowest index (2) in each recursion: slice [high:3] + if (els_p > 1) begin : r + issue_desc #( + .els_p(els_p-1), + .val_p(val_p[els_p+1:3]), + .orig_els(orig_els) + ) x(); + end + initial begin + int expected = orig_els - els_p + 1; + if (val_p[2] !== expected) begin + $error("DESC wrong value %0d expected %0d in %m", val_p[2], expected); + $finish; + end + $display("%08x (desc %m)", val_p[2]); + end +endmodule + +module issue_rev #( + parameter int els_p = 1, + parameter int val_p [2:els_p+1], + parameter int orig_els = 1 +) (); + // Drop the lowest index (2) in each recursion: slice [3:high] + if (els_p > 1) begin : r + issue_rev #( + .els_p(els_p-1), + .val_p(val_p[3:els_p+1]), + .orig_els(orig_els) + ) x(); + end + initial begin + int expected = orig_els - els_p + 1; + if (val_p[2] !== expected) begin + $error("REV wrong value %0d expected %0d in %m", val_p[2], expected); + $finish; + end + $display("%08x (rev %m)", val_p[2]); + end +endmodule + +module issue_def #( + parameter int els_p = 1, + // Internal default fill is zero; the test overrides this with DEADBEEF. + parameter int val_p [els_p+1:2] = '{default:0}, + parameter int orig_els = 1 +) (); + // Recursively slice off the lowest index (2) + if (els_p > 1) begin : r + issue_def #( + .els_p(els_p-1), + .val_p(val_p[els_p+1:3]), + .orig_els(orig_els) + ) x(); + end + initial begin + // Expect 32'hDEADBEEF when overridden by the top-level test. + if (val_p[2] !== 32'hDEADBEEF) begin + $error("DEF wrong value %0x expected DEADBEEF in %m", val_p[2]); + $finish; + end + $display("%08x (def %m)", val_p[2]); + end +endmodule + +module t; + // For els_p=5, the range [els_p+1:2] is [6:2]. + // Descending initializer: index 6=5,5=4,4=3,3=2,2=1. + parameter int val_desc [6:2] = '{5,4,3,2,1}; + // Reverse slice initializer: ascending values on [2:6]. + parameter int val_rev [2:6] = '{1,2,3,4,5}; + // Override for default-array test: all elements set to 32'hDEADBEEF on [6:2]. + parameter int val_def [6:2] = '{default: 32'hDEADBEEF}; + + issue_desc #(.els_p(5), .val_p(val_desc), .orig_els(5)) iss_desc(); + issue_rev #(.els_p(5), .val_p(val_rev), .orig_els(5)) iss_rev(); + issue_def #(.els_p(5), .val_p(val_def), .orig_els(5)) iss_def(); + + initial begin + #1; + $write("*-* All Finished *-*\\n"); + $finish; + end +endmodule From dc049fdd745d2d1fea619097848fb381718ba4e0 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 6 Aug 2025 21:30:37 +0000 Subject: [PATCH 143/252] Apply 'make format' --- src/V3Const.cpp | 10 +++++----- src/V3Param.cpp | 4 ++-- test_regress/t/t_param_slice.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) mode change 100644 => 100755 test_regress/t/t_param_slice.py diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 179f856b4..9b5ba8996 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2792,11 +2792,11 @@ class ConstVisitor final : public VNVisitor { // required (m_required=true), call replaceWithSimulation() to compute // the slice via simulation. Otherwise just iterate the children. void visit(AstSliceSel* nodep) override { - // First constify or width any child nodes - iterateChildren(nodep); - if (!m_required) return; // Do nothing unless we are in parameter mode - // Fallback to simulation: this will invoke SimulateVisitor::visit(AstSliceSel*) - replaceWithSimulation(nodep); + // First constify or width any child nodes + iterateChildren(nodep); + if (!m_required) return; // Do nothing unless we are in parameter mode + // Fallback to simulation: this will invoke SimulateVisitor::visit(AstSliceSel*) + replaceWithSimulation(nodep); } void visit(AstCAwait* nodep) override { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index a76fb208d..fa43d5ab4 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -737,8 +737,8 @@ class ParamProcessor final { // integer array and include it in the module name. Constantify nested // expressions before mangling the value number. V3Const::constifyParamsEdit(pinp->exprp()); - longnamer += "_" + paramSmallName(srcModp, modvarp) - + paramValueNumber(pinp->exprp()); + longnamer + += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(pinp->exprp()); any_overridesr = true; } else { V3Const::constifyParamsEdit(pinp->exprp()); diff --git a/test_regress/t/t_param_slice.py b/test_regress/t/t_param_slice.py old mode 100644 new mode 100755 index 125f07bb2..9a62155e4 --- a/test_regress/t/t_param_slice.py +++ b/test_regress/t/t_param_slice.py @@ -10,7 +10,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=['--exe','--main','--timing']) +test.compile(verilator_flags2=['--exe', '--main', '--timing']) test.execute() From 5b7188fcafcdfdf0743b126f1b8a4686704d9cc5 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Wed, 6 Aug 2025 23:37:00 +0200 Subject: [PATCH 144/252] Fix same variable on the RHS forced to two different LHSs. (#6269) --- src/V3Force.cpp | 38 ++++++++++++++------- test_regress/t/t_force_rhs_ref_multi_lhs.py | 18 ++++++++++ test_regress/t/t_force_rhs_ref_multi_lhs.v | 33 ++++++++++++++++++ 3 files changed, 76 insertions(+), 13 deletions(-) create mode 100755 test_regress/t/t_force_rhs_ref_multi_lhs.py create mode 100644 test_regress/t/t_force_rhs_ref_multi_lhs.v diff --git a/src/V3Force.cpp b/src/V3Force.cpp index a09e3288d..52c16e211 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -148,15 +148,16 @@ private: // AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar) // AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope) // AstVarRef::user2 -> Flag indicating not to replace reference - // AstVarScope::user3 -> AstVarScope*, a `valVscp` force component for each VarScope of - // forced RHS - // AstVarScope::user4p -> AstNodeExpr*, the RHS expression + // AstVarScope::user3p -> AstNodeExpr*, the RHS expression const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; - const VNUser4InUse m_user4InUse; AstUser1Allocator m_forceComponentsVar; AstUser1Allocator m_forceComponentsVarScope; + std::unordered_map, std::vector>> + m_valVscps; + // `valVscp` force components of a forced RHS public: // CONSTRUCTORS @@ -172,11 +173,21 @@ public: } static bool isNotReplaceable(const AstVarRef* const nodep) { return nodep->user2(); } static void markNonReplaceable(AstVarRef* const nodep) { nodep->user2SetOnce(); } - static AstVarScope* getValVscp(AstVarRef* const refp) { - return VN_CAST(refp->varScopep()->user3p(), VarScope); + + // Get all ValVscps for a VarScope + const std::vector* getValVscps(AstVarRef* const refp) const { + auto it = m_valVscps.find(refp->varScopep()); + if (it != m_valVscps.end()) return &(it->second.second); + return nullptr; } - static void setValVscp(AstNodeVarRef* const refp, AstVarScope* const vscp) { - refp->varScopep()->user3p(vscp); + + // Add a ValVscp for a VarScope + void addValVscp(AstVarRef* const refp, AstVarScope* const valVscp) { + if (m_valVscps[refp->varScopep()].first.find(valVscp) + != m_valVscps[refp->varScopep()].first.end()) + return; + m_valVscps[refp->varScopep()].first.emplace(valVscp); + m_valVscps[refp->varScopep()].second.push_back(valVscp); } // METHODS @@ -188,10 +199,10 @@ public: return m_forceComponentsVarScope.tryGet(nodep->varScopep()); } void setValVscpRhsExpr(AstVarScope* valVscp, AstNodeExpr* rhsExpr) { - valVscp->user4p(rhsExpr); + valVscp->user3p(rhsExpr); } AstNodeExpr* getValVscpRhsExpr(AstVarScope* valVscp) const { - return VN_CAST(valVscp->user4p(), NodeExpr); + return VN_CAST(valVscp->user3p(), NodeExpr); } }; @@ -242,7 +253,7 @@ class ForceConvertVisitor final : public VNVisitor { transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) { AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp; m_state.setValVscpRhsExpr(valVscp, rhsp->cloneTreePure(false)); - rhsp->foreach([valVscp](AstVarRef* refp) { ForceState::setValVscp(refp, valVscp); }); + rhsp->foreach([valVscp, this](AstVarRef* refp) { m_state.addValVscp(refp, valVscp); }); return valVscp; }); @@ -380,7 +391,8 @@ class ForceReplaceVisitor final : public VNVisitor { m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp}); } // Emit valVscp update after each write to any VarRef on forced RHS. - if (AstVarScope* const valVscp = ForceState::getValVscp(nodep)) { + if (!m_state.getValVscps(nodep)) break; + for (AstVarScope* const valVscp : *m_state.getValVscps(nodep)) { FileLine* const flp = nodep->fileline(); AstVarRef* const valp = new AstVarRef{flp, valVscp, VAccess::WRITE}; AstNodeExpr* rhsp = m_state.getValVscpRhsExpr(valVscp); @@ -396,7 +408,7 @@ class ForceReplaceVisitor final : public VNVisitor { } default: if (!m_inLogic) return; - if (m_state.tryGetForceComponents(nodep) || ForceState::getValVscp(nodep)) { + if (m_state.tryGetForceComponents(nodep) || m_state.getValVscps(nodep)) { nodep->v3error( "Unsupported: Signals used via read-write reference cannot be forced"); } diff --git a/test_regress/t/t_force_rhs_ref_multi_lhs.py b/test_regress/t/t_force_rhs_ref_multi_lhs.py new file mode 100755 index 000000000..4ff4dfaa5 --- /dev/null +++ b/test_regress/t/t_force_rhs_ref_multi_lhs.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('vlt') + +test.compile(verilator_flags2=["--exe", "--main", "--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_rhs_ref_multi_lhs.v b/test_regress/t/t_force_rhs_ref_multi_lhs.v new file mode 100644 index 000000000..10711caec --- /dev/null +++ b/test_regress/t/t_force_rhs_ref_multi_lhs.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 Antmicro. +// 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; + bit [1:0] a; + bit [1:0] b; + + bit [1:0] d; + + initial begin + a = 0; + force b = a; + force d = a; + a = 2; + #1; + `checkh(a, 2); + `checkh(b, 2); + `checkh(d, 2); + a = 3; + #1; + `checkh(a, 3); + `checkh(b, 3); + `checkh(d, 3); + #1 $finish; + end + +endmodule From 6bd6663dc9483d27a94a569c6872f82ef521b802 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Thu, 7 Aug 2025 11:37:33 -0400 Subject: [PATCH 145/252] Fix spurious VPI value change callbacks (#6274) --- include/verilated_vpi.cpp | 2 +- test_regress/t/t_vpi_var.cpp | 18 ++++++++++++++++++ test_regress/t/t_vpi_var.v | 2 ++ test_regress/t/t_vpi_var2.v | 1 + test_regress/t/t_vpi_var3.v | 1 + 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index a08dea624..a20954308 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -429,7 +429,7 @@ public: void createPrevDatap() { if (VL_UNLIKELY(!m_prevDatap)) { m_prevDatap = new uint8_t[entSize()]; - std::memcpy(prevDatap(), varp()->datap(), entSize()); + std::memcpy(prevDatap(), m_varDatap, entSize()); } } }; diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 3a04f6831..428e4298f 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -156,6 +156,12 @@ int _value_callback_quad(p_cb_data cb_data) { return 0; } +int _value_callback_never(p_cb_data cb_data) { + printf("%%Error: callback should never be called\n"); + exit(-1); + return 0; +} + int _mon_check_value_callbacks() { s_vpi_value v; v.format = vpiIntVal; @@ -210,6 +216,18 @@ int _mon_check_value_callbacks() { TestVpiHandle callback_h = vpi_register_cb(&cb_data); CHECK_RESULT_NZ(callback_h); } + { + TestVpiHandle vh1 = VPI_HANDLE("some_mem"); + CHECK_RESULT_NZ(vh1); + TestVpiHandle vh2 = vpi_handle_by_index(vh1, 3); + CHECK_RESULT_NZ(vh2); + + cb_data.obj = vh2; + cb_data.cb_rtn = _value_callback_never; + + TestVpiHandle callback_h = vpi_register_cb(&cb_data); + CHECK_RESULT_NZ(callback_h); + } return 0; } diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 240694b86..782143ba1 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -59,6 +59,8 @@ extern "C" int mon_check(); // keyword collision localparam int nullptr /*verilator public */ = 123; + logic [31:0] some_mem [4] /* verilator public_flat_rd */ = {0, 0, 0, 432}; + sub sub(); // Test loop diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 93b720f3d..61df88f63 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -75,6 +75,7 @@ extern "C" int mon_check(); real real1; string str1; localparam int nullptr = 123; + logic [31:0] some_mem [4] = {0, 0, 0, 432}; /*verilator public_off*/ sub sub(); diff --git a/test_regress/t/t_vpi_var3.v b/test_regress/t/t_vpi_var3.v index a6422b8c2..a2f75b88b 100644 --- a/test_regress/t/t_vpi_var3.v +++ b/test_regress/t/t_vpi_var3.v @@ -56,6 +56,7 @@ extern "C" int mon_check(); real real1; string str1; localparam int nullptr = 123; + logic [31:0] some_mem [4] = {0, 0, 0, 432}; sub sub(); From 188a12f609a19ffca9a192213dd44c2a2d4e7ee2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 7 Aug 2025 18:17:00 +0100 Subject: [PATCH 146/252] CI: Add raising GitHub issue on failure of scheduled RTLMeter job (#6270) --- .github/workflows/rtlmeter.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index e4d7a9eb3..94cfa153b 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -181,6 +181,7 @@ jobs: private-key: ${{ secrets.RTLMETER_RESULTS_CI_APP_PRIVATE_KEY }} owner: verilator repositories: verilator-rtlmeter-results + permission-contents: write - name: Checkout verilator-rtlmeter-results uses: actions/checkout@v4 with: @@ -205,3 +206,31 @@ jobs: git add . git commit -m "Verilator CI: Results of 'RTLMeter' workflow run #${{ github.run_number }}" git push origin + + # Create GitHub issue for failed schedueld jobs + # This should always be the last job (we want an issue if anything breaks) + create-issue: + name: Create issue on failure + needs: publish-results + if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && !cancelled() }} + runs-on: ubuntu-24.04 + steps: + # Creating issues requires elevated privilege + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v2.0.6 + with: + app-id: ${{ vars.RTLMETER_RESULTS_CI_APP_ID }} + private-key: ${{ secrets.RTLMETER_RESULTS_CI_APP_PRIVATE_KEY }} + owner: verilator + repositories: verilator + permission-issues: write + - name: Create issue + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + gh issue --repo ${{ github.repository }} create \ + --title "Testing - RTLMeter run #${{ github.run_number}} Failed" \ + --body "See ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ + --label new \ + --assignee gezalore,wsnyder From b2388faa5e9ff1e319c6eaf8a414dd7011bd48d5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 7 Aug 2025 19:30:43 +0100 Subject: [PATCH 147/252] CI: Add running RTLMeter workflow on selective PRs (#6271) Runs RTLMeter on PRs if the PR has the 'pr: rtlmeter' label applied to it. This should make it easy to selectively require the RTLMeter workflows to pass on a PR that is potentially invasive, per our own judgement. --- .github/workflows/rtlmeter.yml | 37 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index 94cfa153b..3344907f1 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -9,6 +9,13 @@ on: workflow_dispatch: schedule: - cron: '0 2 * * *' # Daily, starting at 02:00 UTC + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + - unlabeled permissions: contents: read @@ -18,14 +25,29 @@ defaults: shell: bash concurrency: - group: RTLMeter - cancel-in-progress: false + group: ${{ github.ref }} + cancel-in-progress: ${{ github.event_name != 'schedule' }} jobs: + start: + name: Start + # Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks) + # Only run pull request jobs if labelled as needing an RTLMeter run + # Always run workflow dispatch jobs + if: | + (github.event_name == 'schedule' + && vars.ENABLE_SCHEDULED_JOBS == 'true') || + (github.event_name == 'pull_request' + && contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) || + (github.event_name == 'workflow_dispatch') + runs-on: ubuntu-24.04 + steps: + - name: Startup + run: echo + build-gcc: name: Build GCC - # Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks) - if: ${{ github.event_name != 'schedule' || vars.ENABLE_SCHEDULED_JOBS == 'true' }} + needs: start uses: ./.github/workflows/reusable-rtlmeter-build.yml with: runs-on: ubuntu-24.04 @@ -33,8 +55,7 @@ jobs: build-clang: name: Build Clang - # Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks) - if: ${{ github.event_name != 'schedule' || vars.ENABLE_SCHEDULED_JOBS == 'true' }} + needs: start uses: ./.github/workflows/reusable-rtlmeter-build.yml with: runs-on: ubuntu-24.04 @@ -53,7 +74,7 @@ jobs: executeArgs: "" strategy: fail-fast: false - max-parallel: 2 + max-parallel: ${{ github.event == 'schedule' && 2 || 7 }} matrix: cases: - "NVDLA:*" @@ -94,7 +115,7 @@ jobs: executeArgs: "" strategy: fail-fast: false - max-parallel: 2 + max-parallel: ${{ github.event == 'schedule' && 2 || 7 }} matrix: cases: - "NVDLA:*" From 9ddbf5f4b8e875a192240e2ba66c7e35aad734fa Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 8 Aug 2025 08:06:25 +0100 Subject: [PATCH 148/252] CI: Only raise RTLMeter failure issue on failure --- .github/workflows/rtlmeter.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index 3344907f1..ec6bef1b3 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -233,7 +233,7 @@ jobs: create-issue: name: Create issue on failure needs: publish-results - if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && !cancelled() }} + if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} runs-on: ubuntu-24.04 steps: # Creating issues requires elevated privilege @@ -251,7 +251,7 @@ jobs: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | gh issue --repo ${{ github.repository }} create \ - --title "Testing - RTLMeter run #${{ github.run_number}} Failed" \ + --title "RTLMeter run #${{ github.run_number }} Failed" \ --body "See ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ --label new \ --assignee gezalore,wsnyder From dbb8cbece2ff88f5fd1e29e2a6b0cce94f7a3203 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 8 Aug 2025 09:13:17 +0100 Subject: [PATCH 149/252] CI: Improve RTLMeter failure issue body (#6275) --- .github/workflows/rtlmeter.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index ec6bef1b3..f7135084d 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -250,8 +250,11 @@ jobs: env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | + echo "This issue was created automatically by the GitHub Actions CI due to the failure of a scheduled RTLMeter run." >> body.txt + echo "" >> body.txt + echo "Workflow status: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> body.txt gh issue --repo ${{ github.repository }} create \ --title "RTLMeter run #${{ github.run_number }} Failed" \ - --body "See ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ + --body-file body.txt \ --label new \ --assignee gezalore,wsnyder From b12b1c9658e73f085da9216936643a6b18d1536b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 05:09:06 -0400 Subject: [PATCH 150/252] Commentary: Changes update --- Changes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changes b/Changes index c0c5f28ca..8c0734804 100644 --- a/Changes +++ b/Changes @@ -33,6 +33,7 @@ Verilator 5.039 devel * Support randomize() on class member selects (#6161) (#6195). [Igor Zaworski, Ryszard Rozak, Antmicro Ltd.] * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] +* Support parameter resolution of 1D unpacked array slices (#6257) (#6268). [Michael Bedford Taylor] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Improve `--skip-identical` to skip on identical input file contents (#6109). @@ -69,6 +70,9 @@ Verilator 5.039 devel * Fix write of 0 in '%c' (#6248) (#6249). [Rodrigo Batista de Moraes] * Fix coverage of variables of complex types (#6250). [Ryszard Rozak, Antmicro Ltd.] * Fix partial DFG conversion of concat assignments (#6255). [Geza Lore] +* Fix dynamic cast purity (#6267). [Igor Zaworski, Antmicro Ltd.] +* Fix same variable on the RHS forced to two different LHSs. (#6269). [Artur Bieniek, Antmicro Ltd.] +* Fix spurious VPI value change callbacks (#6274). [Todd Strader] Verilator 5.038 2025-07-08 From 6a225d5d008451337bdfb32635934f74d83e08c7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 05:09:54 -0400 Subject: [PATCH 151/252] Internals: Remove AstSysFuncAsTask --- src/V3AstNodeOther.h | 18 ------------------ src/V3EmitCFunc.h | 11 +++++------ src/V3EmitV.cpp | 4 ---- src/V3Width.cpp | 25 +++++++++++-------------- src/verilog.y | 2 +- test_regress/t/t_dump_json.out | 4 ++-- 6 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 8f3ebc539..af7d445d4 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3522,24 +3522,6 @@ public: string emitVerilog() const { return m_isFatal ? "$fatal" : "$stop"; } bool isFatal() const { return m_isFatal; } }; -class AstSysFuncAsTask final : public AstNodeStmt { - // TODO: This is superseded by AstStmtExpr, remove - // Call what is normally a system function (with a return) in a non-return context - // @astgen op1 := lhsp : AstNodeExpr -public: - AstSysFuncAsTask(FileLine* fl, AstNodeExpr* lhsp) - : ASTGEN_SUPER_SysFuncAsTask(fl) { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstSysFuncAsTask; - string verilogKwd() const override { return ""; } - bool isGateOptimizable() const override { return true; } - bool isPredictOptimizable() const override { return true; } - bool isPure() override { return true; } - bool isOutputter() override { return false; } - int instrCount() const override { return 0; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; class AstSystemT final : public AstNodeStmt { // $system used as task // @astgen op1 := lhsp : AstNodeExpr diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 9172013b5..b17552635 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1044,11 +1044,6 @@ public: } puts(")"); } - void visit(AstSysFuncAsTask* nodep) override { - if (!nodep->lhsp()->isWide()) putns(nodep, "(void)"); - iterateAndNextConstNull(nodep->lhsp()); - if (!nodep->lhsp()->isWide()) putns(nodep, ";\n"); - } void visit(AstStackTraceF* nodep) override { putns(nodep, "VL_STACKTRACE_N()"); } void visit(AstStackTraceT* nodep) override { putns(nodep, "VL_STACKTRACE();\n"); } void visit(AstSystemT* nodep) override { @@ -1076,7 +1071,11 @@ public: puts(")"); } void visit(AstStmtExpr* nodep) override { - putns(nodep, ""); + if (VN_IS(nodep->exprp()->dtypep()->skipRefp(), VoidDType)) { + putns(nodep, ""); + } else { + putns(nodep, "(void)"); // Prevent unused expression warning in C + } iterateConst(nodep->exprp()); puts(";\n"); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 468417706..81050ab49 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -344,10 +344,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } puts(");\n"); } - void visit(AstSysFuncAsTask* nodep) override { - iterateAndNextConstNull(nodep->lhsp()); - puts(";\n"); - } void visit(AstSysIgnore* nodep) override { putfs(nodep, nodep->verilogKwd()); putbs("("); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 269006aaf..22a7c78b8 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1432,6 +1432,11 @@ class WidthVisitor final : public VNVisitor { } } + void visit(AstStmtExpr* nodep) override { + assertAtStatement(nodep); + userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p()); + } + void visit(AstImplication* nodep) override { if (m_vup->prelim()) { iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH); @@ -4193,16 +4198,12 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeFrom(ftaskp); nodep->classOrPackagep(classp); if (VN_IS(ftaskp, Task)) { - if (!m_vup) { - nodep->dtypeSetVoid(); - } else { - if (m_vup->prelim()) { - nodep->v3error( - "Cannot call a task/void-function as a member function: " - << nodep->prettyNameQ()); - } - nodep->dtypeSetVoid(); + if (m_vup && m_vup->prelim() && !VN_IS(nodep->backp(), StmtExpr)) { + nodep->v3error( + "Cannot call a task/void-function as a member function: " + << nodep->prettyNameQ()); } + nodep->dtypeSetVoid(); } if (withp) nodep->addPinsp(withp); processFTaskRefArgs(nodep); @@ -5766,10 +5767,6 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetSigned32(); // Spec says integer return } } - void visit(AstSysFuncAsTask* nodep) override { - assertAtStatement(nodep); - userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); - } void visit(AstSystemT* nodep) override { assertAtStatement(nodep); userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); @@ -6143,7 +6140,7 @@ class WidthVisitor final : public VNVisitor { visit(static_cast(nodep)); if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) { UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expection"); - if (m_vup->prelim()) + if (m_vup->prelim() && !VN_IS(nodep->backp(), StmtExpr)) nodep->v3error( "Cannot call a task/void-function as a function: " << nodep->prettyNameQ()); nodep->dtypeSetVoid(); diff --git a/src/verilog.y b/src/verilog.y index 8f1242b03..8f9051fdf 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4430,7 +4430,7 @@ system_t_call: // IEEE: system_tf_call (as task) nullptr, nullptr}; } // // Any system function as a task - | system_f_call_or_t { $$ = new AstSysFuncAsTask{$1, $1}; } + | system_f_call_or_t { $$ = new AstStmtExpr{$1, $1}; } ; system_f_call: // IEEE: system_tf_call (as func) diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 7f456ecf7..3d03126d8 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -1028,8 +1028,8 @@ "childDTypep": [ {"type":"BASICDTYPE","name":"string","addr":"(KQ)","loc":"d,231:35,231:41","dtypep":"(KQ)","keyword":"string","generic":false,"rangep": []} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"SYSFUNCASTASK","name":"","addr":"(LQ)","loc":"d,232:10,232:18", - "lhsp": [ + {"type":"STMTEXPR","name":"","addr":"(LQ)","loc":"d,232:10,232:18", + "exprp": [ {"type":"RAND","name":"","addr":"(MQ)","loc":"d,232:10,232:18","dtypep":"UNLINKED", "seedp": [ {"type":"DOT","name":"","addr":"(NQ)","loc":"d,232:20,232:21","dtypep":"UNLINKED","colon":false, From d1f851e1e146e631ae05cecc2389b0ba0949a7d0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 05:10:40 -0400 Subject: [PATCH 152/252] Internals: Optimize empty 'if' into StmtExpr --- src/V3AstNodeExpr.h | 1 + src/V3Const.cpp | 12 ++++++++++-- src/V3Randomize.cpp | 6 ++++-- src/V3Task.cpp | 3 ++- test_regress/t/t_class_dyn_cast_empty_if.v | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 300aea817..1e06fd10c 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4524,6 +4524,7 @@ public: void isImplicit(bool flag) { m_isImplicit = flag; } bool isScoped() const { return m_isScoped; } void isScoped(bool flag) { m_isScoped = flag; } + bool isPure() override { return false; } }; class AstTaskRef final : public AstNodeFTaskRef { // A reference to a task diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 9b5ba8996..2afc17493 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3241,10 +3241,16 @@ class ConstVisitor final : public VNVisitor { } else if (!AstNode::afterCommentp(nodep->thensp()) && !AstNode::afterCommentp(nodep->elsesp())) { if (!nodep->condp()->isPure()) { - // Condition has side effect - leave - perhaps in - // future simplify to remove all but side effect terms + // Condition has side effect - leave, but don't need return value + UINFO(4, "IF({x}) ; => STMTEXPR({x}): " << nodep); + nodep->fileline()->warnOff(V3ErrorCode::IGNOREDRETURN, true); + AstNodeStmt* const newp + = new AstStmtExpr{nodep->fileline(), nodep->condp()->unlinkFrBack()}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // Empty block, remove it + UINFO(4, "IF({x}) ; => ; : " << nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } } else if (!AstNode::afterCommentp(nodep->thensp())) { @@ -3498,6 +3504,8 @@ class ConstVisitor final : public VNVisitor { } // TODO if there's an ExprStmt underneath just keep lower statements // (No current test case needs this) + // TODO if non-pure, can remove keep removing upper pure-itself + // NodeUniOp/NodeBiOp until get to the non-pure child-node expression } // Simplify diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index ecf20d1d4..26fb1932d 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -241,7 +241,8 @@ class RandomizeMarkVisitor final : public VNVisitor { methodHardp->v3fatalSrc("Unknown rand_mode() receiver"); } } - if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr)) { + if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr) + && !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) { nodep->v3warn( IGNOREDRETURN, "Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)"); @@ -302,7 +303,8 @@ class RandomizeMarkVisitor final : public VNVisitor { nodep->v3error( "'constraint_mode()' with arguments cannot be called as a function"); valid = false; - } else if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr)) { + } else if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr) + && !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) { nodep->v3warn( IGNOREDRETURN, "Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)"); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 358811654..b9a00f833 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1510,7 +1510,8 @@ class TaskVisitor final : public VNVisitor { VIsCached::clearCacheTree(); } else { // VN_IS(nodep->backp(), StmtExpr) insertBeforeStmt(nodep, beginp); - if (nodep->taskp()->isFunction()) { + if (nodep->taskp()->isFunction() + && !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) { nodep->v3warn( IGNOREDRETURN, "Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)"); diff --git a/test_regress/t/t_class_dyn_cast_empty_if.v b/test_regress/t/t_class_dyn_cast_empty_if.v index f11e35bd4..42a02fc02 100644 --- a/test_regress/t/t_class_dyn_cast_empty_if.v +++ b/test_regress/t/t_class_dyn_cast_empty_if.v @@ -7,7 +7,7 @@ typedef class Derived; class Base; function Derived cast(); - if ($cast(cast,this)) begin end + if (!$cast(cast, this)) begin end endfunction endclass From 16d32cdd4aa80e483bea25a3012790e9bede68e9 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 8 Aug 2025 22:53:12 +0100 Subject: [PATCH 153/252] Internals: Refactor Ast to Dfg conversion for reusability. (#6276) This is mainly code motion, with minimal algorithmic changes to facilitate reusing parts in future code. No functional change intended. --- src/V3DfgAstToDfg.cpp | 1056 ++++++++++-------- src/V3DfgContext.h | 2 +- src/V3DfgOptimizer.cpp | 6 +- src/V3DfgPasses.cpp | 5 + src/V3DfgPasses.h | 4 +- src/V3Options.h | 2 +- src/astgen | 8 +- test_regress/t/t_dfg_multidriver_dfg_bad.out | 8 +- 8 files changed, 614 insertions(+), 477 deletions(-) diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 760efd9b3..58b6166ee 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -83,87 +83,43 @@ DfgSliceSel* makeVertex(const AstSliceSel*, DfgGraph&) } // namespace +// Visitor that can convert combinational Ast logic constructs/assignments to Dfg template -class AstToDfgVisitor final : public VNVisitor { +class AstToDfgConverter final : public VNVisitor { // NODE STATE - - // AstNode::user2p // DfgVertex for this AstNode - const VNUser2InUse m_user2InUse; + // AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node // TYPES - using RootType = std::conditional_t; - using VariableType = std::conditional_t; + using Variable = std::conditional_t; // STATE - DfgGraph* const m_dfgp; // The graph being built + DfgGraph& m_dfg; // The graph being built V3DfgAstToDfgContext& m_ctx; // The context for stats bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit' - std::vector m_uncommittedVertices; // Vertices that we might decide to revert bool m_converting = false; // We are trying to convert some logic at the moment - std::vector m_varPackedps; // All the DfgVarPacked vertices we created. - std::vector m_varArrayps; // All the DfgVarArray vertices we created. + std::vector m_uncommittedSpliceps; // New splices made during convertLValue // METHODS - static VariableType* getTarget(const AstVarRef* refp) { + static Variable* getTarget(const AstVarRef* refp) { // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works if VL_CONSTEXPR_CXX17 (T_Scoped) { - return reinterpret_cast(refp->varScopep()); + return reinterpret_cast(refp->varScopep()); } else { - return reinterpret_cast(refp->varp()); + return reinterpret_cast(refp->varp()); } } - static AstVar* getAstVar(VariableType* vp) { - // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works - if VL_CONSTEXPR_CXX17 (T_Scoped) { - return reinterpret_cast(vp)->varp(); - } else { - return reinterpret_cast(vp); + DfgVertexVar* getNet(Variable* varp) { + if (!varp->user2p()) { + AstNodeDType* const dtypep = varp->dtypep()->skipRefp(); + DfgVertexVar* const vtxp + = VN_IS(dtypep, UnpackArrayDType) + ? static_cast(new DfgVarArray{m_dfg, varp}) + : static_cast(new DfgVarPacked{m_dfg, varp}); + varp->user2p(vtxp); } - } - - void markReferenced(AstNode* nodep) { - nodep->foreach([](const AstVarRef* refp) { - VariableType* const tgtp = getTarget(refp); - // Mark as read from non-DFG logic - if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp); - // Mark as written from non-DFG logic - if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp); - }); - } - - void commitVertices() { m_uncommittedVertices.clear(); } - - void revertUncommittedVertices() { - for (DfgVertex* const vtxp : m_uncommittedVertices) vtxp->unlinkDelete(*m_dfgp); - m_uncommittedVertices.clear(); - } - - DfgVertexVar* getNet(VariableType* vp) { - if (!vp->user2p()) { - // vp DfgVertexVar vertices are not added to m_uncommittedVertices, because we - // want to hold onto them via AstVar::user2p, and the AstVar might be referenced via - // multiple AstVarRef instances, so we will never revert a DfgVertexVar once - // created. We will delete unconnected variable vertices at the end. - if (VN_IS(vp->dtypep()->skipRefp(), UnpackArrayDType)) { - DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, vp}; - vp->user2p(); - m_varArrayps.push_back(vtxp); - vp->user2p(vtxp); - } else { - DfgVarPacked* const vtxp = new DfgVarPacked{*m_dfgp, vp}; - m_varPackedps.push_back(vtxp); - vp->user2p(vtxp); - } - } - return vp->user2u().template to(); - } - - DfgVertex* getVertex(AstNode* nodep) { - DfgVertex* vtxp = nodep->user2u().to(); - UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex"); - return vtxp; + return varp->user2u().template to(); } // Returns true if the expression cannot (or should not) be represented by DFG @@ -184,22 +140,74 @@ class AstToDfgVisitor final : public VNVisitor { return m_foundUnhandled; } - std::pair convertLValue(AstNode* nodep) { - if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { - m_foundUnhandled = false; - visit(vrefp); - if (m_foundUnhandled) return {nullptr, 0}; + bool isSupported(const AstVar* varp) { + if (varp->isIfaceRef()) return false; // Cannot handle interface references + if (varp->delayp()) return false; // Cannot handle delayed variables + if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore + return DfgVertex::isSupportedDType(varp->dtypep()); + } + bool isSupported(const AstVarScope* vscp) { + // Check the Var fist + if (!isSupported(vscp->varp())) return false; + // If the variable is not in a regular module, then do not convert it. + // This is especially needed for variabels in interfaces which might be + // referenced via virtual intefaces, which cannot be resovled statically. + if (!VN_IS(vscp->scopep()->modp(), Module)) return false; + // Otherwise OK + return true; + } + + bool isSupported(const AstVarRef* nodep) { + // Cannot represent cross module references + if (nodep->classOrPackagep()) return false; + // Check target + return isSupported(getTarget(nodep)); + } + + // Given an RValue expression, return the equivalent Vertex, or nullptr if not representable. + DfgVertex* convertRValue(AstNodeExpr* nodep) { + UASSERT_OBJ(!m_converting, nodep, "'convertingRValue' should not be called recursively"); + VL_RESTORER(m_converting); + VL_RESTORER(m_foundUnhandled); + m_converting = true; + m_foundUnhandled = false; + + // Convert the expression + iterate(nodep); + + // If falied to convert, return nullptr + if (m_foundUnhandled) return nullptr; + + // Traversal set user2p to the equivalent vertex + DfgVertex* const vtxp = nodep->user2u().to(); + UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex after covnersion"); + return vtxp; + } + + // Given an LValue expression, return the splice node that writes the + // destination, together with the index to use for splicing in the value. + // Returns {nullptr, 0}, if the given LValue expression is not supported. + std::pair convertLValue(AstNodeExpr* nodep) { + if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { + if (!isSupported(vrefp)) { + ++m_ctx.m_nonRepLhs; + return {nullptr, 0}; + } // Get the variable vertex - DfgVertexVar* const vtxp = getVertex(vrefp)->template as(); + DfgVertexVar* const vtxp = getNet(getTarget(vrefp)); // Ensure the Splice driver exists for this variable if (!vtxp->srcp()) { FileLine* const flp = vtxp->fileline(); AstNodeDType* const dtypep = vtxp->dtypep(); if (vtxp->is()) { - vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, dtypep}); + DfgSplicePacked* const newp = new DfgSplicePacked{m_dfg, flp, dtypep}; + m_uncommittedSpliceps.emplace_back(newp); + vtxp->srcp(newp); } else if (vtxp->is()) { - vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, dtypep}); + DfgSpliceArray* const newp = new DfgSpliceArray{m_dfg, flp, dtypep}; + m_uncommittedSpliceps.emplace_back(newp); + vtxp->srcp(newp); } else { nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE } @@ -250,9 +258,13 @@ class AstToDfgVisitor final : public VNVisitor { FileLine* const flp = nodep->fileline(); AstNodeDType* const dtypep = DfgVertex::dtypeFor(nodep); if (VN_IS(dtypep, BasicDType)) { - splicep->addDriver(flp, index, new DfgSplicePacked{*m_dfgp, flp, dtypep}); + DfgSplicePacked* const newp = new DfgSplicePacked{m_dfg, flp, dtypep}; + m_uncommittedSpliceps.emplace_back(newp); + splicep->addDriver(flp, index, newp); } else if (VN_IS(dtypep, UnpackArrayDType)) { - splicep->addDriver(flp, index, new DfgSpliceArray{*m_dfgp, flp, dtypep}); + DfgSpliceArray* const newp = new DfgSpliceArray{m_dfg, flp, dtypep}; + m_uncommittedSpliceps.emplace_back(newp); + splicep->addDriver(flp, index, newp); } else { nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE } @@ -266,8 +278,10 @@ class AstToDfgVisitor final : public VNVisitor { return {nullptr, 0}; } - // Convert assignment. Returns false if unsuccessful. - bool convertAssignment(FileLine* flp, AstNode* lhsp, DfgVertex* vtxp) { + // Given the LHS of an assignment, and the vertex representing the RHS, + // connect up the RHS to drive the targets. + // Returns true on success, false if the LHS is not representable. + bool convertAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* vtxp) { // Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'. // There might be multiple of these if 'lhsp' is a concatenation. struct Assignment final { @@ -285,24 +299,24 @@ class AstToDfgVisitor final : public VNVisitor { // we need to do into 'assignments', return true if all LValues // converted successfully. std::vector assignments; - const std::function convertAllLValues - = [&](AstNode* lhsp, DfgVertex* vtxp) -> bool { + const std::function convertAllLValues + = [&](AstNodeExpr* lhsp, DfgVertex* vtxp) -> bool { // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) - lhsp = V3Const::constifyExpensiveEdit(lhsp); + lhsp = VN_AS(V3Const::constifyExpensiveEdit(lhsp), NodeExpr); // Concatenation on the LHS, convert each parts if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { - AstNode* const cLhsp = concatp->lhsp(); - AstNode* const cRhsp = concatp->rhsp(); + AstNodeExpr* const cLhsp = concatp->lhsp(); + AstNodeExpr* const cRhsp = concatp->rhsp(); // Convert Left of concat FileLine* const lFlp = cLhsp->fileline(); - DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(cLhsp)}; + DfgSel* const lVtxp = new DfgSel{m_dfg, lFlp, DfgVertex::dtypeFor(cLhsp)}; lVtxp->fromp(vtxp); lVtxp->lsb(cRhsp->width()); if (!convertAllLValues(cLhsp, lVtxp)) return false; // Convert Rigth of concat FileLine* const rFlp = cRhsp->fileline(); - DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(cRhsp)}; + DfgSel* const rVtxp = new DfgSel{m_dfg, rFlp, DfgVertex::dtypeFor(cRhsp)}; rVtxp->fromp(vtxp); rVtxp->lsb(0); return convertAllLValues(cRhsp, rVtxp); @@ -315,7 +329,14 @@ class AstToDfgVisitor final : public VNVisitor { return true; }; // Convert the given LHS assignment, give up if any LValues failed to convert - if (!convertAllLValues(lhsp, vtxp)) return false; + if (!convertAllLValues(lhsp, vtxp)) { + for (DfgVertexSplice* const splicep : m_uncommittedSpliceps) { + VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); + } + m_uncommittedSpliceps.clear(); + return false; + } + m_uncommittedSpliceps.clear(); // All successful, connect the drivers for (const Assignment& a : assignments) { @@ -330,20 +351,18 @@ class AstToDfgVisitor final : public VNVisitor { return true; } - bool convertEquation(AstNode* nodep, FileLine* flp, AstNode* lhsp, AstNode* rhsp) { - UASSERT_OBJ(m_uncommittedVertices.empty(), nodep, "Should not nest"); - + // Convert the assignment with the given LHS and RHS into DFG. + // Returns true on success, false if not representable. + bool convertEquation(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) { // Check data types are compatible. if (!DfgVertex::isSupportedDType(lhsp->dtypep()) || !DfgVertex::isSupportedDType(rhsp->dtypep())) { - markReferenced(nodep); ++m_ctx.m_nonRepDType; return false; } // For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c) if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) { - markReferenced(nodep); ++m_ctx.m_nonRepDType; return false; } @@ -351,71 +370,199 @@ class AstToDfgVisitor final : public VNVisitor { // Cannot handle mismatched widths. Mismatched assignments should have been fixed up in // earlier passes anyway, so this should never be hit, but being paranoid just in case. if (lhsp->width() != rhsp->width()) { // LCOV_EXCL_START - markReferenced(nodep); ++m_ctx.m_nonRepWidth; return false; } // LCOV_EXCL_STOP - VL_RESTORER(m_converting); - m_converting = true; + // Convert the RHS expression + DfgVertex* const rVtxp = convertRValue(rhsp); + if (!rVtxp) return false; - m_foundUnhandled = false; - iterate(rhsp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) { - revertUncommittedVertices(); - markReferenced(nodep); - return false; - } + // Connect the RHS vertex to the LHS targets + if (!convertAssignment(flp, lhsp, rVtxp)) return false; - if (!convertAssignment(flp, lhsp, getVertex(rhsp))) { - revertUncommittedVertices(); - markReferenced(nodep); - return false; - } - - // Connect the rhs vertex to the driven edge - commitVertices(); - - // Remove node from Ast. Now represented by the Dfg. - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - - // + // All good ++m_ctx.m_representable; return true; } - // Prune vertices potentially unused due to resolving multiple drivers. - // Having multiple drivers is an error and is hence assumed to be rare, - // so performance is not very important, set will suffice. - void removeUnused(std::set& prune) { - while (!prune.empty()) { - // Pop last vertex - const auto it = prune.begin(); - DfgVertex* const vtxp = *it; - prune.erase(it); - // If used (or a variable), then done - if (vtxp->hasSinks() || vtxp->is()) continue; - // If unused, then add sources to work list and delete - vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); }); - vtxp->unlinkDelete(*m_dfgp); + // Convert an AstNodeAssign (AstAssign or AstAssignW) + bool convertNodeAssign(AstNodeAssign* nodep) { + UASSERT_OBJ(VN_IS(nodep, AssignW) || VN_IS(nodep, Assign), nodep, "Invalid subtype"); + ++m_ctx.m_inputEquations; + + // Cannot handle assignment with timing control yet + if (nodep->timingControlp()) { + ++m_ctx.m_nonRepTiming; + return false; } + + return convertEquation(nodep->fileline(), nodep->lhsp(), nodep->rhsp()); } - // Normalize packed driver - return the normalized vertex and location for 'splicep' - std::pair // - normalizePacked(DfgVertexVar* varp, const std::string& sub, DfgSplicePacked* const splicep) { - // Represents a driver of 'splicep' - struct Driver final { - FileLine* m_fileline; - DfgVertex* m_vtxp; - uint32_t m_lsb; - Driver() = delete; - Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) - : m_fileline{flp} - , m_vtxp{vtxp} - , m_lsb{lsb} {} - }; + // Convert special simple form Always block into DFG. + // Returns true on success, false if not representable/not simple. + bool convertSimpleAlways(AstAlways* nodep) { + // Only consider single statement block + if (!nodep->isJustOneBodyStmt()) return false; + + AstNode* const stmtp = nodep->stmtsp(); + + if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) { + return convertNodeAssign(assignp); + } + + if (AstIf* const ifp = VN_CAST(stmtp, If)) { + // Will only handle single assignments to the same LHS in both branches + AstAssign* const thenp = VN_CAST(ifp->thensp(), Assign); + AstAssign* const elsep = VN_CAST(ifp->elsesp(), Assign); + if (!thenp || !elsep || thenp->nextp() || elsep->nextp() + || !thenp->lhsp()->sameTree(elsep->lhsp())) { + return false; + } + + ++m_ctx.m_inputEquations; + if (thenp->timingControlp() || elsep->timingControlp()) { + ++m_ctx.m_nonRepTiming; + return false; + } + + // Create a conditional for the rhs by borrowing the components from the AstIf + AstCond* const rhsp = new AstCond{ifp->fileline(), // + ifp->condp()->unlinkFrBack(), // + thenp->rhsp()->unlinkFrBack(), // + elsep->rhsp()->unlinkFrBack()}; + const bool success = convertEquation(ifp->fileline(), thenp->lhsp(), rhsp); + // Put the AstIf back together + ifp->condp(rhsp->condp()->unlinkFrBack()); + thenp->rhsp(rhsp->thenp()->unlinkFrBack()); + elsep->rhsp(rhsp->elsep()->unlinkFrBack()); + // Delete the auxiliary conditional + VL_DO_DANGLING(rhsp->deleteTree(), rhsp); + return success; + } + + return false; + } + + // VISITORS + + // Unhandled node + void visit(AstNode* nodep) override { + if (!m_foundUnhandled && m_converting) ++m_ctx.m_nonRepUnknown; + m_foundUnhandled = true; + } + + // Expressions - mostly auto generated, but a few special ones + void visit(AstVarRef* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + // This visit method is only called on RValues, where only read refs are supportes + if (!nodep->access().isReadOnly() || !isSupported(nodep)) { + m_foundUnhandled = true; + ++m_ctx.m_nonRepVarRef; + return; + } + nodep->user2p(getNet(getTarget(nodep))); + } + void visit(AstConst* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + DfgVertex* const vtxp = new DfgConst{m_dfg, nodep->fileline(), nodep->num()}; + nodep->user2p(vtxp); + } + void visit(AstSel* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + + iterate(nodep->fromp()); + if (m_foundUnhandled) return; + + FileLine* const flp = nodep->fileline(); + DfgVertex* vtxp = nullptr; + if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { + DfgSel* const selp = new DfgSel{m_dfg, flp, DfgVertex::dtypeFor(nodep)}; + selp->fromp(nodep->fromp()->user2u().to()); + selp->lsb(constp->toUInt()); + vtxp = selp; + } else { + iterate(nodep->lsbp()); + if (m_foundUnhandled) return; + DfgMux* const muxp = new DfgMux{m_dfg, flp, DfgVertex::dtypeFor(nodep)}; + muxp->fromp(nodep->fromp()->user2u().to()); + muxp->lsbp(nodep->lsbp()->user2u().to()); + vtxp = muxp; + } + nodep->user2p(vtxp); + } +// The rest of the visit methods for expressions are generated by 'astgen' +#include "V3Dfg__gen_ast_to_dfg.h" + +public: + // PUBLIC METHODS + + // Convert AstAssignW to Dfg, return true if successful. + bool convert(AstAssignW* nodep) { + if (convertNodeAssign(nodep)) { + // Remove node from Ast. Now represented by the Dfg. + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return true; + } + + return false; + } + + // Convert AstAlways to Dfg, return true if successful. + bool convert(AstAlways* nodep) { + // Ignore sequential logic + const VAlwaysKwd kwd = nodep->keyword(); + if (nodep->sensesp() || (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) { + return false; + } + + // Attemp to convert special forms + if (convertSimpleAlways(nodep)) { + // Remove node from Ast. Now represented by the Dfg. + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return true; + } + + return false; + } + + // CONSTRUCTOR + AstToDfgConverter(DfgGraph& dfg, V3DfgAstToDfgContext& ctx) + : m_dfg{dfg} + , m_ctx{ctx} {} +}; + +// Resolves multiple drivers (keep only the first one), +// and ensures drivers are stored in ascending index order +class AstToDfgNormalizeDrivers final { + // TYPES + struct Driver final { + FileLine* m_flp; // Location of driver in source + uint32_t m_low; // Low index of driven range + DfgVertex* m_vtxp; // Driving vertex + Driver() = delete; + Driver(FileLine* flp, uint32_t low, DfgVertex* vtxp) + : m_flp{flp} + , m_low{low} + , m_vtxp{vtxp} {} + }; + + // STATE + DfgGraph& m_dfg; // The graph being processed + DfgVertexVar& m_var; // The variable being normalzied + + // METHODS + + // Normalize packed driver + void normalizePacked(const std::string& sub, DfgSplicePacked* const splicep) { + UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSplicePacked"); // The drivers of 'splicep' std::vector drivers; @@ -431,63 +578,57 @@ class AstToDfgVisitor final : public VNVisitor { gather(rhsp->fileline(), lsb, rhsp); DfgVertex* const lhsp = concatp->lhsp(); gather(lhsp->fileline(), lsb + rhs_width, lhsp); - concatp->unlinkDelete(*m_dfgp); + concatp->unlinkDelete(m_dfg); } else { drivers.emplace_back(flp, lsb, vtxp); } }; // Gather and unlink all drivers - splicep->forEachSourceEdge([&](DfgEdge& edge, size_t idx) { + splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { DfgVertex* const driverp = edge.sourcep(); UASSERT(driverp, "Should not have created undriven sources"); UASSERT_OBJ(!driverp->is(), splicep, "Should not be DfgVertexSplice"); - gather(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp); + gather(splicep->driverFileLine(i), splicep->driverLsb(i), driverp); edge.unlinkSource(); }); + splicep->resetSources(); const auto cmp = [](const Driver& a, const Driver& b) { - if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb; - return a.m_fileline->operatorCompare(*b.m_fileline) < 0; + if (a.m_low != b.m_low) return a.m_low < b.m_low; + return a.m_flp->operatorCompare(*b.m_flp) < 0; }; // Sort drivers by LSB std::stable_sort(drivers.begin(), drivers.end(), cmp); - // Vertices that might have become unused due to multiple driver resolution. Having - // multiple drivers is an error and is hence assumed to be rare, so performance is - // not very important, set will suffice. - std::set prune; - // Fix multiply driven ranges for (auto it = drivers.begin(); it != drivers.end();) { Driver& a = *it++; const uint32_t aWidth = a.m_vtxp->width(); - const uint32_t aEnd = a.m_lsb + aWidth; + const uint32_t aEnd = a.m_low + aWidth; while (it != drivers.end()) { Driver& b = *it; // If no overlap, then nothing to do - if (b.m_lsb >= aEnd) break; + if (b.m_low >= aEnd) break; const uint32_t bWidth = b.m_vtxp->width(); - const uint32_t bEnd = b.m_lsb + bWidth; + const uint32_t bEnd = b.m_low + bWidth; const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; // Loop index often abused, so suppress - if (!varp->varp()->isUsedLoopIdx()) { - AstNode* const vp = varp->varScopep() - ? static_cast(varp->varScopep()) - : static_cast(varp->varp()); - - vp->v3warn( // + if (!m_var.varp()->isUsedLoopIdx()) { + AstNode* const nodep = m_var.nodep(); + nodep->v3warn( // MULTIDRIVEN, "Bits [" // - << overlapEnd << ":" << b.m_lsb << "] of signal '" << vp->prettyName() - << sub << "' have multiple combinational drivers\n" - << a.m_fileline->warnOther() << "... Location of first driver\n" - << a.m_fileline->warnContextPrimary() << '\n' - << b.m_fileline->warnOther() << "... Location of other driver\n" - << b.m_fileline->warnContextSecondary() << vp->warnOther() + << overlapEnd << ":" << b.m_low << "] of signal '" + << nodep->prettyName() << sub + << "' have multiple combinational drivers\n" + << a.m_flp->warnOther() << "... Location of first driver\n" + << a.m_flp->warnContextPrimary() << '\n' + << b.m_flp->warnOther() << "... Location of other driver\n" + << b.m_flp->warnContextSecondary() << nodep->warnOther() << "... Only the first driver will be respected"); } @@ -495,84 +636,26 @@ class AstToDfgVisitor final : public VNVisitor { // we can just delete the second driver completely, otherwise adjust the // second driver to apply from the end of the range of the first driver. if (aEnd >= bEnd) { - prune.emplace(b.m_vtxp); it = drivers.erase(it); } else { const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd); - DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep}; + DfgSel* const selp = new DfgSel{m_dfg, b.m_vtxp->fileline(), dtypep}; selp->fromp(b.m_vtxp); - selp->lsb(aEnd - b.m_lsb); - b.m_lsb = aEnd; + selp->lsb(aEnd - b.m_low); + b.m_low = aEnd; b.m_vtxp = selp; std::stable_sort(it, drivers.end(), cmp); } } } - // Coalesce adjacent ranges - for (size_t i = 0, j = 1; j < drivers.size(); ++j) { - Driver& a = drivers[i]; - Driver& b = drivers[j]; - - // Coalesce adjacent range - const uint32_t aWidth = a.m_vtxp->width(); - const uint32_t bWidth = b.m_vtxp->width(); - if (a.m_lsb + aWidth == b.m_lsb) { - const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth); - DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep}; - concatp->rhsp(a.m_vtxp); - concatp->lhsp(b.m_vtxp); - a.m_vtxp = concatp; - b.m_vtxp = nullptr; // Mark as moved - ++m_ctx.m_coalescedAssignments; - continue; - } - - ++i; - - // Compact non-adjacent ranges within the vector - if (j != i) { - Driver& c = drivers[i]; - UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved"); - c = b; - b.m_vtxp = nullptr; // Mark as moved - } - } - // Reinsert drivers in order - splicep->resetSources(); - for (const Driver& driver : drivers) { - if (!driver.m_vtxp) break; // Stop at end of compacted list - splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); - } - - removeUnused(prune); - - // If the whole variable is driven whole, we can just use that driver - if (splicep->arity() == 1 // - && splicep->driverLsb(0) == 0 // - && splicep->source(0)->width() == splicep->width()) { - const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0)); - VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep); - return result; - } - return {splicep, splicep->fileline()}; + for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); } - // Normalize array driver - return the normalized vertex and location for 'splicep' - std::pair // - normalizeArray(DfgVertexVar* varp, const std::string& sub, DfgSpliceArray* const splicep) { - // Represents a driver of 'splicep' - struct Driver final { - FileLine* m_fileline; - DfgVertex* m_vtxp; - uint32_t m_idx; - Driver() = delete; - Driver(FileLine* flp, uint32_t idx, DfgVertex* vtxp) - : m_fileline{flp} - , m_vtxp{vtxp} - , m_idx{idx} {} - }; + // Normalize array driver + void normalizeArray(const std::string& sub, DfgSpliceArray* const splicep) { + UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSpliceArray"); // The drivers of 'splicep' std::vector drivers; @@ -583,67 +666,61 @@ class AstToDfgVisitor final : public VNVisitor { DfgVertex* const driverp = edge.sourcep(); UASSERT(driverp, "Should not have created undriven sources"); const uint32_t idx = splicep->driverIndex(i); - if (DfgSplicePacked* const spp = driverp->cast()) { - const auto pair - = normalizePacked(varp, sub + "[" + std::to_string(idx) + "]", spp); - drivers.emplace_back(pair.second, idx, pair.first); - } else if (DfgSpliceArray* const sap = driverp->cast()) { - const auto pair = normalizeArray(varp, sub + "[" + std::to_string(idx) + "]", sap); - drivers.emplace_back(pair.second, idx, pair.first); + // Normalize + if (DfgSplicePacked* const splicePackedp = driverp->cast()) { + normalizePacked(sub + "[" + std::to_string(idx) + "]", splicePackedp); + } else if (DfgSpliceArray* const spliceArrayp = driverp->cast()) { + normalizeArray(sub + "[" + std::to_string(idx) + "]", spliceArrayp); } else if (driverp->is()) { - driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); - } else { - drivers.emplace_back(splicep->driverFileLine(i), idx, driverp); + driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE } + // Gather + drivers.emplace_back(splicep->driverFileLine(i), idx, driverp); + // Unlink edge.unlinkSource(); }); + splicep->resetSources(); const auto cmp = [](const Driver& a, const Driver& b) { - if (a.m_idx != b.m_idx) return a.m_idx < b.m_idx; - return a.m_fileline->operatorCompare(*b.m_fileline) < 0; + if (a.m_low != b.m_low) return a.m_low < b.m_low; + return a.m_flp->operatorCompare(*b.m_flp) < 0; }; // Sort drivers by index std::stable_sort(drivers.begin(), drivers.end(), cmp); - // Vertices that become unused due to multiple driver resolution - std::set prune; - // Fix multiply driven ranges for (auto it = drivers.begin(); it != drivers.end();) { Driver& a = *it++; AstUnpackArrayDType* aArrayDTypep = VN_CAST(a.m_vtxp->dtypep(), UnpackArrayDType); const uint32_t aElements = aArrayDTypep ? aArrayDTypep->elementsConst() : 1; - const uint32_t aEnd = a.m_idx + aElements; + const uint32_t aEnd = a.m_low + aElements; while (it != drivers.end()) { Driver& b = *it; // If no overlap, then nothing to do - if (b.m_idx >= aEnd) break; + if (b.m_low >= aEnd) break; AstUnpackArrayDType* bArrayDTypep = VN_CAST(b.m_vtxp->dtypep(), UnpackArrayDType); const uint32_t bElements = bArrayDTypep ? bArrayDTypep->elementsConst() : 1; - const uint32_t bEnd = b.m_idx + bElements; + const uint32_t bEnd = b.m_low + bElements; const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - AstNode* const vp = varp->varScopep() ? static_cast(varp->varScopep()) - : static_cast(varp->varp()); - - vp->v3warn( // + AstNode* const nodep = m_var.nodep(); + nodep->v3warn( // MULTIDRIVEN, "Elements [" // - << overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName() + << overlapEnd << ":" << b.m_low << "] of signal '" << nodep->prettyName() << sub << "' have multiple combinational drivers\n" - << a.m_fileline->warnOther() << "... Location of first driver\n" - << a.m_fileline->warnContextPrimary() << '\n' - << b.m_fileline->warnOther() << "... Location of other driver\n" - << b.m_fileline->warnContextSecondary() << vp->warnOther() + << a.m_flp->warnOther() << "... Location of first driver\n" + << a.m_flp->warnContextPrimary() << '\n' + << b.m_flp->warnOther() << "... Location of other driver\n" + << b.m_flp->warnContextSecondary() << nodep->warnOther() << "... Only the first driver will be respected"); // If the first driver completely covers the range of the second driver, // we can just delete the second driver completely, otherwise adjust the // second driver to apply from the end of the range of the first driver. if (aEnd >= bEnd) { - prune.emplace(b.m_vtxp); it = drivers.erase(it); } else { const auto distance = std::distance(drivers.begin(), it); @@ -655,9 +732,9 @@ class AstToDfgVisitor final : public VNVisitor { it = drivers.erase(it); // Add missing items element-wise for (uint32_t i = aEnd; i < bEnd; ++i) { - DfgArraySel* const aselp = new DfgArraySel{*m_dfgp, flp, elemDtypep}; + DfgArraySel* const aselp = new DfgArraySel{m_dfg, flp, elemDtypep}; aselp->fromp(bVtxp); - aselp->bitp(new DfgConst{*m_dfgp, flp, 32, i}); + aselp->bitp(new DfgConst{m_dfg, flp, 32, i}); drivers.emplace_back(flp, i, aselp); } it = drivers.begin(); @@ -668,33 +745,245 @@ class AstToDfgVisitor final : public VNVisitor { } // Reinsert drivers in order + for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); + } + + // CONSTRUCTOR + AstToDfgNormalizeDrivers(DfgGraph& dfg, DfgVertexVar& var) + : m_dfg{dfg} + , m_var{var} { + // Nothing to do for un-driven (input) variables + if (!var.srcp()) return; + + // The driver of a variable must always be a splice vertex, normalize it + if (DfgSpliceArray* const sArrayp = var.srcp()->cast()) { + normalizeArray("", sArrayp); + } else if (DfgSplicePacked* const sPackedp = var.srcp()->cast()) { + normalizePacked("", sPackedp); + } else { + var.v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE + } + } + +public: + // Normalize drivers of given variable + static void apply(DfgGraph& dfg, DfgVertexVar& var) { AstToDfgNormalizeDrivers{dfg, var}; } +}; + +// Coalesce contiguous driver ranges, +// and remove redundant splice vertices (when the variable is driven whole) +class AstToDfgCoalesceDrivers final { + // TYPES + struct Driver final { + FileLine* m_flp; // Location of driver in source + uint32_t m_low; // Low index of driven range + DfgVertex* m_vtxp; // Driving vertex + Driver() = delete; + Driver(FileLine* flp, uint32_t low, DfgVertex* vtxp) + : m_flp{flp} + , m_low{low} + , m_vtxp{vtxp} {} + }; + + // STATE + DfgGraph& m_dfg; // The graph being processed + V3DfgAstToDfgContext& m_ctx; // The context for stats + + // METHODS + + // Coalesce packed driver - return the coalesced vertex and location for 'splicep' + std::pair coalescePacked(DfgSplicePacked* const splicep) { + UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSplicePacked"); + + // The drivers of 'splicep' + std::vector drivers; + drivers.reserve(splicep->arity()); + + // Gather and unlink all drivers + int64_t prevHigh = -1; // High index of previous driven range + splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { + DfgVertex* const driverp = edge.sourcep(); + UASSERT_OBJ(driverp, splicep, "Should not have created undriven sources"); + UASSERT_OBJ(!driverp->is(), splicep, "Should not be DfgVertexSplice"); + const uint32_t low = splicep->driverLsb(i); + UASSERT_OBJ(static_cast(low) > prevHigh, splicep, + "Drivers should have been normalized"); + prevHigh = low + driverp->width() - 1; + // Gather + drivers.emplace_back(splicep->driverFileLine(i), low, driverp); + // Unlink + edge.unlinkSource(); + }); splicep->resetSources(); - for (const Driver& driver : drivers) { - if (!driver.m_vtxp) break; // Stop at end of compacted list - splicep->addDriver(driver.m_fileline, driver.m_idx, driver.m_vtxp); + + // Coalesce adjacent ranges + if (drivers.size() > 1) { + size_t mergeInto = 0; + size_t mergeFrom = 1; + do { + Driver& into = drivers[mergeInto]; + Driver& from = drivers[mergeFrom]; + const uint32_t intoWidth = into.m_vtxp->width(); + const uint32_t fromWidth = from.m_vtxp->width(); + + if (into.m_low + intoWidth == from.m_low) { + // Adjacent ranges, coalesce + const auto dtypep = DfgVertex::dtypeForWidth(intoWidth + fromWidth); + DfgConcat* const concatp = new DfgConcat{m_dfg, into.m_flp, dtypep}; + concatp->rhsp(into.m_vtxp); + concatp->lhsp(from.m_vtxp); + into.m_vtxp = concatp; + from.m_vtxp = nullptr; // Mark as moved + ++m_ctx.m_coalescedAssignments; + } else { + // There is a gap - future merges go into the next position + ++mergeInto; + // Move 'from' into the next position, unless it's already there + if (mergeFrom != mergeInto) { + Driver& next = drivers[mergeInto]; + UASSERT_OBJ(!next.m_vtxp, next.m_flp, "Should have been marked moved"); + next = from; + from.m_vtxp = nullptr; // Mark as moved + } + } + + // Consider next driver + ++mergeFrom; + } while (mergeFrom < drivers.size()); + // Rightsize vector + drivers.erase(drivers.begin() + (mergeInto + 1), drivers.end()); } - removeUnused(prune); - - // If the whole variable is driven whole, we can just use that driver - if (splicep->arity() == 1 // - && splicep->driverIndex(0) == 0 // - && splicep->source(0)->dtypep()->isSame(splicep->dtypep())) { - const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0)); - VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep); - return result; + // If the variable is driven whole, we can just use that driver + if (drivers.size() == 1 // + && drivers[0].m_low == 0 // + && drivers[0].m_vtxp->width() == splicep->width()) { + VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); + // Use the driver directly + return {drivers[0].m_vtxp, drivers[0].m_flp}; } + + // Reinsert drivers in order + for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); + // Use the original splice return {splicep, splicep->fileline()}; } - // VISITORS - void visit(AstNode* nodep) override { - // Conservatively treat this node as unhandled - if (!m_foundUnhandled && m_converting) ++m_ctx.m_nonRepUnknown; - m_foundUnhandled = true; - markReferenced(nodep); + // Coalesce array driver - return the coalesced vertex and location for 'splicep' + std::pair coalesceArray(DfgSpliceArray* const splicep) { + UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSpliceArray"); + + // The drivers of 'splicep' + std::vector drivers; + drivers.reserve(splicep->arity()); + + // Coalesce, gather and unlink all drivers + int64_t prevHigh = -1; // High index of previous driven range + splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { + DfgVertex* driverp = edge.sourcep(); + UASSERT_OBJ(driverp, splicep, "Should not have created undriven sources"); + const uint32_t low = splicep->driverIndex(i); + UASSERT_OBJ(static_cast(low) > prevHigh, splicep, + "Drivers should have been normalized"); + prevHigh = low; + FileLine* flp = splicep->driverFileLine(i); + // Coalesce + if (DfgSplicePacked* const spp = driverp->cast()) { + std::tie(driverp, flp) = coalescePacked(spp); + } else if (DfgSpliceArray* const sap = driverp->cast()) { + std::tie(driverp, flp) = coalesceArray(sap); + } else if (driverp->is()) { + driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE + } + // Gather + drivers.emplace_back(flp, low, driverp); + // Unlink + edge.unlinkSource(); + }); + splicep->resetSources(); + + // If the variable is driven whole, we can just use that driver + if (drivers.size() == 1 // + && drivers[0].m_low == 0 // + && drivers[0].m_vtxp->dtypep()->isSame(splicep->dtypep())) { + VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); + // Use the driver directly + return {drivers[0].m_vtxp, drivers[0].m_flp}; + } + + // Reinsert drivers in order + for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); + // Use the original splice + return {splicep, splicep->fileline()}; } + // CONSTRUCTOR + AstToDfgCoalesceDrivers(DfgGraph& dfg, DfgVertexVar& var, V3DfgAstToDfgContext& ctx) + : m_dfg{dfg} + , m_ctx{ctx} { + // Nothing to do for un-driven (input) variables + if (!var.srcp()) return; + + // The driver of a variable must always be a splice vertex, coalesce it + std::pair normalizedDriver; + if (DfgSpliceArray* const sArrayp = var.srcp()->cast()) { + normalizedDriver = coalesceArray(sArrayp); + } else if (DfgSplicePacked* const sPackedp = var.srcp()->cast()) { + normalizedDriver = coalescePacked(sPackedp); + } else { + var.v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE + } + var.srcp(normalizedDriver.first); + var.driverFileLine(normalizedDriver.second); + } + +public: + // Coalesce drivers of given variable + static void apply(DfgGraph& dfg, DfgVertexVar& var, V3DfgAstToDfgContext& ctx) { + AstToDfgCoalesceDrivers{dfg, var, ctx}; + } +}; + +// Visitor that converts a whole module (when T_Scoped is false), +// or the whole netlist (when T_Scoped is true). +template +class AstToDfgVisitor final : public VNVisitor { + // NODE STATE + const VNUser2InUse m_user2InUse; // Used by AstToDfgConverter + + // TYPES + using RootType = std::conditional_t; + using Variable = std::conditional_t; + + // STATE + AstToDfgConverter m_converter; // The convert instance to use for each construct + + // METHODS + static Variable* getTarget(const AstVarRef* refp) { + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + return reinterpret_cast(refp->varScopep()); + } else { + return reinterpret_cast(refp->varp()); + } + } + + // Mark variables referenced under node + static void markReferenced(AstNode* nodep) { + nodep->foreach([](const AstVarRef* refp) { + Variable* const tgtp = getTarget(refp); + // Mark as read from non-DFG logic + if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp); + // Mark as written from non-DFG logic + if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp); + }); + } + + // VISITORS + // Unhandled node + void visit(AstNode* nodep) override { markReferenced(nodep); } + + // Containers to descend through to find logic constructs void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); } void visit(AstModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); } void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); } @@ -707,208 +996,49 @@ class AstToDfgVisitor final : public VNVisitor { } } + // Non-representable constructs void visit(AstCell* nodep) override { markReferenced(nodep); } void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); } + // Potentially representable constructs void visit(AstAssignW* nodep) override { - ++m_ctx.m_inputEquations; - - // Cannot handle assignment with timing control yet - if (nodep->timingControlp()) { - markReferenced(nodep); - ++m_ctx.m_nonRepTiming; - return; - } - - convertEquation(nodep, nodep->fileline(), nodep->lhsp(), nodep->rhsp()); + if (!m_converter.convert(nodep)) markReferenced(nodep); } - void visit(AstAlways* nodep) override { - // Ignore sequential logic, or if there are multiple statements - const VAlwaysKwd kwd = nodep->keyword(); - if (nodep->sensesp() || !nodep->isJustOneBodyStmt() - || (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) { - markReferenced(nodep); - return; - } - - AstNode* const stmtp = nodep->stmtsp(); - - if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) { - ++m_ctx.m_inputEquations; - if (assignp->timingControlp()) { - markReferenced(stmtp); - ++m_ctx.m_nonRepTiming; - return; - } - convertEquation(nodep, assignp->fileline(), assignp->lhsp(), assignp->rhsp()); - } else if (AstIf* const ifp = VN_CAST(stmtp, If)) { - // Will only handle single assignments to the same LHS in both branches - AstAssign* const thenp = VN_CAST(ifp->thensp(), Assign); - AstAssign* const elsep = VN_CAST(ifp->elsesp(), Assign); - if (!thenp || !elsep || thenp->nextp() || elsep->nextp() - || !thenp->lhsp()->sameTree(elsep->lhsp())) { - markReferenced(stmtp); - return; - } - - ++m_ctx.m_inputEquations; - if (thenp->timingControlp() || elsep->timingControlp()) { - markReferenced(stmtp); - ++m_ctx.m_nonRepTiming; - return; - } - - // Create a conditional for the rhs by borrowing the components from the AstIf - AstCond* const rhsp = new AstCond{ifp->fileline(), // - ifp->condp()->unlinkFrBack(), // - thenp->rhsp()->unlinkFrBack(), // - elsep->rhsp()->unlinkFrBack()}; - - if (!convertEquation(nodep, ifp->fileline(), thenp->lhsp(), rhsp)) { - // Failed to convert. Mark 'rhsp', as 'convertEquation' only marks 'nodep'. - markReferenced(rhsp); - // Put the AstIf back together - ifp->condp(rhsp->condp()->unlinkFrBack()); - thenp->rhsp(rhsp->thenp()->unlinkFrBack()); - elsep->rhsp(rhsp->elsep()->unlinkFrBack()); - } - // Delete the auxiliary conditional - VL_DO_DANGLING(rhsp->deleteTree(), rhsp); - } else { - markReferenced(stmtp); - } - } - - void visit(AstVarRef* nodep) override { - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - - if (nodep->access().isRW() // Cannot represent read-write references - || nodep->varp()->isIfaceRef() // Cannot handle interface references - || nodep->varp()->delayp() // Cannot handle delayed variables - || nodep->classOrPackagep() // Cannot represent cross module references - || nodep->varp()->isSc() // SystemC variables are special and rare, we can ignore - ) { - markReferenced(nodep); - m_foundUnhandled = true; - ++m_ctx.m_nonRepVarRef; - return; - } - - // If the referenced variable is not in a regular module, then do not - // convert it. This is especially needed for variabels in interfaces - // which might be referenced via virtual intefaces, which cannot be - // resovled statically. - if (T_Scoped && !VN_IS(nodep->varScopep()->scopep()->modp(), Module)) { - markReferenced(nodep); - m_foundUnhandled = true; - ++m_ctx.m_nonRepVarRef; - return; - } - - // Sadly sometimes AstVarRef does not have the same dtype as the referenced variable - if (!DfgVertex::isSupportedDType(nodep->varp()->dtypep())) { - m_foundUnhandled = true; - ++m_ctx.m_nonRepVarRef; - return; - } - - nodep->user2p(getNet(getTarget(nodep))); - } - - void visit(AstConst* nodep) override { - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - DfgVertex* const vtxp = new DfgConst{*m_dfgp, nodep->fileline(), nodep->num()}; - m_uncommittedVertices.push_back(vtxp); - nodep->user2p(vtxp); - } - - void visit(AstSel* nodep) override { - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - - iterate(nodep->fromp()); - if (m_foundUnhandled) return; - - FileLine* const flp = nodep->fileline(); - DfgVertex* vtxp = nullptr; - if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { - DfgSel* const selp = new DfgSel{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; - selp->fromp(nodep->fromp()->user2u().to()); - selp->lsb(constp->toUInt()); - vtxp = selp; - } else { - iterate(nodep->lsbp()); - if (m_foundUnhandled) return; - DfgMux* const muxp = new DfgMux{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; - muxp->fromp(nodep->fromp()->user2u().to()); - muxp->lsbp(nodep->lsbp()->user2u().to()); - vtxp = muxp; - } - m_uncommittedVertices.push_back(vtxp); - nodep->user2p(vtxp); - } - -// The rest of the 'visit' methods are generated by 'astgen' -#include "V3Dfg__gen_ast_to_dfg.h" - - static DfgGraph* makeDfg(RootType& root) { - if VL_CONSTEXPR_CXX17 (T_Scoped) { - return new DfgGraph{nullptr, "netlist"}; - } else { - AstModule* const modp = VN_AS((AstNode*)&(root), Module); // Remove this when C++17 - return new DfgGraph{modp, modp->name()}; - } + if (!m_converter.convert(nodep)) markReferenced(nodep); } // CONSTRUCTOR - explicit AstToDfgVisitor(RootType& root, V3DfgAstToDfgContext& ctx) - : m_dfgp{makeDfg(root)} - , m_ctx{ctx} { - // Build the DFG + AstToDfgVisitor(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) + : m_converter{dfg, ctx} { iterate(&root); - UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain"); - - if (dumpDfgLevel() >= 8) m_dfgp->dumpDotFilePrefixed(ctx.prefix() + "ast2dfg"); - - // Normalize variable drivers (remove multiple drivers, remove unnecessary splice vertices) - for (DfgVertexVar* const varp : m_dfgp->varVertices().unlinkable()) { - // Delete variables with no sinks nor sources (this can happen due to reverting - // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && !varp->srcp()) { - VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); - continue; - } - - // Nothing to do for un-driven (input) variables - if (!varp->srcp()) continue; - - // The driver of a variable must always be a splice vertex, normalize it - std::pair normalizedDriver; - if (DfgSpliceArray* const sArrayp = varp->srcp()->cast()) { - normalizedDriver = normalizeArray(varp, "", sArrayp); - } else if (DfgSplicePacked* const sPackedp = varp->srcp()->cast()) { - normalizedDriver = normalizePacked(varp, "", sPackedp); - } else { - varp->v3fatalSrc("Unhandled DfgSplicePacked sub-type"); // LCOV_EXCL_LINE - } - varp->srcp(normalizedDriver.first); - varp->driverFileLine(normalizedDriver.second); - } } public: - static DfgGraph* apply(RootType& root, V3DfgAstToDfgContext& ctx) { - return AstToDfgVisitor{root, ctx}.m_dfgp; + static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) { + // Convert all logic under 'root' + AstToDfgVisitor{dfg, root, ctx}; + if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-conv"); + // Normalize and coalesce all variable drivers + for (DfgVertexVar& var : dfg.varVertices()) { + AstToDfgNormalizeDrivers::apply(dfg, var); + AstToDfgCoalesceDrivers::apply(dfg, var, ctx); + } + if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-norm"); + // Remove all unused vertices + V3DfgPasses::removeUnused(dfg); + if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-prun"); } }; -DfgGraph* V3DfgPasses::astToDfg(AstModule& module, V3DfgContext& ctx) { - return AstToDfgVisitor::apply(module, ctx.m_ast2DfgContext); +std::unique_ptr V3DfgPasses::astToDfg(AstModule& module, V3DfgContext& ctx) { + DfgGraph* const dfgp = new DfgGraph{&module, module.name()}; + AstToDfgVisitor::apply(*dfgp, module, ctx.m_ast2DfgContext); + return std::unique_ptr{dfgp}; } -DfgGraph* V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgContext& ctx) { - return AstToDfgVisitor::apply(netlist, ctx.m_ast2DfgContext); +std::unique_ptr V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgContext& ctx) { + DfgGraph* const dfgp = new DfgGraph{nullptr, "netlist"}; + AstToDfgVisitor::apply(*dfgp, netlist, ctx.m_ast2DfgContext); + return std::unique_ptr{dfgp}; } diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index 27e406b6b..87487ad56 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -171,7 +171,7 @@ public: private: V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label) - : V3DfgSubContext{ctx, label, "Dfg2Ast"} {} + : V3DfgSubContext{ctx, label, "DfgToAst"} {} ~V3DfgDfgToAstContext() { addStat("result equations", m_resultEquations); } }; class V3DfgEliminateVarsContext final : public V3DfgSubContext { diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 327dd4787..de7695bfd 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -262,7 +262,7 @@ class DataflowOptimize final { // Attempt to convert cyclic components into acyclic ones std::vector> madeAcyclicComponents; - if (v3Global.opt.fDfgBreakCyckes()) { + if (v3Global.opt.fDfgBreakCycles()) { for (auto it = cyclicComponents.begin(); it != cyclicComponents.end();) { auto result = V3DfgPasses::breakCycles(**it, m_ctx); if (!result.first) { @@ -335,7 +335,7 @@ class DataflowOptimize final { UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'"); ++m_ctx.m_modules; // Build the DFG of this module or netlist - const std::unique_ptr dfgp{V3DfgPasses::astToDfg(*modp, m_ctx)}; + const std::unique_ptr dfgp = V3DfgPasses::astToDfg(*modp, m_ctx); // Actually process the graph optimize(*dfgp); // Convert back to Ast @@ -345,7 +345,7 @@ class DataflowOptimize final { // Post V3Scope application. Run on whole netlist. UINFO(4, "Applying DFG optimization to entire netlist"); // Build the DFG of the entire netlist - const std::unique_ptr dfgp{V3DfgPasses::astToDfg(*netlistp, m_ctx)}; + const std::unique_ptr dfgp = V3DfgPasses::astToDfg(*netlistp, m_ctx); // Actually process the graph optimize(*dfgp); // Convert back to Ast diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 0e6e6c9ac..a6aad4c37 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -139,6 +139,11 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { vtxp->unlinkDelete(dfg); } + // Remove unused and undriven variable vertices + for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) { + if (!vtxp->hasSinks() && !vtxp->srcp()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); + } + // Finally remove unused constants for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) { if (!vtxp->hasSinks()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 21311ae8f..98ce5e196 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -33,10 +33,10 @@ namespace V3DfgPasses { // Construct a DfGGraph representing the combinational logic in the given AstModule. The logic // that is represented by the graph is removed from the given AstModule. Returns the // constructed DfgGraph. -DfgGraph* astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED; +std::unique_ptr astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED; // Same as above, but for the entire netlist, after V3Scope -DfgGraph* astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED; +std::unique_ptr astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED; // Attempt to make the given cyclic graph into an acyclic, or "less cyclic" // equivalent. If the returned pointer is null, then no improvement was diff --git a/src/V3Options.h b/src/V3Options.h index e77cc1cad..39f8e9a4b 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -736,7 +736,7 @@ public: bool fConstBitOpTree() const { return m_fConstBitOpTree; } bool fConstEager() const { return m_fConstEager; } bool fDedupe() const { return m_fDedupe; } - bool fDfgBreakCyckes() const { return m_fDfgBreakCycles; } + bool fDfgBreakCycles() const { return m_fDfgBreakCycles; } bool fDfgPeephole() const { return m_fDfgPeephole; } bool fDfgPreInline() const { return m_fDfgPreInline; } bool fDfgPostInline() const { return m_fDfgPostInline; } diff --git a/src/astgen b/src/astgen index a6fd71e1c..8d64b12c7 100755 --- a/src/astgen +++ b/src/astgen @@ -1250,6 +1250,9 @@ def write_dfg_ast_to_dfg(filename): continue fh.write("void visit(Ast{t}* nodep) override {{\n".format(t=node.name)) + fh.write( + ' UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");\n' + ) fh.write(' UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");\n\n') fh.write(" if (unhandled(nodep)) return;\n\n") for i in range(node.arity): @@ -1259,8 +1262,8 @@ def write_dfg_ast_to_dfg(filename): ' UASSERT_OBJ(nodep->op{j}p()->user2p(), nodep, "Child {j} missing Dfg vertex");\n' .format(j=i + 1)) fh.write("\n") - fh.write(" Dfg{t}* const vtxp = makeVertex(nodep, *m_dfgp);\n".format( - t=node.name)) + fh.write( + " Dfg{t}* const vtxp = makeVertex(nodep, m_dfg);\n".format(t=node.name)) fh.write(" if (!vtxp) {\n") fh.write(" m_foundUnhandled = true;\n") fh.write(" ++m_ctx.m_nonRepNode;\n") @@ -1271,7 +1274,6 @@ def write_dfg_ast_to_dfg(filename): " vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to());\n". format(i=i, j=i + 1)) fh.write("\n") - fh.write(" m_uncommittedVertices.push_back(vtxp);\n") fh.write(" nodep->user2p(vtxp);\n") fh.write("}\n") diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.out b/test_regress/t/t_dfg_multidriver_dfg_bad.out index 9020d13ac..07a2fa37e 100644 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.out +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.out @@ -50,15 +50,15 @@ t/t_dfg_multidriver_dfg_bad.v:31:14: ... Location of first driver 31 | assign v = j; | ^ - t/t_dfg_multidriver_dfg_bad.v:32:17: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:32:13: ... Location of other driver 32 | assign v[1] = i; - | ^ + | ^ t/t_dfg_multidriver_dfg_bad.v:30:18: ... Only the first driver will be respected %Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:18: Elements [0:0] of signal 'w' have multiple combinational drivers : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:35:17: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:35:13: ... Location of first driver 35 | assign w[0] = i; - | ^ + | ^ t/t_dfg_multidriver_dfg_bad.v:36:14: ... Location of other driver 36 | assign w = j; | ^ From 1d05028087d6fc9053785d1f7b7dd4f9f1feedd5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 17:31:15 -0400 Subject: [PATCH 154/252] Add compiler warning disable '-Wno-int-in-bool-context' --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index c72b048e8..5efee99b2 100644 --- a/configure.ac +++ b/configure.ac @@ -477,6 +477,7 @@ m4_foreach([cflag],[ [-Wno-bool-operation], [-Wno-c++11-narrowing], [-Wno-constant-logical-operand], + [-Wno-int-in-bool-context], [-Wno-non-pod-varargs], [-Wno-parentheses-equality], [-Wno-shadow], From 1eccfa64b5c6dba7a74a3c23922535f827fceb87 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 17:31:54 -0400 Subject: [PATCH 155/252] Commentary --- .github/workflows/reusable-build.yml | 1 - .github/workflows/reusable-rtlmeter-build.yml | 1 - .github/workflows/reusable-rtlmeter-run.yml | 1 - .github/workflows/reusable-test.yml | 3 +-- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index e2f30147e..0a3b57d2a 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -1,6 +1,5 @@ --- # DESCRIPTION: Github actions config -# This name is key to badges in README.rst, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 name: reusable-build diff --git a/.github/workflows/reusable-rtlmeter-build.yml b/.github/workflows/reusable-rtlmeter-build.yml index 65aabc8ca..0ed682d32 100644 --- a/.github/workflows/reusable-rtlmeter-build.yml +++ b/.github/workflows/reusable-rtlmeter-build.yml @@ -1,6 +1,5 @@ --- # DESCRIPTION: Github actions config -# This name is key to badges in README.rst, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 name: reusable-rtlmeter-build diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index c61044e03..ba8d45199 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -1,6 +1,5 @@ --- # DESCRIPTION: Github actions config -# This name is key to badges in README.rst, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 name: reusable-rtlmeter-run diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml index 5bc3fd650..f3b07c902 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test.yml @@ -1,9 +1,8 @@ --- # DESCRIPTION: Github actions config -# This name is key to badges in README.rst, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -name: reusable-build +name: reusable-test on: workflow_call: From 3ca1c9b6dd5fe5c77e101af05f517f342cbcaf3e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 8 Aug 2025 18:21:12 -0400 Subject: [PATCH 156/252] Internals: Fix and enforce brace new constructors. No functional change intended. --- include/verilated_vpi.cpp | 2 +- src/V3Const.cpp | 2 +- src/V3Descope.cpp | 6 +++--- src/V3HierBlock.cpp | 4 ++-- src/V3Inst.cpp | 2 +- src/V3ParseImp.h | 2 +- src/V3Randomize.cpp | 4 ++-- test_regress/t/t_dist_cppstyle.py | 24 +++++++++++++++++------- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index a20954308..144587e91 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -310,7 +310,7 @@ public: delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - VerilatedRange* const rangep = new VerilatedRange(*m_iter); + VerilatedRange* const rangep = new VerilatedRange{*m_iter}; ++m_iter; return ((new VerilatedVpioRange{rangep})->castVpiHandle()); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 2afc17493..ec73e7694 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2116,7 +2116,7 @@ class ConstVisitor final : public VNVisitor { = new AstVarRef{rhsp->fileline(), tempPurep, VAccess::WRITE}; AstNodeAssign* const asnp = VN_IS(nodep, AssignDly) - ? new AstAssign(nodep->fileline(), tempPureRefp, rhsp) + ? new AstAssign{nodep->fileline(), tempPureRefp, rhsp} : nodep->cloneType(tempPureRefp, rhsp); nodep->addHereThisAsNext(asnp); nodep->rhsp(new AstVarRef{rhsp->fileline(), tempPurep, VAccess::READ}); diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 8d670b967..9a9fa9e5b 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -190,11 +190,11 @@ class DescopeVisitor final : public VNVisitor { } // Not really any way the user could do this, and we'd need // to come up with some return value - // newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(), + // newfuncp->addStmtsp(new AstDisplay{newfuncp->fileline(), // VDisplayType::DT_WARNING, // "%%Error: "s+name+"() called with bad - // scope", nullptr)); - // newfuncp->addStmtsp(new AstStop(newfuncp->fileline())); + // scope", nullptr}); + // newfuncp->addStmtsp(new AstStop{newfuncp->fileline()}); UINFOTREE(9, newfuncp, "", "newfunc"); } else { // Only a single function under this name, we can rename it diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index 77ba1992f..d97ebf035 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -292,8 +292,8 @@ void V3HierBlock::writeParametersFile() const { *of << "module " << moduleName << ";\n"; for (AstParamTypeDType* const gparam : m_params.gTypeParams()) { AstTypedef* tdefp - = new AstTypedef(new FileLine{FileLine::builtInFilename()}, gparam->name(), nullptr, - VFlagChildDType{}, gparam->skipRefp()->cloneTreePure(true)); + = new AstTypedef{new FileLine{FileLine::builtInFilename()}, gparam->name(), nullptr, + VFlagChildDType{}, gparam->skipRefp()->cloneTreePure(true)}; V3EmitV::verilogForTree(tdefp, *of); VL_DO_DANGLING(tdefp->deleteTree(), tdefp); } diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index e53d9f8c6..08c2af8c6 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -550,7 +550,7 @@ private: VAccess::READ}; rhsp->dtypep(rhsarrp->subDTypep()->skipRefp()); rhsp->classOrPackagep(rhsrefp->classOrPackagep()); - AstAssign* const assignp = new AstAssign(nodep->fileline(), lhsp, rhsp); + AstAssign* const assignp = new AstAssign{nodep->fileline(), lhsp, rhsp}; nodep->addNextHere(assignp); } VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index fe6c100c1..617344d12 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -238,7 +238,7 @@ public: return strp; } string* newString(const char* text, size_t length) { - string* const strp = new string(text, length); + string* const strp = new string(text, length); // Need () constructor m_stringps.push_back(strp); return strp; } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 26fb1932d..2d0cdb1c7 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1205,7 +1205,7 @@ class CaptureVisitor final : public VNVisitor { thisRefp->user1(true); m_ignore.emplace(thisRefp); AstMemberSel* const memberSelp - = new AstMemberSel(nodep->fileline(), thisRefp, nodep->varp()); + = new AstMemberSel{nodep->fileline(), thisRefp, nodep->varp()}; memberSelp->user2p(m_targetp); nodep->replaceWith(memberSelp); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -1259,7 +1259,7 @@ class CaptureVisitor final : public VNVisitor { return; } AstVarRef* const varRefp - = new AstVarRef(nodep->fileline(), nodep->varp(), nodep->access()); + = new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()}; fixupClassOrPackage(nodep->varp(), varRefp); varRefp->user1(nodep->user1()); nodep->replaceWith(varRefp); diff --git a/test_regress/t/t_dist_cppstyle.py b/test_regress/t/t_dist_cppstyle.py index 40815115d..a21c25eb6 100755 --- a/test_regress/t/t_dist_cppstyle.py +++ b/test_regress/t/t_dist_cppstyle.py @@ -27,6 +27,7 @@ def get_source_files(root): def check_pattern(filename, contents, pattern, not_pattern, message): + # Pattern uses match, so must include skipping leading whitespace if necessary lineno = 0 buffer = "\n" for line in contents.splitlines(): @@ -36,9 +37,10 @@ def check_pattern(filename, contents, pattern, not_pattern, message): # Build a buffer until a newline so we check a block at a time. buffer += line + "\n" continue - if re.search(r"\n" + pattern, buffer): + m = re.search(r"\n" + pattern, buffer) + if m: if not not_pattern or not re.search(not_pattern, buffer): - test.error(filename + ":" + str(lineno) + ": " + message) + test.error_keep_going(filename + ":" + str(lineno) + ": " + message + m.group(0)) buffer = "\n" @@ -58,20 +60,28 @@ for filename in sorted(files.keys()): contents = test.file_contents(filename) + "\n\n" - check_pattern(filename, contents, r"[^\']*virtual[^{};\n]+override", None, + check_pattern(filename, contents, r"[^\']*virtual[^{};\n]+override[^\n]*", None, "'virtual' keyword is redundant on 'override' method") check_pattern(filename, contents, - r' \s*(\w+ )*\s*(inline) [^;]+?\([^;]*?\)[^;]+?(?:{|:|=\s*default)', None, - "'inline' keyword is redundant on method definitions inside classes") + r' \s*(\w+ )*\s*(inline) [^;]+?\([^;]*?\)[^;]+?(?:{|:|=\s*default[^\n]*)', + None, "'inline' keyword is redundant on method definitions inside classes") check_pattern( - filename, contents, r'inline \S+ [^;:(]+::[^;:(]+\([^;]*\)[^;]+{', r'template', + filename, contents, r'\s*inline \S+ [^;:(]+::[^;:(]+\([^;]*\)[^;]+{[^\n]*', r'template', "Use 'inline' only on declaration inside classes" " (except for template specializations)") + check_pattern( + filename, + contents, + r'.*[( ]new [a-zA-Z0-9]+\([^\n]*', + # Ignore common ok narrowing conversions, on int vs uint32_t arguments + r'(Need \(\) constructor|new AstArraySel|new AstConst|new AstRange)', + "Use brace instead of parenthesis-style constructors e.g. 'new ...{...}'") + if re.search(r'\.(c|cpp)', filename): - check_pattern(filename, contents, r'(\w+\s+)*(inline)', None, + check_pattern(filename, contents, r'(\w+\s+)*(inline)[^\n]*', None, "'inline' keyword is on functions defined in .cpp files") test.passes() From d28436dccc80b8e60f940c63e30670a02061cf92 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 9 Aug 2025 14:59:58 +0100 Subject: [PATCH 157/252] Fix stray ']' in Verilog code output for non-constant select (#6277) --- src/V3EmitV.cpp | 1 - test_regress/t/t_cover_expr.out | 18 +++++++++--------- test_regress/t/t_cover_expr_max.out | 18 +++++++++--------- test_regress/t/t_cover_expr_trace.out | 18 +++++++++--------- test_regress/t/t_cover_line_expr.out | 18 +++++++++--------- test_regress/t/t_debug_emitv.out | 2 +- 6 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 81050ab49..539bd39cd 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -668,7 +668,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } putfs(nodep, "+:"); puts(cvtToStr(nodep->widthConst())); - puts("]"); } puts("]"); } diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out index 2886f1501..914ec8071 100644 --- a/test_regress/t/t_cover_expr.out +++ b/test_regress/t/t_cover_expr.out @@ -206,15 +206,15 @@ if (0 == 1) begin for (int loop_var = 0; loop_var < 1; loop_var++) begin %000000 if (cyc[loop_var] && t2) $write(""); --000000 point: comment=(cyc[loop_var[4:0]+:1]]==0) => 0 hier=top.t --000000 point: comment=(cyc[loop_var[4:0]+:1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==1 && t2==1) => 1 hier=top.t -000000 point: comment=(t2==0) => 0 hier=top.t end end // stop at the first layer even if there's more to find %000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); --000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==0 && cyc[31]==0) => 0 hier=top.t --000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==1) => 1 hier=top.t +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==1) => 1 hier=top.t -000000 point: comment=(cyc[31]==1) => 1 hier=top.t // impossible branches and redundant terms %000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); @@ -233,12 +233,12 @@ -000005 point: comment=(cyc[1]==0) => 0 hier=top.t // demonstrate current limitations of term matching scheme %000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); --000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==1 && cyc[2]==1) => 0 hier=top.t --000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]==0 && cyc[3]==0) => 0 hier=top.t -000004 point: comment=(cyc[0]==0) => 0 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[3]==1) => 1 hier=top.t --000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t -000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t -000005 point: comment=(cyc[1]==0) => 0 hier=top.t //verilator coverage_off diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out index ee7dca17a..47706565e 100644 --- a/test_regress/t/t_cover_expr_max.out +++ b/test_regress/t/t_cover_expr_max.out @@ -206,15 +206,15 @@ if (0 == 1) begin for (int loop_var = 0; loop_var < 1; loop_var++) begin %000000 if (cyc[loop_var] && t2) $write(""); --000000 point: comment=(cyc[loop_var[4:0]+:1]]==0) => 0 hier=top.t --000000 point: comment=(cyc[loop_var[4:0]+:1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==1 && t2==1) => 1 hier=top.t -000000 point: comment=(t2==0) => 0 hier=top.t end end // stop at the first layer even if there's more to find %000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); --000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==0 && cyc[31]==0) => 0 hier=top.t --000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==1) => 1 hier=top.t +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==1) => 1 hier=top.t -000000 point: comment=(cyc[31]==1) => 1 hier=top.t // impossible branches and redundant terms %000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); @@ -233,12 +233,12 @@ -000005 point: comment=(cyc[1]==0) => 0 hier=top.t // demonstrate current limitations of term matching scheme %000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); --000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==1 && cyc[2]==1) => 0 hier=top.t --000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]==0 && cyc[3]==0) => 0 hier=top.t -000004 point: comment=(cyc[0]==0) => 0 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[3]==1) => 1 hier=top.t --000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t -000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t -000005 point: comment=(cyc[1]==0) => 0 hier=top.t //verilator coverage_off diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out index 2886f1501..914ec8071 100644 --- a/test_regress/t/t_cover_expr_trace.out +++ b/test_regress/t/t_cover_expr_trace.out @@ -206,15 +206,15 @@ if (0 == 1) begin for (int loop_var = 0; loop_var < 1; loop_var++) begin %000000 if (cyc[loop_var] && t2) $write(""); --000000 point: comment=(cyc[loop_var[4:0]+:1]]==0) => 0 hier=top.t --000000 point: comment=(cyc[loop_var[4:0]+:1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==1 && t2==1) => 1 hier=top.t -000000 point: comment=(t2==0) => 0 hier=top.t end end // stop at the first layer even if there's more to find %000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); --000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==0 && cyc[31]==0) => 0 hier=top.t --000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==1) => 1 hier=top.t +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==1) => 1 hier=top.t -000000 point: comment=(cyc[31]==1) => 1 hier=top.t // impossible branches and redundant terms %000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); @@ -233,12 +233,12 @@ -000005 point: comment=(cyc[1]==0) => 0 hier=top.t // demonstrate current limitations of term matching scheme %000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); --000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==1 && cyc[2]==1) => 0 hier=top.t --000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]==0 && cyc[3]==0) => 0 hier=top.t -000004 point: comment=(cyc[0]==0) => 0 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[3]==1) => 1 hier=top.t --000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t -000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t -000005 point: comment=(cyc[1]==0) => 0 hier=top.t //verilator coverage_off diff --git a/test_regress/t/t_cover_line_expr.out b/test_regress/t/t_cover_line_expr.out index e6a1ec2b0..4f07df409 100644 --- a/test_regress/t/t_cover_line_expr.out +++ b/test_regress/t/t_cover_line_expr.out @@ -282,16 +282,16 @@ %000000 if (cyc[loop_var] && t2) $write(""); -000000 point: comment=if hier=top.t -000000 point: comment=else hier=top.t --000000 point: comment=(cyc[loop_var[4:0]+:1]]==0) => 0 hier=top.t --000000 point: comment=(cyc[loop_var[4:0]+:1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:1]==1 && t2==1) => 1 hier=top.t -000000 point: comment=(t2==0) => 0 hier=top.t end end // stop at the first layer even if there's more to find %000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); -000002 point: comment=else hier=top.t --000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==0 && cyc[31]==0) => 0 hier=top.t --000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2]])==1) => 1 hier=top.t +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:2] == cyc[(32'sh5 + (t3 || t4))[4:0]+:2])==1) => 1 hier=top.t -000000 point: comment=(cyc[31]==1) => 1 hier=top.t -000007 point: comment=if hier=top.t // impossible branches and redundant terms @@ -316,12 +316,12 @@ // demonstrate current limitations of term matching scheme %000008 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); -000008 point: comment=else hier=top.t --000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==1 && cyc[2]==1) => 0 hier=top.t --000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:1]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:1]==0 && cyc[3]==0) => 0 hier=top.t -000004 point: comment=(cyc[0]==0) => 0 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t --000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]]==0 && cyc[3]==1) => 1 hier=top.t --000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:1]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:1]==1) => 1 hier=top.t -000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t -000005 point: comment=(cyc[1]==0) => 0 hier=top.t -000001 point: comment=if hier=top.t diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 03c910a7f..0ae5fa7c3 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -59,7 +59,7 @@ module Vt_debug_emitv_t; $write(""); end if (downto_32[(ident('sh21) - 'h20)[5:0] + 32 - +:1]]) begin + +:1]) begin $write(""); end if ((| downto_32[48:40])) begin From a74a3bb68922cca7de44c1baf3771f86fa784518 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 10 Aug 2025 06:13:18 -0400 Subject: [PATCH 158/252] Internals: No need for 'if' before VL_FFLUSH_I --- src/V3EmitCFunc.h | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index b17552635..2cadbbcf5 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -482,8 +482,8 @@ public: emitIQW(selp->fromp()); emitIQW(nodep->rhsp()); puts("("); - putns(selp->fromp(), cvtToStr(selp->fromp()->widthMin()) + ","); - puts(cvtToStr(nodep->widthMin()) + ","); + putns(selp->fromp(), cvtToStr(selp->fromp()->widthMin()) + ", "); + puts(cvtToStr(nodep->widthMin()) + ", "); iterateAndNextConstNull(selp->lsbp()); puts(", "); iterateAndNextConstNull(selp->fromp()); @@ -502,7 +502,7 @@ public: emitScIQW(varp); emitIQW(nodep); puts("("); - puts(cvtToStr(nodep->widthMin()) + ","); + puts(cvtToStr(nodep->widthMin()) + ", "); iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else if (AstVar* const varp = AstVar::scVarRecurse(nodep->rhsp())) { @@ -510,7 +510,7 @@ public: emitIQW(nodep); emitScIQW(varp); puts("("); - puts(cvtToStr(nodep->widthMin()) + ","); + puts(cvtToStr(nodep->widthMin()) + ", "); iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else if (const AstCvtPackedToArray* const castp @@ -561,7 +561,7 @@ public: paren = false; } else if (nodep->isWide() && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { putnbs(nodep, "VL_ASSIGN_W("); - puts(cvtToStr(nodep->widthMin()) + ","); + puts(cvtToStr(nodep->widthMin()) + ", "); iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else { @@ -671,7 +671,7 @@ public: putnbs(nodep, "[&]("); if (auto* const argrefp = nodep->indexArgRefp()) { putnbs(argrefp, argrefp->dtypep()->cType(argrefp->nameProtect(), false, false)); - puts(","); + puts(", "); } if (auto* const argrefp = nodep->valueArgRefp()) { putnbs(argrefp, argrefp->dtypep()->cType(argrefp->nameProtect(), false, false)); @@ -974,20 +974,17 @@ public: if (!nodep->filep()) { putns(nodep, "Verilated::runFlushCallbacks();\n"); } else { - putns(nodep, "if ("); - iterateAndNextConstNull(nodep->filep()); - puts(") { "); putns(nodep, "VL_FFLUSH_I("); iterateAndNextConstNull(nodep->filep()); - puts("); }\n"); + puts(");\n"); } } void visit(AstFSeek* nodep) override { putns(nodep, "(VL_FSEEK_I("); iterateAndNextConstNull(nodep->filep()); - puts(","); + puts(", "); iterateAndNextConstNull(nodep->offset()); - puts(","); + puts(", "); iterateAndNextConstNull(nodep->operation()); puts(") == -1 ? -1 : 0)"); } @@ -1004,7 +1001,7 @@ public: void visit(AstFRead* nodep) override { putns(nodep, "VL_FREAD_I("); puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width - putbs(","); + putbs(", "); uint32_t array_lo = 0; uint32_t array_size = 0; { @@ -1022,7 +1019,7 @@ public: } } puts(cvtToStr(array_lo)); - putbs(","); + putbs(", "); puts(cvtToStr(array_size)); putbs(", "); puts("&("); @@ -1406,7 +1403,7 @@ public: emitIQW(nodep); puts("OI("); if (nodep->srcp()) puts(cvtToStr(nodep->srcp()->widthMin())); - puts(","); + puts(", "); iterateAndNextConstNull(nodep->srcp()); puts(", "); iterateAndNextConstNull(nodep->countp()); From 641dd756c001f1993d395e1e1124720367d650ea Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 10 Aug 2025 07:23:28 -0400 Subject: [PATCH 159/252] Add check for mis-assignment of dynamic/automatics per IEEE --- src/V3AstNodeDType.h | 4 ++ src/V3WidthCommit.cpp | 43 ++++++++++++++++ test_regress/t/t_assign_automatic_bad.out | 26 ++++++++++ test_regress/t/t_assign_automatic_bad.py | 16 ++++++ test_regress/t/t_assign_automatic_bad.v | 51 +++++++++++++++++++ test_regress/t/t_assigndly_dynamic.v | 12 ++--- .../t/t_assigndly_dynamic_notiming_bad.v | 2 +- test_regress/t/t_enum_type_pins.v | 4 +- test_regress/t/t_event_control_expr.v | 2 +- test_regress/t/t_export_packed_struct2.cpp | 2 +- test_regress/t/t_export_packed_struct2.v | 25 ++++----- test_regress/t/t_interface_array4.v | 2 +- test_regress/t/t_savable.v | 9 ++-- test_regress/t/t_sys_readmem_assoc.v | 8 +-- ..._interface_member_trigger_realistic_case.v | 2 +- 15 files changed, 168 insertions(+), 40 deletions(-) create mode 100644 test_regress/t/t_assign_automatic_bad.out create mode 100755 test_regress/t/t_assign_automatic_bad.py create mode 100644 test_regress/t/t_assign_automatic_bad.v diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 74cc9d005..53c28d404 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -167,6 +167,7 @@ public: string cType(const string& name, bool forFunc, bool isRef, bool packed = false) const; // Represents a C++ LiteralType? (can be constexpr) bool isLiteralType() const VL_MT_STABLE; + virtual bool isDynamicallySized() const { return false; } private: class CTypeRecursed; @@ -383,6 +384,7 @@ public: int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } bool isCompound() const override { return true; } + bool isDynamicallySized() const override { return true; } }; class AstBasicDType final : public AstNodeDType { // Builtin atomic/vectored data type @@ -756,6 +758,7 @@ public: int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } bool isCompound() const override { return true; } + bool isDynamicallySized() const override { return true; } }; class AstEmptyQueueDType final : public AstNodeDType { // For EmptyQueue @@ -1124,6 +1127,7 @@ public: int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } bool isCompound() const override { return true; } + bool isDynamicallySized() const override { return true; } }; class AstRefDType final : public AstNodeDType { // @astgen op1 := typeofp : Optional[AstNode] diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 0f1c841ba..f94eb0788 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -42,6 +42,7 @@ class WidthCommitVisitor final : public VNVisitor { // STATE AstNodeModule* m_modp = nullptr; + std::string m_contNba; // In continuous- or non-blocking assignment VMemberMap m_memberMap; // Member names cached for fast lookup public: @@ -124,6 +125,26 @@ private: } } } + void varLifetimeCheck(AstNode* nodep, AstVar* varp) { + if (!m_contNba.empty()) { + std::string varType; + const AstNodeDType* const varDtp = varp->dtypep()->skipRefp(); + if (varp->lifetime().isAutomatic() && !VN_IS(varDtp, IfaceRefDType) + && !(varp->isFuncLocal() && varp->isIO())) + varType = "Automatic lifetime"; + else if (varp->isClassMember() && !varp->lifetime().isStatic() + && !VN_IS(varDtp, IfaceRefDType)) + varType = "Class non-static"; + else if (varDtp->isDynamicallySized()) + varType = "Dynamically-sized"; + if (!varType.empty()) { + UINFO(1, " Related var dtype: " << varDtp); + nodep->v3error(varType + << " variable not allowed in " << m_contNba + << " assignment (IEEE 1800-2023 6.21): " << varp->prettyNameQ()); + } + } + } // VISITORS void visit(AstNodeModule* nodep) override { @@ -278,6 +299,27 @@ private: iterateChildren(nodep); editDType(nodep); classEncapCheck(nodep, nodep->varp(), VN_CAST(nodep->classOrPackagep(), Class)); + if (nodep->access().isWriteOrRW()) varLifetimeCheck(nodep, nodep->varp()); + } + void visit(AstAssignDly* nodep) override { + iterateAndNextNull(nodep->timingControlp()); + iterateAndNextNull(nodep->rhsp()); + { + VL_RESTORER(m_contNba); + m_contNba = "nonblocking"; + iterateAndNextNull(nodep->lhsp()); + } + editDType(nodep); + } + void visit(AstAssignW* nodep) override { + iterateAndNextNull(nodep->timingControlp()); + iterateAndNextNull(nodep->rhsp()); + { + VL_RESTORER(m_contNba); + m_contNba = "continuous"; + iterateAndNextNull(nodep->lhsp()); + } + editDType(nodep); } void visit(AstNodeFTaskRef* nodep) override { iterateChildren(nodep); @@ -290,6 +332,7 @@ private: if (auto* const classrefp = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType)) { classEncapCheck(nodep, nodep->varp(), classrefp->classp()); } // else might be struct, etc + varLifetimeCheck(nodep, nodep->varp()); } void visit(AstVar* nodep) override { iterateChildren(nodep); diff --git a/test_regress/t/t_assign_automatic_bad.out b/test_regress/t/t_assign_automatic_bad.out new file mode 100644 index 000000000..6fb3bcec9 --- /dev/null +++ b/test_regress/t/t_assign_automatic_bad.out @@ -0,0 +1,26 @@ +%Error: t/t_assign_automatic_bad.v:31:10: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_auto3' + : ... note: In instance 't' + 31 | assign bad_auto3 = 2; + | ^~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_assign_automatic_bad.v:32:10: Dynamically-sized variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_dyn5' + : ... note: In instance 't' + 32 | assign bad_dyn5 = empty_dyn; + | ^~~~~~~~ +%Error: t/t_assign_automatic_bad.v:33:12: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'm_bad1' + : ... note: In instance 't' + 33 | assign c.m_bad1 = 2; + | ^~~~~~ +%Error: t/t_assign_automatic_bad.v:43:5: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_auto4' + : ... note: In instance 't' + 43 | bad_auto4 <= 2; + | ^~~~~~~~~ +%Error: t/t_assign_automatic_bad.v:44:5: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_dyn6' + : ... note: In instance 't' + 44 | bad_dyn6 <= empty_dyn; + | ^~~~~~~~ +%Error: t/t_assign_automatic_bad.v:46:7: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'm_bad2' + : ... note: In instance 't' + 46 | c.m_bad2 <= 2; + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_assign_automatic_bad.py b/test_regress/t/t_assign_automatic_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_assign_automatic_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_assign_automatic_bad.v b/test_regress/t/t_assign_automatic_bad.v new file mode 100644 index 000000000..98905ca97 --- /dev/null +++ b/test_regress/t/t_assign_automatic_bad.v @@ -0,0 +1,51 @@ +// 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 + +// 6.21 Scope and lifetime +// Automatic variables and elements of dynamically sized array variables shall +// not be written with nonblocking, continuous, or procedural continuous +// assignments. Non-static class properties shall not be written with continuous +// or procedural continuous assignments. + +class Cls; + static int s_ok1; + static int s_ok2; + int m_bad1; + int m_bad2; +endclass + +module t(clk); + input clk; + + Cls c; + + automatic int bad_auto3; + automatic int bad_auto4; + int bad_dyn5[]; + int bad_dyn6[]; + int empty_dyn[]; + + assign bad_auto3 = 2; // <--- Error: continuous automatic + assign bad_dyn5 = empty_dyn; // <--- Error: continuous dynarray + assign c.m_bad1 = 2; // <--- Error: continuous class non-static + // Only one simulator fails on this, probably not legal + // assign Cls::s_ok1 = 2; // OK: continuous class static + + logic ok_7; + task mt(output o); // OK: function output + o <= 1; + endtask + + always @(posedge clk) begin + bad_auto4 <= 2; // <--- Error: nonblocking automatic + bad_dyn6 <= empty_dyn; // <--- Error: nonblocking dynarray + Cls::s_ok2 <= 2; // OK: nonblocking class static + c.m_bad2 <= 2; // <--- Error: nonblocking class automatic + mt(ok_7); + $stop; + end + +endmodule diff --git a/test_regress/t/t_assigndly_dynamic.v b/test_regress/t/t_assigndly_dynamic.v index 7a403fa9b..391174165 100644 --- a/test_regress/t/t_assigndly_dynamic.v +++ b/test_regress/t/t_assigndly_dynamic.v @@ -17,7 +17,7 @@ class nba_waiter; // Task taken from UVM task wait_for_nba_region; - int nba; + static int nba; int next_nba; next_nba++; nba <= `DELAY next_nba; @@ -27,19 +27,14 @@ endclass class Foo; task bar(logic a, logic b); - int x; - int y; + static int x; + static int y; // bar's local vars and intravals could be overwritten by other locals if (a) x <= `DELAY 'hDEAD; if (b) y <= `DELAY 'hBEEF; #2 if (x != 'hDEAD) $stop; endtask - - task qux(); - int x[] = new[1]; - x[0] <= `DELAY 'hBEEF; // Segfault check - endtask endclass module t; @@ -61,7 +56,6 @@ module t; if (cnt != 4) $stop; if ($time != `TIME_AFTER_SECOND_WAIT) $stop; foo.bar(1, 1); - foo.qux(); #2 $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_assigndly_dynamic_notiming_bad.v b/test_regress/t/t_assigndly_dynamic_notiming_bad.v index b2f77420a..b439348c8 100644 --- a/test_regress/t/t_assigndly_dynamic_notiming_bad.v +++ b/test_regress/t/t_assigndly_dynamic_notiming_bad.v @@ -6,7 +6,7 @@ class Cls; task bar; - int qux; + static int qux; qux <= '1; endtask endclass diff --git a/test_regress/t/t_enum_type_pins.v b/test_regress/t/t_enum_type_pins.v index eaeb9d3d5..a7f2b899a 100644 --- a/test_regress/t/t_enum_type_pins.v +++ b/test_regress/t/t_enum_type_pins.v @@ -76,7 +76,7 @@ module test (/*AUTOARG*/ // Use the enumeration size to initialize a dynamic array t_pinid e; - int myarray1 [] = new [e.num]; + int myarray1 [] = new [e.num]; always @(posedge clk) begin @@ -87,7 +87,7 @@ module test (/*AUTOARG*/ e = e.first; forever begin - myarray1[e] <= e.prev; + myarray1[e] = e.prev; `ifdef TEST_VERBOSE $write ("myarray1[%d] (enum %s) = %d\n", e, e.name, myarray1[e]); diff --git a/test_regress/t/t_event_control_expr.v b/test_regress/t/t_event_control_expr.v index 74456d898..d544b419a 100644 --- a/test_regress/t/t_event_control_expr.v +++ b/test_regress/t/t_event_control_expr.v @@ -63,7 +63,7 @@ function int id(int x); return x; endfunction `define CLASS_TEST(name, expr) \ module t_``name(input int k); \ class Cls; \ - int k; \ + static int k; \ function int get_k(); return k; endfunction \ endclass \ Cls obj = new; \ diff --git a/test_regress/t/t_export_packed_struct2.cpp b/test_regress/t/t_export_packed_struct2.cpp index 6357902ef..c97e39392 100644 --- a/test_regress/t/t_export_packed_struct2.cpp +++ b/test_regress/t/t_export_packed_struct2.cpp @@ -92,7 +92,7 @@ int main(int argc, char** argv) { std::memset(reinterpret_cast(&tmp), 0xff, sizeof(tmp)); // `set` function should clear upper bits of `tmp.a` - tmp.set(adder->rootp->add__DOT__op2->__PVT__in); + tmp.set(adder->rootp->add__DOT__op2); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { diff --git a/test_regress/t/t_export_packed_struct2.v b/test_regress/t/t_export_packed_struct2.v index 885c23313..5a67b0842 100644 --- a/test_regress/t/t_export_packed_struct2.v +++ b/test_regress/t/t_export_packed_struct2.v @@ -6,10 +6,10 @@ // Packed struct in package package TEST_TYPES; - typedef union soft packed { - logic [64 : 0] a; - logic [2 : 0] b; - } sub_t; + typedef union soft packed { + logic [64 : 0] a; + logic [2 : 0] b; + } sub_t; typedef struct packed { struct packed { // Anonymous packed struct logic a; @@ -28,7 +28,6 @@ class cls_in; logic a; TEST_TYPES::sub_t [2:0][2:0][2:0] b; } in_t /*verilator public*/; - in_t in; endclass //cls module add ( @@ -36,31 +35,25 @@ module add ( //input cls_in op2, output TEST_TYPES::out_t out ); - cls_in op2 /*verilator public_flat*/; + cls_in::in_t op2 /*verilator public_flat*/; - initial begin - if(op2 != null) $stop; - op2 = new(); - if(!op2) $stop; - end - - assign op2.in.a = op1.anon.a; + assign op2.a = op1.anon.a; generate for (genvar i = 0; i < 3; ++i) begin for (genvar j = 0; j < 3; ++j) begin for (genvar k = 0; k < 3; ++k) begin - assign op2.in.b[i][j][k] = op1.b[i][j][k]; + assign op2.b[i][j][k] = op1.b[i][j][k]; end end end endgenerate - assign out.anon.a = op1.anon.a + op2.in.a; + assign out.anon.a = op1.anon.a + op2.a; generate for (genvar i = 0; i < 3; ++i) begin for (genvar j = 0; j < 3; ++j) begin for (genvar k = 0; k < 3; ++k) begin - assign out.b[i][j][k] = op1.b[i][j][k] + op2.in.b[i][j][k]; + assign out.b[i][j][k] = op1.b[i][j][k] + op2.b[i][j][k]; end end end diff --git a/test_regress/t/t_interface_array4.v b/test_regress/t/t_interface_array4.v index f16386c9b..f3042fa2d 100644 --- a/test_regress/t/t_interface_array4.v +++ b/test_regress/t/t_interface_array4.v @@ -29,7 +29,7 @@ endmodule module devB (Ifc s); endmodule -module top; +module t; Ifc s14[1:4] (); devA a1 (s14[1]); devB b1 (s14[2]); diff --git a/test_regress/t/t_savable.v b/test_regress/t/t_savable.v index 8b07a7814..e9acda757 100644 --- a/test_regress/t/t_savable.v +++ b/test_regress/t/t_savable.v @@ -69,12 +69,13 @@ module sub (/*AUTOARG*/ pvec[2][2] <= 32'h10202; r <= 1.234; s <= "hello"; - sarr[1] <= "sarr[1]"; - sarr[2] <= "sarr[2]"; - assoc["mapped"] <= "Is mapped"; + // Blocking to avoid delayed to dynamic var + sarr[1] = "sarr[1]"; + sarr[2] = "sarr[2]"; + assoc["mapped"] = "Is mapped"; end if (cyc==1) begin - if ($test$plusargs("save_restore")!=0) begin + if ($test$plusargs("save_restore") != 0) begin // Don't allow the restored model to run from time 0, it must run from a restore $write("%%Error: didn't really restore\n"); $stop; diff --git a/test_regress/t/t_sys_readmem_assoc.v b/test_regress/t/t_sys_readmem_assoc.v index 7d2487f31..d2a123837 100644 --- a/test_regress/t/t_sys_readmem_assoc.v +++ b/test_regress/t/t_sys_readmem_assoc.v @@ -7,9 +7,9 @@ `define STRINGIFY(x) `"x`" module t(/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; int cyc; @@ -20,7 +20,7 @@ module t(/*AUTOARG*/ always_ff @ (posedge clk) begin cyc <= cyc + 1; if (cyc == 1) begin - assoc_c[300] <= 10; // See if clearing must happen first + assoc_c[300] = 10; // See if clearing must happen first // Also checks no BLKANDNBLK due to readmem/writemem end else if (cyc == 2) begin diff --git a/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v index 2674d7a0a..617f94732 100755 --- a/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v +++ b/test_regress/t/t_virtual_interface_member_trigger_realistic_case.v @@ -57,7 +57,7 @@ class intf_driver; endtask endclass -module t_virtual_interface_member_trigger_realistic_case(); +module t; logic clk; logic [7:0] data; logic valid; From eb80db9397d20fbb827eac03ce78f42a82dacbdc Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 10 Aug 2025 08:38:26 -0400 Subject: [PATCH 160/252] Clarify extern error message --- src/V3LinkDot.cpp | 6 ++++-- test_regress/t/t_class_extern_bad.out | 4 ++-- test_regress/t/t_constraint_extern_bad.out | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 641e57355..fa3a765bb 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3717,7 +3717,8 @@ class LinkDotResolveVisitor final : public VNVisitor { } if (nodep->isExternProto()) { if (!m_curSymp->findIdFallback(nodep->name()) && nodep->isExternExplicit()) { - nodep->v3error("Definition not found for extern " + nodep->prettyNameQ()); + nodep->v3error("Definition not found for extern prototype " + + nodep->prettyNameQ()); } } VL_RESTORER(m_curSymp); @@ -4286,7 +4287,8 @@ class LinkDotResolveVisitor final : public VNVisitor { } if (nodep->isExternProto()) { if (!m_curSymp->findIdFallback(nodep->name())) { - nodep->v3error("Definition not found for extern " + nodep->prettyNameQ()); + nodep->v3error("Definition not found for extern prototype " + + nodep->prettyNameQ()); } } VL_RESTORER(m_curSymp); diff --git a/test_regress/t/t_class_extern_bad.out b/test_regress/t/t_class_extern_bad.out index fef3a2036..785b44d9e 100644 --- a/test_regress/t/t_class_extern_bad.out +++ b/test_regress/t/t_class_extern_bad.out @@ -5,10 +5,10 @@ 8 | extern task nodef(); | ^~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_class_extern_bad.v:8:16: Definition not found for extern 'nodef' +%Error: t/t_class_extern_bad.v:8:16: Definition not found for extern prototype 'nodef' 8 | extern task nodef(); | ^~~~~ -%Error: t/t_class_extern_bad.v:9:16: Definition not found for extern 'nodef' +%Error: t/t_class_extern_bad.v:9:16: Definition not found for extern prototype 'nodef' 9 | extern task nodef(); | ^~~~~ %Error: t/t_class_extern_bad.v:12:6: extern not found that declares 'noproto' diff --git a/test_regress/t/t_constraint_extern_bad.out b/test_regress/t/t_constraint_extern_bad.out index 73b10b4f8..2e6c8787f 100644 --- a/test_regress/t/t_constraint_extern_bad.out +++ b/test_regress/t/t_constraint_extern_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_constraint_extern_bad.v:8:22: Definition not found for extern 'missing_bad' +%Error: t/t_constraint_extern_bad.v:8:22: Definition not found for extern prototype 'missing_bad' 8 | extern constraint missing_bad; | ^~~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. From e6e52dd60ab3411cf457c98f88b6ee36dfcf8d91 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 10 Aug 2025 08:43:04 -0400 Subject: [PATCH 161/252] Tests: Fix t_constraint_nosolver_bad.py (#6273) --- test_regress/t/t_constraint_nosolver_bad.out | 7 ------- test_regress/t/t_constraint_nosolver_bad.py | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 test_regress/t/t_constraint_nosolver_bad.out diff --git a/test_regress/t/t_constraint_nosolver_bad.out b/test_regress/t/t_constraint_nosolver_bad.out deleted file mode 100644 index 2f4435757..000000000 --- a/test_regress/t/t_constraint_nosolver_bad.out +++ /dev/null @@ -1,7 +0,0 @@ -Process::open: execvp(someimaginarysolver): No such file or directory -%Warning: Subprocess command `someimaginarysolver' failed: exit status 127 -%Warning: Unable to communicate with SAT solver, please check its installation or specify a different one in VERILATOR_SOLVER environment variable. - ... Tried: $ someimaginarysolver - -%Error: t/t_constraint.v:25: Verilog $stop -Aborting... diff --git a/test_regress/t/t_constraint_nosolver_bad.py b/test_regress/t/t_constraint_nosolver_bad.py index 118aafe1b..69c260d6a 100755 --- a/test_regress/t/t_constraint_nosolver_bad.py +++ b/test_regress/t/t_constraint_nosolver_bad.py @@ -14,8 +14,9 @@ test.top_filename = "t/t_constraint.v" test.compile() -test.execute(run_env='VERILATOR_SOLVER=someimaginarysolver', - fails=True, - expect_filename=test.golden_filename) +test.execute(run_env='VERILATOR_SOLVER=someimaginarysolver', fails=True) + +# Not using golden file, as may get spurious pipe messages, see issue #6273 +test.file_grep(test.run_log_filename, r'Unable to communicate with SAT solver') test.passes() From 7696b0651c5209a77134611171218479ae82cf18 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 10 Aug 2025 09:19:16 +0100 Subject: [PATCH 162/252] Internals: Improve DFG dumping functions --- src/V3Dfg.cpp | 76 +++++++++++++++++++++++++--------------- src/V3Dfg.h | 20 ++++++++--- src/V3DfgBreakCycles.cpp | 7 ++++ 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 60167ed2e..ca744c99e 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -259,7 +259,7 @@ DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNo } } -static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; } +static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; } // Dump one DfgVertex in Graphviz format static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { @@ -383,25 +383,20 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } // Dump one DfgEdge in Graphviz format -static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, const string& headlabel) { - os << toDotId(*edge.sourcep()) << " -> " << toDotId(*edge.sinkp()); - if (!headlabel.empty()) os << " [headlabel=\"" << headlabel << "\"]"; +static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) { + if (!edge.sourcep()) return; + const DfgVertex& sink = *edge.sinkp(); // sink is never nullptr + os << toDotId(*edge.sourcep()) << " -> " << toDotId(sink); + if (sink.arity() > 1 || sink.is()) { + os << " [headlabel=\"" << sink.srcName(idx) << "\"]"; + } os << '\n'; } -// Dump one DfgVertex and all of its source DfgEdges in Graphviz format -static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) { - dumpDotVertex(os, vtx); - vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { // - if (edge.sourcep()) { - string headLabel; - if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); - dumpDotEdge(os, edge, headLabel); - } - }); -} +void DfgGraph::dumpDot(std::ostream& os, const std::string& label, + std::function p) const { + // This generates a graphviz dump, https://www.graphviz.org -void DfgGraph::dumpDot(std::ostream& os, const string& label) const { // Header os << "digraph dfg {\n"; os << "graph [label=\"" << name(); @@ -409,31 +404,53 @@ void DfgGraph::dumpDot(std::ostream& os, const string& label) const { os << "\", labelloc=t, labeljust=l]\n"; os << "graph [rankdir=LR]\n"; - // Emit all vertices - forEachVertex([&](const DfgVertex& vtx) { dumpDotVertexAndSourceEdges(os, vtx); }); + if (!p) { + // Emit all vertices and edges + forEachVertex([&](const DfgVertex& vtx) { + dumpDotVertex(os, vtx); + vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(os, e, i); }); + }); + } else { + // Emit vertices that satify the predicate 'p' + forEachVertex([&](const DfgVertex& vtx) { + if (!p(vtx)) return; + dumpDotVertex(os, vtx); + vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { + if (!e.sourcep() || !p(*e.sourcep())) return; + dumpDotEdge(os, e, i); + }); + }); + } // Footer os << "}\n"; } -void DfgGraph::dumpDotFile(const string& filename, const string& label) const { - // This generates a file used by graphviz, https://www.graphviz.org - // "hardcoded" parameters: +std::string DfgGraph::dumpDotString(const std::string& label, + std::function p) const { + std::stringstream ss; + dumpDot(ss, label, p); + return ss.str(); +} + +void DfgGraph::dumpDotFile(const std::string& filename, const std::string& label, + std::function p) const { const std::unique_ptr os{V3File::new_ofstream(filename)}; if (os->fail()) v3fatal("Can't write file: " << filename); - dumpDot(*os.get(), label); + dumpDot(*os.get(), label, p); os->close(); } -void DfgGraph::dumpDotFilePrefixed(const string& label) const { - string filename = name(); +void DfgGraph::dumpDotFilePrefixed(const std::string& label, + std::function p) const { + std::string filename = name(); if (!label.empty()) filename += "-" + label; - dumpDotFile(v3Global.debugFilename(filename) + ".dot", label); + dumpDotFile(v3Global.debugFilename(filename) + ".dot", label, p); } -// LCOV_EXCL_START // Debug function for developer use only -void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, - const string& name) const { +// LCOV_EXCL_START // Debug functions for developer use only +void DfgGraph::dumpDotUpstreamCone(const std::string& fileName, const DfgVertex& vtx, + const std::string& name) const { // Open output file const std::unique_ptr os{V3File::new_ofstream(fileName)}; if (os->fail()) v3fatal("Can't write file: " << fileName); @@ -465,7 +482,8 @@ void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); // Emit this vertex and all of its source edges - dumpDotVertexAndSourceEdges(*os, *vtxp); + dumpDotVertex(*os, *vtxp); + vtxp->forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(*os, e, i); }); } // Footer diff --git a/src/V3Dfg.h b/src/V3Dfg.h index d9675b627..2ee391d9f 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -746,17 +746,27 @@ public: // Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of // the graph which is included in the output. - void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED; + // If the predicate function 'p' is provided, only those vertices are dumped that satifty it. + void dumpDot(std::ostream& os, const std::string& label, + std::function p = {}) const VL_MT_DISABLED; // Dump graph in Graphviz format into a new file with the given 'filename'. 'label' is added to // the name of the graph which is included in the output. - void dumpDotFile(const string& filename, const string& label = "") const VL_MT_DISABLED; + // If the predicate function 'p' is provided, only those vertices are dumped that satifty it. + void dumpDotFile(const std::string& filename, const std::string& label, + std::function p = {}) const VL_MT_DISABLED; + // Same as dumpDotFile, but returns the contents as a string. + std::string dumpDotString(const std::string& label, + std::function p = {}) const VL_MT_DISABLED; // Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is // added to the name of the graph, which is included in the file name and the output. - void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED; + // If the predicate function 'p' is provided, only those vertices are dumped that satifty it. + void dumpDotFilePrefixed(const std::string& label, + std::function p = {}) const VL_MT_DISABLED; + // Dump upstream (source) logic cone starting from given vertex into a file with the given // 'filename'. 'name' is the name of the graph, which is included in the output. - void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx, - const string& name = "") const VL_MT_DISABLED; + void dumpDotUpstreamCone(const std::string& filename, const DfgVertex& vtx, + const std::string& name = "") const VL_MT_DISABLED; }; // Specializations of privateTypeTest diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 1cdb26033..673e2fc66 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -1060,6 +1060,13 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { } } while (nImprovements != prevNImprovements); + if (dumpDfgLevel() >= 9) { + const auto userDataInUse = res.userDataInUse(); + V3DfgPasses::colorStronglyConnectedComponents(res); + res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining", + [](const DfgVertex& vtx) { return vtx.getUser(); }); + } + // If an improvement was made, return the still cyclic improved graph if (nImprovements) { UINFO(7, "Graph was improved " << nImprovements << " times"); From 762c5f573ce6a5e5f2507197d9f4f294c3f10356 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 10 Aug 2025 18:14:02 +0100 Subject: [PATCH 163/252] Improve DFG to enable breaking more combinational cycles --- src/V3DfgBreakCycles.cpp | 171 ++++++++++++++++++++++++---- test_regress/t/t_dfg_break_cycles.v | 36 +++++- 2 files changed, 181 insertions(+), 26 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 673e2fc66..2aa7f39da 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -74,7 +74,9 @@ class TraceDriver final : public DfgVisitor { // Denotes if a 'Visited' entry appear in m_stack std::unordered_map m_visited; +#ifdef VL_DEBUG std::ofstream m_lineCoverageFile; // Line coverage file, just for testing +#endif // METHODS @@ -190,7 +192,7 @@ class TraceDriver final : public DfgVisitor { } template - Vertex* bitwiseBinary(Vertex* vtxp) { + Vertex* traceBinary(Vertex* vtxp) { static_assert(std::is_base_of::value, "Should only call on DfgVertexBinary"); if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, m_lsb)) { @@ -229,11 +231,15 @@ class TraceDriver final : public DfgVisitor { // Use this macro to set the result in 'visit' methods. This also emits // a line to m_lineCoverageFile for testing. // TODO: Use C++20 std::source_location instead of a macro +#ifdef VL_DEBUG #define SET_RESULT(vtxp) \ do { \ m_resp = vtxp; \ if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \ } while (false) +#else +#define SET_RESULT(vtxp) m_resp = vtxp; +#endif // VISITORS void visit(DfgVertex* vtxp) override { @@ -350,15 +356,47 @@ class TraceDriver final : public DfgVisitor { void visit(DfgAnd* vtxp) override { if (!m_aggressive) return; - SET_RESULT(bitwiseBinary(vtxp)); + SET_RESULT(traceBinary(vtxp)); } void visit(DfgOr* vtxp) override { if (!m_aggressive) return; - SET_RESULT(bitwiseBinary(vtxp)); + SET_RESULT(traceBinary(vtxp)); } void visit(DfgXor* vtxp) override { if (!m_aggressive) return; - SET_RESULT(bitwiseBinary(vtxp)); + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgAdd* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgSub* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgEq* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgNeq* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgLt* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgLte* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgGt* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); + } + void visit(DfgGte* vtxp) override { + if (!m_aggressive) return; + SET_RESULT(traceBinary(vtxp)); } void visit(DfgShiftR* vtxp) override { @@ -432,6 +470,22 @@ class TraceDriver final : public DfgVisitor { } } + void visit(DfgCond* vtxp) override { + if (!m_aggressive) return; + if (DfgVertex* const condp = trace(vtxp->condp(), 0, 0)) { + if (DfgVertex* const thenp = trace(vtxp->thenp(), m_msb, m_lsb)) { + if (DfgVertex* const elsep = trace(vtxp->elsep(), m_msb, m_lsb)) { + DfgCond* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->condp(condp); + resp->thenp(thenp); + resp->elsep(elsep); + SET_RESULT(resp); + return; + } + } + } + } + #undef SET_RESULT // CONSTRUCTOR @@ -439,12 +493,14 @@ class TraceDriver final : public DfgVisitor { : m_dfg{dfg} , m_component{component} , m_aggressive{aggressive} { +#ifdef VL_DEBUG if (v3Global.opt.debugCheck()) { m_lineCoverageFile.open( // v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__V3DfgBreakCycles-TraceDriver-line-coverage.txt", // std::ios_base::out | std::ios_base::app); } +#endif } public: @@ -503,15 +559,37 @@ class IndependentBits final : public DfgVisitor { // Use this macro to call 'mask' in 'visit' methods. This also emits // a line to m_lineCoverageFile for testing. // TODO: Use C++20 std::source_location instead of a macro +#ifdef VL_DEBUG #define MASK(vtxp) \ ([this](const DfgVertex* p) -> V3Number& { \ if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \ return mask(p); \ }(vtxp)) +#else +#define MASK(vtxp) mask(vtxp) +#endif + + // Set all bits at or below the most signicant set bit + void floodTowardsLsb(V3Number& num) { + bool set = false; + for (int i = num.width() - 1; i >= 0; --i) { + if (num.bitIs1(i)) set = true; + if (set) num.setBit(i, '1'); + } + } + + // Set all bits at or above the least signicant set bit + void floodTowardsMsb(V3Number& num) { + bool set = false; + for (int i = 0; i < num.width(); ++i) { + if (num.bitIs1(i)) set = true; + if (set) num.setBit(i, '1'); + } + } // VISITORS void visit(DfgVertex* vtxp) override { - UINFO(9, "Unhandled vertex type " << vtxp->typeName()); + UINFO(9, "IndependentBits - Unhandled vertex type: " << vtxp->typeName()); // Conservative assumption about all bits being dependent prevails } @@ -594,6 +672,42 @@ class IndependentBits final : public DfgVisitor { MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); } + void visit(DfgAdd* vtxp) override { + V3Number& m = MASK(vtxp); + m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); + floodTowardsMsb(m); + } + void visit(DfgSub* vtxp) override { // Same as Add: 2's complement (a - b) == (a + ~b + 1) + V3Number& m = MASK(vtxp); + m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); + floodTowardsMsb(m); + } + + void visit(DfgEq* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgNeq* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgLt* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgLte* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgGt* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgGte* vtxp) override { + const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); + MASK(vtxp).setBit(0, independent ? '0' : '1'); + } + void visit(DfgShiftR* vtxp) override { DfgVertex* const rhsp = vtxp->rhsp(); DfgVertex* const lhsp = vtxp->lhsp(); @@ -602,22 +716,20 @@ class IndependentBits final : public DfgVisitor { // Constant shift can be computed precisely if (DfgConst* const rConstp = rhsp->cast()) { const uint32_t shiftAmount = rConstp->toU32(); - if (shiftAmount >= width) return; - V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; - shiftedMask.opShiftR(MASK(lhsp), rConstp->num()); V3Number& m = MASK(vtxp); - m.opSelInto(shiftedMask, 0, width - shiftAmount); - m.opSetRange(width - shiftAmount, shiftAmount, '0'); + if (shiftAmount >= width) { + m.setAllBits0(); + } else { + m.opShiftR(MASK(lhsp), rConstp->num()); + } return; } // Otherwise, as the shift amount is non-negative, any bit at or below // the most significant dependent bit might be dependent - V3Number& lMask = MASK(lhsp); - V3Number& vMask = MASK(vtxp); - uint32_t lzc = 0; // Leading zero count - while (lzc < width && lMask.bitIs0(width - 1 - lzc)) ++lzc; - while (lzc > 0) vMask.setBit(width - 1 - (--lzc), '0'); + V3Number& m = MASK(vtxp); + m = MASK(lhsp); + floodTowardsLsb(m); } void visit(DfgShiftL* vtxp) override { @@ -628,22 +740,28 @@ class IndependentBits final : public DfgVisitor { // Constant shift can be computed precisely if (DfgConst* const rConstp = rhsp->cast()) { const uint32_t shiftAmount = rConstp->toU32(); - if (shiftAmount >= width) return; - V3Number shiftedMask{lhsp->fileline(), static_cast(width), 0}; - shiftedMask.opShiftL(MASK(lhsp), rConstp->num()); V3Number& m = MASK(vtxp); - m.opSelInto(shiftedMask, shiftAmount, width - shiftAmount); - m.opSetRange(0, shiftAmount, '0'); + if (shiftAmount >= width) { + m.setAllBits0(); + } else { + m.opShiftL(MASK(lhsp), rConstp->num()); + } return; } // Otherwise, as the shift amount is non-negative, any bit at or above // the least significant dependent bit might be dependent - V3Number& lMask = MASK(lhsp); - V3Number& vMask = MASK(vtxp); - uint32_t tzc = 0; // Trailing zero count - while (tzc < width && lMask.bitIs0(tzc)) ++tzc; - while (tzc > 0) vMask.setBit(--tzc, '0'); + V3Number& m = MASK(vtxp); + m = MASK(lhsp); + floodTowardsMsb(m); + } + + void visit(DfgCond* vtxp) override { + if (!MASK(vtxp->condp()).isEqZero()) { + MASK(vtxp).setAllBits1(); + } else { + MASK(vtxp).opOr(MASK(vtxp->thenp()), MASK(vtxp->elsep())); + } } #undef MASK @@ -651,12 +769,15 @@ class IndependentBits final : public DfgVisitor { // CONSTRUCTOR IndependentBits(DfgGraph& dfg, DfgVertex* vtxp) : m_component{vtxp->getUser()} { + +#ifdef VL_DEBUG if (v3Global.opt.debugCheck()) { m_lineCoverageFile.open( // v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__V3DfgBreakCycles-IndependentBits-line-coverage.txt", // std::ios_base::out | std::ios_base::app); } +#endif // Work list for the traversal std::deque workList; diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index 5a44799e8..996a80bd9 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -4,7 +4,7 @@ // any use, without warranty, 2025 by Geza Lore. // SPDX-License-Identifier: CC0-1.0 -`define signal(name, width) wire [width-1:0] name; +`define signal(name, width) wire [width-1:0] name module t ( `include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py @@ -140,4 +140,38 @@ module t ( assign array_3[1] = array_3[0]; `signal(ARRAY_3, 3); assign ARRAY_3 = array_3[0]; + + `signal(ADD, 8); + assign ADD = ((ADD << 4) + 8'(rand_a[3:0])); + + `signal(SUB, 8); + assign SUB = ((SUB << 4) - 8'(rand_a[3:0])); + + `signal(EQ, 2); + assign EQ = {rand_a[0], EQ >> 1 == rand_b[1:0]}; + + `signal(NEQ, 2); + assign NEQ = {rand_a[0], NEQ >> 1 != rand_b[1:0]}; + + `signal(LT, 2); + assign LT = {rand_a[0], LT >> 1 < rand_b[1:0]}; + + `signal(LTE, 2); + assign LTE = {rand_a[0], LTE >> 1 <= rand_b[1:0]}; + + `signal(GT, 2); + assign GT = {rand_a[0], GT >> 1 > rand_b[1:0]}; + + `signal(GTE, 2); + assign GTE = {rand_a[0], GTE >> 1 >= rand_b[1:0]}; + + `signal(COND_THEN, 3); + assign COND_THEN = {rand_a[0], rand_a[0] ? 2'(COND_THEN << 2) : rand_b[1:0]}; + + `signal(COND_ELSE, 3); + assign COND_ELSE = {rand_a[0], rand_a[0] ? rand_b[1:0] : 2'(COND_ELSE << 2)}; + + `signal(COND_COND, 3); + assign COND_COND = {rand_a[0], (COND_COND >> 2) == 3'b001 ? rand_b[3:2] : rand_b[1:0]}; + endmodule From 60cbbf0ec1d4b4c6faad3ff0c66d2497768131dc Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 11 Aug 2025 19:50:47 -0400 Subject: [PATCH 164/252] Add error on mismatching prototypes (#6207). --- Changes | 2 +- docs/guide/warnings.rst | 13 +++ src/V3Ast.h | 4 + src/V3AstNodeDType.h | 2 +- src/V3AstNodeExpr.h | 4 +- src/V3AstNodeOther.h | 4 + src/V3Error.cpp | 4 +- src/V3Error.h | 22 ++--- src/V3LinkDot.cpp | 25 ++++-- src/V3Width.cpp | 99 +++++++++++++++++++++- src/V3WidthCommit.cpp | 11 +++ test_regress/t/t_class_extern_args.py | 17 ++++ test_regress/t/t_class_extern_args_bad.out | 75 ++++++++++++++++ test_regress/t/t_class_extern_args_bad.py | 16 ++++ test_regress/t/t_class_extern_args_bad.v | 46 ++++++++++ test_regress/t/t_class_extern_bad.out | 25 +++--- test_regress/t/t_class_extern_bad.v | 8 +- test_regress/t/t_constraint_extern_bad.out | 5 +- 18 files changed, 340 insertions(+), 42 deletions(-) create mode 100755 test_regress/t/t_class_extern_args.py create mode 100644 test_regress/t/t_class_extern_args_bad.out create mode 100755 test_regress/t/t_class_extern_args_bad.py create mode 100644 test_regress/t/t_class_extern_args_bad.v diff --git a/Changes b/Changes index 8c0734804..3711067e3 100644 --- a/Changes +++ b/Changes @@ -18,7 +18,7 @@ Verilator 5.039 devel * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add PARAMNODEFAULT error, for parameters without defaults. * Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. -* Add error on missing forward declarations (#6206). [Alex Solomatnikov] +* Add error on missing and mismatching prototypes (#6206) (#6207). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add error on class 'function static'. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 22e61d582..24bfd9a56 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1612,6 +1612,19 @@ List Of Warnings accepts the protected code. +.. option:: PROTOTYPEMIS + + Error that a function prototype does not match in some respects the + out-of-block declaration of that function. IEEE requires this error. + + The typical solution is to fix the prototype to match the declaration + exactly, including in number of arguments, name of arguments, argument + data types, and return data type (for functions). + + Disabling this error will cause Verilator to ignore the prototype and + may make the code illegal in other tools. + + .. option:: RANDC Historical, never issued since version 5.018, when :code:`randc` became diff --git a/src/V3Ast.h b/src/V3Ast.h index f0528a961..f4a2842bc 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -508,6 +508,9 @@ public: ENUM_NAME, // V3Width processes ENUM_VALID, // V3Width processes // + FUNC_ARG_PROTO, // V3WidthCommit processes + FUNC_RETURN_PROTO, // V3WidthCommit processes + // TYPEID, // V3Width processes TYPENAME, // V3Width processes // @@ -538,6 +541,7 @@ public: "DT_PUBLIC", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID", + "FUNC_ARG_PROTO", "FUNC_RETURN_PROTO", "TYPEID", "TYPENAME", "VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_FORCEABLE", "VAR_PORT_DTYPE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 53c28d404..f16fc7fa2 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -1336,7 +1336,7 @@ public: AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; } AstNodeDType* virtRefDTypep() const override { return nullptr; } void virtRefDTypep(AstNodeDType* nodep) override {} - bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; } + bool similarDTypeNode(const AstNodeDType* samep) const override { return true; } AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } int widthAlignBytes() const override { return 1; } int widthTotalBytes() const override { return 1; } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 1e06fd10c..5b5c17f6d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -594,6 +594,7 @@ class AstAttrOf final : public AstNodeExpr { // @astgen op1 := fromp : Optional[AstNode] // @astgen op2 := dimp : Optional[AstNodeExpr] VAttrType m_attrType; // What sort of extraction + string m_name; // Name for some attributes public: AstAttrOf(FileLine* fl, VAttrType attrtype, AstNode* fromp = nullptr, AstNodeExpr* dimp = nullptr) @@ -603,10 +604,11 @@ public: m_attrType = attrtype; } ASTGEN_MEMBERS_AstAttrOf; + string name() const override VL_MT_STABLE { return m_name; } // * = Var name + void name(const string& name) override { m_name = name; } VAttrType attrType() const { return m_attrType; } void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; - string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { V3ERROR_NA_RETURN(true); } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index af7d445d4..7b55b7ce9 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -69,6 +69,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode { bool m_taskPublic : 1; // Public task bool m_attrIsolateAssign : 1; // User isolate_assignments attribute bool m_classMethod : 1; // Class method + bool m_didProto : 1; // Did prototype processing bool m_prototype : 1; // Just a prototype bool m_dpiExport : 1; // DPI exported bool m_dpiImport : 1; // DPI imported @@ -98,6 +99,7 @@ protected: , m_taskPublic{false} , m_attrIsolateAssign{false} , m_classMethod{false} + , m_didProto{false} , m_prototype{false} , m_dpiExport{false} , m_dpiImport{false} @@ -145,6 +147,8 @@ public: void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } bool classMethod() const { return m_classMethod; } void classMethod(bool flag) { m_classMethod = flag; } + bool didProto() const { return m_didProto; } + void didProto(bool flag) { m_didProto = flag; } bool isExternProto() const { return m_isExternProto; } void isExternProto(bool flag) { m_isExternProto = flag; } bool isExternDef() const { return m_isExternDef; } diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 0ef6e50fb..67c7566bf 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -300,8 +300,8 @@ void V3Error::init() { describedEachWarn(static_cast(i), false); pretendError(static_cast(i), V3ErrorCode{i}.pretendError()); } - UASSERT(std::string{V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()} == " MAX", - "Enum table in V3ErrorCode::EC_ascii() is munged"); + // Not an UASSERT as failure would call V3Error and it's broken due to this + assert(std::string{V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()} == " MAX"); } string V3Error::lineStr(const char* filename, int lineno) VL_PURE { diff --git a/src/V3Error.h b/src/V3Error.h index ebb8e3fa5..224f6f987 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -139,7 +139,8 @@ public: PROCASSINIT, // Procedural assignment versus initialization PROCASSWIRE, // Procedural assignment on wire PROFOUTOFDATE, // Profile data out of date - PROTECTED, // detected `pragma protected + PROTECTED, // Detected `pragma protected + PROTOTYPEMIS, // Prototype mismatch or related RANDC, // Unsupported: 'randc' converted to 'rand' REALCVT, // Real conversion REDEFMACRO, // Redefining existing define macro @@ -213,14 +214,14 @@ public: "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", - "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", - "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", - "SPECIFYIGN", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", - "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", - "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", - "USERERROR", "USERFATAL", "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", - "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", - " MAX"}; + "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", + "PROTOTYPEMIS", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", + "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", "SPLITVAR", "STATICVAR", "STMTDLY", + "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", + "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP", + "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", "USERWARN", + "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", + "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; return names[m_e]; } // Warnings that default to off @@ -249,7 +250,8 @@ public: || m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == IMPURE || m_e == MODMISSING || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND - || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE + || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == PROTOTYPEMIS + || m_e == ZEROREPL // Says IEEE ); } // Warnings to mention manual diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index fa3a765bb..6a53003d4 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3717,8 +3717,8 @@ class LinkDotResolveVisitor final : public VNVisitor { } if (nodep->isExternProto()) { if (!m_curSymp->findIdFallback(nodep->name()) && nodep->isExternExplicit()) { - nodep->v3error("Definition not found for extern prototype " - + nodep->prettyNameQ()); + nodep->v3warn(PROTOTYPEMIS, "Definition not found for extern prototype " + << nodep->prettyNameQ()); } } VL_RESTORER(m_curSymp); @@ -4270,7 +4270,8 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); checkNoDot(nodep); - if (nodep->isExternDef()) { + if (nodep->isExternDef() && !nodep->didProto()) { + nodep->didProto(true); if (const VSymEnt* const foundp = m_curSymp->findIdFallback("extern " + nodep->name())) { const AstNodeFTask* const protop = VN_AS(foundp->nodep(), NodeFTask); @@ -4281,14 +4282,28 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->isStatic(protop->isStatic()); nodep->isVirtual(protop->isVirtual()); nodep->lifetime(protop->lifetime()); + // Always add an FUNC_RETURN_PROTO even if task, so we can locate the prototype + nodep->addStmtsp(new AstAttrOf{protop->fileline(), VAttrType::FUNC_RETURN_PROTO, + protop->fvarp() + ? protop->fvarp()->cloneTree(false) + : new AstVoidDType{protop->fileline()}}); + for (AstNode* stmtp = protop->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (AstVar* const portp = VN_CAST(stmtp, Var)) { + AstAttrOf* const attrp + = new AstAttrOf{protop->fileline(), VAttrType::FUNC_ARG_PROTO, + portp->subDTypep()->cloneTree(false)}; + attrp->name(portp->name()); + nodep->addStmtsp(attrp); + } + } } else { nodep->v3error("extern not found that declares " + nodep->prettyNameQ()); } } if (nodep->isExternProto()) { if (!m_curSymp->findIdFallback(nodep->name())) { - nodep->v3error("Definition not found for extern prototype " - + nodep->prettyNameQ()); + nodep->v3warn(PROTOTYPEMIS, + "Definition not found for extern prototype " + nodep->prettyNameQ()); } } VL_RESTORER(m_curSymp); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 22a7c78b8..9af0997ad 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -911,7 +911,7 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeStream* nodep) override { VL_RESTORER(m_streamConcat); - UINFOTREE(1, nodep, "stream-in vup" << m_vup, "stream-in "); + // UINFOTREE(1, nodep, "stream-in vup" << m_vup, "stream-in "); if (m_vup->prelim()) { m_streamConcat = true; iterateCheckSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); @@ -1669,9 +1669,6 @@ class WidthVisitor final : public VNVisitor { if (nodep->dimp()) userIterateAndNext(nodep->dimp(), WidthVP{SELF, BOTH}.p()); // Don't iterate children, don't want to lose VarRef. switch (nodep->attrType()) { - case VAttrType::VAR_BASE: - // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf - break; case VAttrType::DIM_DIMENSIONS: case VAttrType::DIM_UNPK_DIMENSIONS: { UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression"); @@ -1795,6 +1792,36 @@ class WidthVisitor final : public VNVisitor { } break; } + case VAttrType::FUNC_ARG_PROTO: + // Created by V3LinkDot only to check that the prototype was correct when we got here + // Checked at bottom of visit(AstNodeFTask) + // V3WidthCommit::visit(AstAttrOf) will delete nodep + break; + case VAttrType::FUNC_RETURN_PROTO: { + // Created by V3LinkDot only to check that the prototype was correct when we got here + UASSERT_OBJ(m_ftaskp, nodep, "FUNC attr not under function"); + AstNodeDType* const protoDtp = nodep->fromp()->dtypep(); + AstNodeDType* const declDtp = m_ftaskp->fvarp() + ? m_ftaskp->fvarp()->dtypep() + : new AstVoidDType{m_ftaskp->fileline()}; + if (!similarDTypeRecurse(protoDtp, declDtp)) { + protoDtp->v3warn( + PROTOTYPEMIS, + "In prototype for " + << m_ftaskp->prettyNameQ() + << ", return data type does not match out-of-block" + " declaration data-type (IEEE 1800-2023 8.24)\n" + << protoDtp->warnMore() + << "... Prototype data type: " << protoDtp->prettyDTypeNameQ() << '\n' + << protoDtp->warnMore() + << "... Declaration data type: " << declDtp->prettyDTypeNameQ() << '\n' + << protoDtp->warnContextPrimary() << '\n' + << declDtp->warnOther() << "... Location of out-of-block declaration\n" + << declDtp->warnContextSecondary()); + } + // V3WidthCommit::visit(AstAttrOf) will delete nodep + break; + } case VAttrType::TYPENAME: { UASSERT_OBJ(nodep->fromp(), nodep, "Unprovided expression"); const string result = nodep->fromp()->dtypep()->prettyDTypeName(true); @@ -1808,6 +1835,9 @@ class WidthVisitor final : public VNVisitor { // Soon to be handled in AstEqT nodep->dtypeSetSigned32(); break; + case VAttrType::VAR_BASE: + // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf + break; default: { // Everything else resolved earlier nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // Approximation, unsized 32 @@ -6076,6 +6106,67 @@ class WidthVisitor final : public VNVisitor { nodep->dpiOpenParentInc(); // Mark so V3Task will wait for a child to build calling // func } + + std::vector ports; + std::vector protos; + AstAttrOf* protop = nullptr; // Base prototype + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (AstVar* const portp = VN_CAST(stmtp, Var)) { + if (portp->isIO() && !portp->isFuncReturn()) { ports.emplace_back(portp); } + } else if (AstAttrOf* const attrp = VN_CAST(stmtp, AttrOf)) { + if (attrp->attrType() == VAttrType::FUNC_ARG_PROTO) { protos.emplace_back(attrp); } + if (attrp->attrType() == VAttrType::FUNC_RETURN_PROTO) { protop = attrp; } + } + } + if (protop) { + for (size_t i = 0; i < std::max(ports.size(), protos.size()); ++i) { + if (i >= ports.size() || i >= protos.size()) { + protop->v3warn( + PROTOTYPEMIS, + "In prototype for " + << nodep->prettyNameQ() + << ", the argumement counts do not match the out-of-block declaration" + << " (IEEE 1800-2023 8.24)\n" + << protop->warnContextPrimary() << '\n' + << nodep->warnOther() << "... Location of out-of-block declaration\n" + << nodep->warnContextSecondary()); + break; + } else { + AstVar* const portp = ports[i]; + AstAttrOf* const protop = protos[i]; + AstNodeDType* const declDtp = portp->dtypep(); + AstNodeDType* const protoDtp = protop->fromp()->dtypep(); + if (portp->name() != protop->name()) { + protoDtp->v3warn(PROTOTYPEMIS, + "In prototype for " + << nodep->prettyNameQ() << ", argument " << (i + 1) + << " named " << protop->prettyNameQ() + << " mismatches out-of-block argument name " + << portp->prettyNameQ() << " (IEEE 1800-2023 8.24)\n" + << protoDtp->warnContextPrimary() << '\n' + << declDtp->warnOther() + << "... Location of out-of-block declaration\n" + << declDtp->warnContextSecondary()); + } else if (!similarDTypeRecurse(protoDtp, declDtp)) { + protoDtp->v3warn( + PROTOTYPEMIS, + "In prototype for " + << nodep->prettyNameQ() << ", argument " << portp->prettyNameQ() + << " data-type does not match out-of-block" + " declaration's data-type (IEEE 1800-2023 8.24)\n" + << protoDtp->warnMore() << "... Prototype data type: " + << protoDtp->prettyDTypeNameQ() << '\n' + << protoDtp->warnMore() << "... Declaration data type: " + << declDtp->prettyDTypeNameQ() << '\n' + << protoDtp->warnContextPrimary() << '\n' + << declDtp->warnOther() + << "... Location of out-of-block declaration\n" + << declDtp->warnContextSecondary()); + } + } + } + // V3WidthCommit::visit(AstAttrOf) will delete nodep + } } void visit(AstConstraint* nodep) override { if (nodep->didWidth()) return; diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index f94eb0788..64e35b3ea 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -168,6 +168,17 @@ private: } } } + void visit(AstAttrOf* nodep) override { + switch (nodep->attrType()) { + case VAttrType::FUNC_ARG_PROTO: // FALLTHRU + case VAttrType::FUNC_RETURN_PROTO: + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + default:; + } + iterateChildren(nodep); + editDType(nodep); + } void visit(AstClassExtends* nodep) override { if (nodep->user1SetOnce()) return; // Process once // Extend arguments were converted to super.new arguments in V3LinkDot diff --git a/test_regress/t/t_class_extern_args.py b/test_regress/t/t_class_extern_args.py new file mode 100755 index 000000000..3d49093ba --- /dev/null +++ b/test_regress/t/t_class_extern_args.py @@ -0,0 +1,17 @@ +#!/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.top_filename = 't/t_class_extern_args_bad.v' + +test.lint(verilator_flags2=['-Wno-PROTOTYPEMIS']) + +test.passes() diff --git a/test_regress/t/t_class_extern_args_bad.out b/test_regress/t/t_class_extern_args_bad.out new file mode 100644 index 000000000..facf52cdb --- /dev/null +++ b/test_regress/t/t_class_extern_args_bad.out @@ -0,0 +1,75 @@ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:8:15: In prototype for 'func_bad', return data type does not match out-of-block declaration data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'VOIDDTYPE' + : ... Declaration data type: 'bit' + 8 | extern task func_bad(); + | ^~~~~~~~ + t/t_class_extern_args_bad.v:19:10: ... Location of out-of-block declaration + 19 | function bit Cls::func_bad(); + | ^~~ + ... For error description see https://verilator.org/warn/PROTOTYPEMIS?v=latest +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:9:19: In prototype for 'f1_bad', return data type does not match out-of-block declaration data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'int' + : ... Declaration data type: 'bit' + 9 | extern function int f1_bad(); + | ^~~ + t/t_class_extern_args_bad.v:22:10: ... Location of out-of-block declaration + 22 | function bit Cls::f1_bad(); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:10:19: In prototype for 'f2_bad', return data type does not match out-of-block declaration data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'int' + : ... Declaration data type: 'VOIDDTYPE' + 10 | extern function int f2_bad(); + | ^~~ + t/t_class_extern_args_bad.v:24:15: ... Location of out-of-block declaration + 24 | function void Cls::f2_bad(); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:11:24: In prototype for 'f3_bad', return data type does not match out-of-block declaration data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'VOIDDTYPE' + : ... Declaration data type: 'bit' + 11 | extern function void f3_bad(); + | ^~~~~~ + t/t_class_extern_args_bad.v:26:10: ... Location of out-of-block declaration + 26 | function bit Cls::f3_bad(); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:12:34: In prototype for 'f1bit_bad', argument 'a' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'int' + : ... Declaration data type: 'bit' + 12 | extern function void f1bit_bad(int a); + | ^~~ + t/t_class_extern_args_bad.v:29:30: ... Location of out-of-block declaration + 29 | function void Cls::f1bit_bad(bit a); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:13:24: In prototype for 'f2args1_bad', the argumement counts do not match the out-of-block declaration (IEEE 1800-2023 8.24) + : ... note: In instance 't' + 13 | extern function void f2args1_bad(bit a); + | ^~~~~~~~~~~ + t/t_class_extern_args_bad.v:32:15: ... Location of out-of-block declaration + 32 | function void Cls::f2args1_bad(bit a, bit b); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:14:24: In prototype for 'f2args2', the argumement counts do not match the out-of-block declaration (IEEE 1800-2023 8.24) + : ... note: In instance 't' + 14 | extern function void f2args2(bit a); + | ^~~~~~~ + t/t_class_extern_args_bad.v:35:15: ... Location of out-of-block declaration + 35 | function void Cls::f2args2(bit a, bit b); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:15:24: In prototype for 'f2args3_bad', the argumement counts do not match the out-of-block declaration (IEEE 1800-2023 8.24) + : ... note: In instance 't' + 15 | extern function void f2args3_bad(bit a, bit b, bit c); + | ^~~~~~~~~~~ + t/t_class_extern_args_bad.v:38:15: ... Location of out-of-block declaration + 38 | function void Cls::f2args3_bad(bit a, bit b); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_args_bad.v:16:38: In prototype for 'farg_name_bad', argument 1 named 'declnamebad' mismatches out-of-block argument name 'declname' (IEEE 1800-2023 8.24) + : ... note: In instance 't' + 16 | extern function void farg_name_bad(bit declnamebad); + | ^~~ + t/t_class_extern_args_bad.v:41:34: ... Location of out-of-block declaration + 41 | function void Cls::farg_name_bad(bit declname); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_extern_args_bad.py b/test_regress/t/t_class_extern_args_bad.py new file mode 100755 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_class_extern_args_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_extern_args_bad.v b/test_regress/t/t_class_extern_args_bad.v new file mode 100644 index 000000000..a969abb06 --- /dev/null +++ b/test_regress/t/t_class_extern_args_bad.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + extern task func_bad(); //<--- Error (mismatch func) + extern function int f1_bad(); //<--- Error (mismatch func type) + extern function int f2_bad(); //<--- Error (mismatch func type) + extern function void f3_bad(); //<--- Error (mismatch func type) + extern function void f1bit_bad(int a); //<--- Error (mismatch arg type) + extern function void f2args1_bad(bit a); //<--- Error (missing arg) + extern function void f2args2(bit a); // ok + extern function void f2args3_bad(bit a, bit b, bit c); //<--- Error (missing arg) + extern function void farg_name_bad(bit declnamebad); //<--- Error (declname arg) +endclass + +function bit Cls::func_bad(); +endfunction + +function bit Cls::f1_bad(); +endfunction +function void Cls::f2_bad(); +endfunction +function bit Cls::f3_bad(); +endfunction + +function void Cls::f1bit_bad(bit a); +endfunction + +function void Cls::f2args1_bad(bit a, bit b); +endfunction + +function void Cls::f2args2(bit a, bit b); +endfunction + +function void Cls::f2args3_bad(bit a, bit b); +endfunction + +function void Cls::farg_name_bad(bit declname); +endfunction + +module t; + initial $stop; +endmodule diff --git a/test_regress/t/t_class_extern_bad.out b/test_regress/t/t_class_extern_bad.out index 785b44d9e..d83e39a43 100644 --- a/test_regress/t/t_class_extern_bad.out +++ b/test_regress/t/t_class_extern_bad.out @@ -1,16 +1,17 @@ -%Error: t/t_class_extern_bad.v:9:16: Duplicate declaration of task: 'extern nodef' - 9 | extern task nodef(); - | ^~~~~ - t/t_class_extern_bad.v:8:16: ... Location of original declaration - 8 | extern task nodef(); - | ^~~~~ +%Error: t/t_class_extern_bad.v:9:15: Duplicate declaration of task: 'extern nodef' + 9 | extern task nodef(); + | ^~~~~ + t/t_class_extern_bad.v:8:15: ... Location of original declaration + 8 | extern task nodef(); + | ^~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_class_extern_bad.v:8:16: Definition not found for extern prototype 'nodef' - 8 | extern task nodef(); - | ^~~~~ -%Error: t/t_class_extern_bad.v:9:16: Definition not found for extern prototype 'nodef' - 9 | extern task nodef(); - | ^~~~~ +%Error-PROTOTYPEMIS: t/t_class_extern_bad.v:8:15: Definition not found for extern prototype 'nodef' + 8 | extern task nodef(); + | ^~~~~ + ... For error description see https://verilator.org/warn/PROTOTYPEMIS?v=latest +%Error-PROTOTYPEMIS: t/t_class_extern_bad.v:9:15: Definition not found for extern prototype 'nodef' + 9 | extern task nodef(); + | ^~~~~ %Error: t/t_class_extern_bad.v:12:6: extern not found that declares 'noproto' 12 | task Base1::noproto(); | ^~~~~ diff --git a/test_regress/t/t_class_extern_bad.v b/test_regress/t/t_class_extern_bad.v index 86b921820..58d6dbbdb 100644 --- a/test_regress/t/t_class_extern_bad.v +++ b/test_regress/t/t_class_extern_bad.v @@ -5,12 +5,12 @@ // SPDX-License-Identifier: CC0-1.0 class Base1; - extern task nodef(); - extern task nodef(); // duplicate + extern task nodef(); + extern task nodef(); // <--- Error: duplicate endclass -task Base1::noproto(); // no such prototype +task Base1::noproto(); // <--- Error: Missing prototype endtask -module t (/*AUTOARG*/); +module t; endmodule diff --git a/test_regress/t/t_constraint_extern_bad.out b/test_regress/t/t_constraint_extern_bad.out index 2e6c8787f..c47d59249 100644 --- a/test_regress/t/t_constraint_extern_bad.out +++ b/test_regress/t/t_constraint_extern_bad.out @@ -1,8 +1,9 @@ -%Error: t/t_constraint_extern_bad.v:8:22: Definition not found for extern prototype 'missing_bad' +%Error-PROTOTYPEMIS: t/t_constraint_extern_bad.v:8:22: Definition not found for extern prototype 'missing_bad' 8 | extern constraint missing_bad; | ^~~~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. + ... For error description see https://verilator.org/warn/PROTOTYPEMIS?v=latest %Error: t/t_constraint_extern_bad.v:11:20: extern not found that declares 'missing_extern' 11 | constraint Packet::missing_extern { } | ^~~~~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to From 75c6745868982c3fc70a6f21f6d2d67aaa6e0b31 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 12 Aug 2025 19:32:56 -0400 Subject: [PATCH 165/252] Test driver.py: Pass-through +verilator+ arguments to runtime --- docs/internals.rst | 8 ++++++++ test_regress/driver.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/internals.rst b/docs/internals.rst index a572a67f6..99295c218 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -2109,6 +2109,14 @@ the regression tests with OBJCACHE enabled and in parallel on a machine with many cores. See the -j option and OBJCACHE environment variable. +driver.py Pass-Thru Arguments +----------------------------- + +Arguments not understood by `driver.py` which begin with `+verilator+` are +passed through as Verilated executable arguments. Other not-understood +arguments are passed as Verilator arguments. + + driver.py Non-Scenario Arguments -------------------------------- diff --git a/test_regress/driver.py b/test_regress/driver.py index a5292afe7..5c6f2d8de 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -1543,6 +1543,7 @@ class VlTest: *param['all_run_flags'], ("'" if Args.gdbsim else ""), ] + cmd += self.driver_verilated_flags self.run( cmd=cmd, aslr_off=param['aslr_off'], # Disable address space layour randomization @@ -1578,6 +1579,10 @@ class VlTest: def driver_verilator_flags(self) -> list: return Args.passdown_verilator_flags + @property + def driver_verilated_flags(self) -> list: + return Args.passdown_verilated_flags + @property def get_default_vltmt_threads(self) -> int: return Vltmt_Threads @@ -2734,6 +2739,8 @@ def _parameter(param: str) -> None: sys.exit("%Error: Expected number following " + _Parameter_Next_Level + ": " + param) Args.passdown_verilator_flags.append(param) _Parameter_Next_Level = None + elif re.match(r'^(\+verilator\+.*)', param): + Args.passdown_verilated_flags.append(param) elif re.search(r'\.py', param): Arg_Tests.append(param) elif re.match(r'^-?(-debugi|-dumpi)', param): @@ -2891,6 +2898,7 @@ if __name__ == '__main__': (Args, rest) = parser.parse_known_intermixed_args() Args.passdown_verilator_flags = [] + Args.passdown_verilated_flags = [] for arg in rest: _parameter(arg) From 047a12cc62902940c6b6d797916203fcf147bcc3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 13 Aug 2025 18:05:37 -0400 Subject: [PATCH 166/252] Fix variables hiding package imports (#6289). --- Changes | 3 +- src/V3LinkDot.cpp | 2 +- test_regress/t/t_package_import_override.py | 16 +++++++ test_regress/t/t_package_import_override.v | 49 +++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_package_import_override.py create mode 100644 test_regress/t/t_package_import_override.v diff --git a/Changes b/Changes index 3711067e3..cabe4e2ac 100644 --- a/Changes +++ b/Changes @@ -73,6 +73,7 @@ Verilator 5.039 devel * Fix dynamic cast purity (#6267). [Igor Zaworski, Antmicro Ltd.] * Fix same variable on the RHS forced to two different LHSs. (#6269). [Artur Bieniek, Antmicro Ltd.] * Fix spurious VPI value change callbacks (#6274). [Todd Strader] +* Fix variables hiding package imports (#6289). [Johan Wouters] Verilator 5.038 2025-07-08 @@ -904,7 +905,7 @@ Verilator 5.016 2023-09-16 * Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd] * Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov] * Fix internal error on real conversion (#4447). [vdhotre-ventana] -* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra] +* Fix lifetime unknown error on enum.name (#4448). [Johan Wouters] * Fix unstable output of VHashSha256 (#4453). [Anthony Donlon] * Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd] * Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov] diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 6a53003d4..e6987da00 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1462,7 +1462,7 @@ class LinkDotFindVisitor final : public VNVisitor { bool ins = false; if (!foundp) { ins = true; - } else if (!findvarp && m_curSymp->findIdFlat(nodep->name())) { + } else if (!findvarp && m_curSymp->findIdFlat(nodep->name()) && !foundp->imported()) { nodep->v3error("Unsupported in C: Variable has same name as " << LinkDotState::nodeTextType(foundp->nodep()) << ": " << nodep->prettyNameQ()); diff --git a/test_regress/t/t_package_import_override.py b/test_regress/t/t_package_import_override.py new file mode 100755 index 000000000..6305a4fd8 --- /dev/null +++ b/test_regress/t/t_package_import_override.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('linter') + +test.compile(verilator_flags2=['--timing']) + +test.passes() diff --git a/test_regress/t/t_package_import_override.v b/test_regress/t/t_package_import_override.v new file mode 100644 index 000000000..3c3e5d10c --- /dev/null +++ b/test_regress/t/t_package_import_override.v @@ -0,0 +1,49 @@ +// 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 + +package pkg; + + typedef enum logic [1:0] { + INT, + BLA, + DUMMY + } t_shadowed_enum; + +endpackage + +module sub + import pkg::*; +( + input logic INT, // This is also in the pkg::t_shadowed_enum, but it shadows it + output logic dummy_out +); + + assign dummy_out = !INT; +endmodule + +module t; + logic my_wire; + logic dummy_out; + + sub i_sub ( + .INT(my_wire), + .dummy_out(dummy_out) + ); + + initial begin + my_wire = 1'b0; + + repeat (2) begin + my_wire = ~my_wire; + #1ns; + $display("my_wire = %b, dummy_out = %b", my_wire, dummy_out); + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From e753480b19e08b1e98750a43a93dff768fa078f6 Mon Sep 17 00:00:00 2001 From: Mateusz Gancarz <67756666+magancarz@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:19:33 +0200 Subject: [PATCH 167/252] Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290) --- include/verilated_random.h | 2 +- test_regress/t/t_randomize_queue_wide.py | 21 ++++++++++++ test_regress/t/t_randomize_queue_wide.v | 38 +++++++++++++++++++++ test_regress/t/t_randomize_unpacked_wide.py | 21 ++++++++++++ test_regress/t/t_randomize_unpacked_wide.v | 34 ++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_randomize_queue_wide.py create mode 100644 test_regress/t/t_randomize_queue_wide.v create mode 100755 test_regress/t/t_randomize_unpacked_wide.py create mode 100644 test_regress/t/t_randomize_unpacked_wide.v diff --git a/include/verilated_random.h b/include/verilated_random.h index bc32c73c7..b3bea7147 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -408,7 +408,7 @@ public: // Record a flat (non-class) element into the array variable table template - typename std::enable_if::value, void>::type + typename std::enable_if::value || VlIsVlWide::value, void>::type record_arr_table(T& var, const std::string& name, int dimension, std::vector indices, std::vector idxWidths) { const std::string key = generateKey(name, m_index); diff --git a/test_regress/t/t_randomize_queue_wide.py b/test_regress/t/t_randomize_queue_wide.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_queue_wide.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_queue_wide.v b/test_regress/t/t_randomize_queue_wide.v new file mode 100644 index 000000000..7fbb34516 --- /dev/null +++ b/test_regress/t/t_randomize_queue_wide.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; + rand bit [65:0] m_wideQueue[$]; + + function new; + m_wideQueue = '{3{0}}; + endfunction + + constraint int_queue_c { + m_wideQueue[0] == 0; + m_wideQueue[1] == 1; + m_wideQueue[2] == 2; + } + function void self_check(); + if (m_wideQueue[0] != 0) $stop; + if (m_wideQueue[1] != 1) $stop; + if (m_wideQueue[2] != 2) $stop; + endfunction +endclass + +module t; + int success; + initial begin + Foo foo = new; + success = foo.randomize(); + if (success != 1) $stop; + foo.self_check(); + + $display("Queue: %p", foo.m_wideQueue); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_unpacked_wide.py b/test_regress/t/t_randomize_unpacked_wide.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_unpacked_wide.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_unpacked_wide.v b/test_regress/t/t_randomize_unpacked_wide.v new file mode 100644 index 000000000..07d85986d --- /dev/null +++ b/test_regress/t/t_randomize_unpacked_wide.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; + rand bit [65:0] m_wideUnpacked[3]; + + constraint int_queue_c { + m_wideUnpacked[0] == 0; + m_wideUnpacked[1] == 1; + m_wideUnpacked[2] == 2; + } + function void self_check(); + if (m_wideUnpacked[0] != 0) $stop; + if (m_wideUnpacked[1] != 1) $stop; + if (m_wideUnpacked[2] != 2) $stop; + endfunction +endclass + +module t; + int success; + initial begin + Foo foo = new; + success = foo.randomize(); + if (success != 1) $stop; + foo.self_check(); + + $display("Unpacked: %p", foo.m_wideUnpacked); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 9c11f5e05d67bf4a9ce20f9adab5ddf4a039a236 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 15 Aug 2025 08:20:36 +0100 Subject: [PATCH 168/252] Fix DFG circular driver tracing --- src/V3DfgBreakCycles.cpp | 121 ++++++++++++++++++--------- test_regress/t/t_dfg_break_cycles.py | 3 +- test_regress/t/t_dfg_break_cycles.v | 104 +++++++++++++---------- 3 files changed, 144 insertions(+), 84 deletions(-) diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 2aa7f39da..cf9d718cb 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -192,7 +192,7 @@ class TraceDriver final : public DfgVisitor { } template - Vertex* traceBinary(Vertex* vtxp) { + Vertex* traceBitwiseBinary(Vertex* vtxp) { static_assert(std::is_base_of::value, "Should only call on DfgVertexBinary"); if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, m_lsb)) { @@ -206,6 +206,39 @@ class TraceDriver final : public DfgVisitor { return nullptr; } + template + DfgVertex* traceAddSub(Vertex* vtxp) { + static_assert(std::is_base_of::value, + "Should only call on DfgVertexBinary"); + if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, 0)) { + if (DfgVertex* const lp = trace(vtxp->lhsp(), m_msb, 0)) { + Vertex* const opp = make(vtxp, m_msb + 1); + opp->rhsp(rp); + opp->lhsp(lp); + DfgSel* const selp = make(vtxp, m_msb - m_lsb + 1); + selp->fromp(opp); + selp->lsb(m_lsb); + return selp; + } + } + return nullptr; + } + + template + Vertex* traceCmp(Vertex* vtxp) { + static_assert(std::is_base_of::value, + "Should only call on DfgVertexBinary"); + if (DfgVertex* const rp = trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0)) { + if (DfgVertex* const lp = trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, 0)) { + Vertex* const resp = make(vtxp, m_msb - m_lsb + 1); + resp->rhsp(rp); + resp->lhsp(lp); + return resp; + } + } + return nullptr; + } + // Predicate to do determine if vtxp[$bits(vtxp)-1:idx] is known to be zero // Somewhat rudimentary but sufficient for current purposes. static bool knownToBeZeroAtAndAbove(const DfgVertex* vtxp, uint32_t idx) { @@ -356,47 +389,47 @@ class TraceDriver final : public DfgVisitor { void visit(DfgAnd* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceBitwiseBinary(vtxp)); } void visit(DfgOr* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceBitwiseBinary(vtxp)); } void visit(DfgXor* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceBitwiseBinary(vtxp)); } void visit(DfgAdd* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceAddSub(vtxp)); } void visit(DfgSub* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceAddSub(vtxp)); } void visit(DfgEq* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgNeq* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgLt* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgLte* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgGt* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgGte* vtxp) override { if (!m_aggressive) return; - SET_RESULT(traceBinary(vtxp)); + SET_RESULT(traceCmp(vtxp)); } void visit(DfgShiftR* vtxp) override { @@ -1032,39 +1065,45 @@ class FixUpIndependentRanges final { lsb = msb + 1; } - // If we managed to imporove something, apply the replacement - if (nImprovements) { - // Concatenate all the terms to create the replacement - DfgVertex* replacementp = termps.front(); - const uint64_t vComp = vtxp->getUser(); - for (size_t i = 1; i < termps.size(); ++i) { - DfgVertex* const termp = termps[i]; - const uint32_t catWidth = replacementp->width() + termp->width(); - AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(catWidth); - DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; - catp->rhsp(replacementp); - catp->lhsp(termp); - // Need to figure out which component the replacement vertex - // belongs to. If any of the terms are of the original - // component, it's part of that component, otherwise its not - // cyclic (all terms are from outside the original component, - // and feed into the original component). - const uint64_t tComp = termp->getUser(); - const uint64_t rComp = replacementp->getUser(); - catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); - replacementp = catp; - } + // If no imporovement was possible, delete the terms we just created + if (!nImprovements) { + for (DfgVertex* const vtxp : termps) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); + termps.clear(); + return 0; + } - // Replace the vertex - vtxp->replaceWith(replacementp); - // Connect the Sel nodes in the replacement - for (DfgVertex* const termp : termps) { - if (DfgSel* const selp = termp->cast()) { - if (!selp->fromp()) selp->fromp(vtxp); - } + // We managed to imporove something, apply the replacement + // Concatenate all the terms to create the replacement + DfgVertex* replacementp = termps.front(); + const uint64_t vComp = vtxp->getUser(); + for (size_t i = 1; i < termps.size(); ++i) { + DfgVertex* const termp = termps[i]; + const uint32_t catWidth = replacementp->width() + termp->width(); + AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(catWidth); + DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; + catp->rhsp(replacementp); + catp->lhsp(termp); + // Need to figure out which component the replacement vertex + // belongs to. If any of the terms are of the original + // component, it's part of that component, otherwise its not + // cyclic (all terms are from outside the original component, + // and feed into the original component). + const uint64_t tComp = termp->getUser(); + const uint64_t rComp = replacementp->getUser(); + catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); + replacementp = catp; + } + + // Replace the vertex + vtxp->replaceWith(replacementp); + // Connect the Sel nodes in the replacement + for (DfgVertex* const termp : termps) { + if (DfgSel* const selp = termp->cast()) { + if (!selp->fromp()) selp->fromp(vtxp); } } + // Done return nImprovements; } diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index f20d6352b..198c4cd48 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -42,11 +42,12 @@ with open(rdFile, 'r', encoding="utf8") as rdFh, \ open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \ open(checkFile, 'w', encoding="utf8") as checkFh: for line in rdFh: + if "// UNOPTFLAT" in line: + nExpectedCycles += 1 line = line.split("//")[0] m = re.search(r'`signal\((\w+),', line) if not m: continue - nExpectedCycles += 1 sig = m.group(1) plistFh.write(sig + ",\n") pdeclFh.write("output " + sig + ";\n") diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index 996a80bd9..538e4abdc 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -26,56 +26,56 @@ module t ( // Interesting user code to cover ////////////////////////////////////////////////////////////////////////// - `signal(GRAY_SEL, 3); + `signal(GRAY_SEL, 3); // UNOPTFLAT assign GRAY_SEL = rand_a[2:0] ^ 3'(GRAY_SEL[2:1]); - `signal(GRAY_SHIFT, 3); + `signal(GRAY_SHIFT, 3); // UNOPTFLAT assign GRAY_SHIFT = rand_a[2:0] ^ (GRAY_SHIFT >> 1); - `signal(GRAY_REV_SEL, 3); + `signal(GRAY_REV_SEL, 3); // UNOPTFLAT assign GRAY_REV_SEL = rand_a[2:0] ^ {GRAY_REV_SEL[1:0], 1'b0}; - `signal(GRAY_REV_SHIFT, 3); + `signal(GRAY_REV_SHIFT, 3); // UNOPTFLAT assign GRAY_REV_SHIFT = rand_a[2:0] ^ (GRAY_REV_SHIFT << 1); ////////////////////////////////////////////////////////////////////////// // Fill coverage ////////////////////////////////////////////////////////////////////////// - `signal(CONCAT_RHS, 2); + `signal(CONCAT_RHS, 2); // UNOPTFLAT assign CONCAT_RHS[0] = rand_a[0]; assign CONCAT_RHS[1] = CONCAT_RHS[0]; - `signal(CONCAT_LHS, 2); + `signal(CONCAT_LHS, 2); // UNOPTFLAT assign CONCAT_LHS[0] = CONCAT_LHS[1]; assign CONCAT_LHS[1] = rand_a[1]; - `signal(CONCAT_MID, 3); + `signal(CONCAT_MID, 3); // UNOPTFLAT assign CONCAT_MID[0] = |CONCAT_MID[2:1]; assign CONCAT_MID[2:1] = {rand_a[2], ~rand_a[2]}; - `signal(SEL, 3); + `signal(SEL, 3); // UNOPTFLAT assign SEL[0] = rand_a[4]; assign SEL[1] = SEL[0]; assign SEL[2] = SEL[1]; - `signal(EXTEND, 8); + `signal(EXTEND, 8); // UNOPTFLAT assign EXTEND[0] = rand_a[3]; assign EXTEND[3:1] = 3'(EXTEND[0]); assign EXTEND[4] = EXTEND[1]; assign EXTEND[6:5] = EXTEND[2:1]; assign EXTEND[7] = EXTEND[3]; - `signal(NOT, 3); + `signal(NOT, 3); // UNOPTFLAT assign NOT = ~(rand_a[2:0] ^ 3'(NOT[2:1])); - `signal(AND, 3); + `signal(AND, 3); // UNOPTFLAT assign AND = rand_a[2:0] & 3'(AND[2:1]); - `signal(OR, 3); + `signal(OR, 3); // UNOPTFLAT assign OR = rand_a[2:0] | 3'(OR[2:1]); - `signal(SHIFTR, 14); + `signal(SHIFTR, 14); // UNOPTFLAT assign SHIFTR = { SHIFTR[6:5], // 13:12 SHIFTR[7:6], // 11:10 @@ -84,10 +84,10 @@ module t ( rand_a[3:0] // 3:0 }; - `signal(SHIFTR_VARIABLE, 2); + `signal(SHIFTR_VARIABLE, 2); // UNOPTFLAT assign SHIFTR_VARIABLE = rand_a[1:0] ^ ({1'b0, SHIFTR_VARIABLE[1]} >> rand_b[0]); - `signal(SHIFTL, 14); + `signal(SHIFTL, 14); // UNOPTFLAT assign SHIFTL = { SHIFTL[6:5], // 13:12 SHIFTL[7:6], // 11:10 @@ -96,18 +96,18 @@ module t ( rand_a[3:0] // 3:0 }; - `signal(SHIFTL_VARIABLE, 2); + `signal(SHIFTL_VARIABLE, 2); // UNOPTFLAT assign SHIFTL_VARIABLE = rand_a[1:0] ^ ({SHIFTL_VARIABLE[0], 1'b0} << rand_b[0]); - `signal(VAR_A, 2); + `signal(VAR_A, 2); // UNOPTFLAT wire logic [1:0] VAR_B; assign VAR_A = {rand_a[0], VAR_B[0]}; assign VAR_B = (VAR_A >> 1) ^ 2'(VAR_B[1]); - `signal(REPLICATE, 4); + `signal(REPLICATE, 4); // UNOPTFLAT assign REPLICATE = rand_a[3:0] ^ ({2{REPLICATE[3:2]}} >> 2); - `signal(PARTIAL, 4); + `signal(PARTIAL, 4); // UNOPTFLAT assign PARTIAL[0] = rand_a[0]; // PARTIAL[1] intentionally unconnected assign PARTIAL[3:2] = rand_a[3:2] ^ {PARTIAL[2], PARTIAL[0]}; @@ -115,14 +115,14 @@ module t ( wire [2:0] array_0 [2]; assign array_0[0] = rand_a[2:0]; assign array_0[1] = array_0[0]; - `signal(ARRAY_0, 3); + `signal(ARRAY_0, 3); // UNOPTFLAT assign ARRAY_0 = array_0[1]; wire [2:0] array_1 [1]; assign array_1[0][0] = rand_a[0]; assign array_1[0][1] = array_1[0][0]; assign array_1[0][2] = array_1[0][1]; - `signal(ARRAY_1, 3); + `signal(ARRAY_1, 3); // UNOPTFLAT assign ARRAY_1 = array_1[0]; wire [2:0] array_2a [2]; @@ -132,46 +132,66 @@ module t ( assign array_2a[0][2] = array_2b[1][1]; assign array_2a[1] = array_2a[0]; assign array_2b = array_2a; - `signal(ARRAY_2, 3); + `signal(ARRAY_2, 3); // UNOPTFLAT assign ARRAY_2 = array_2a[0]; wire [2:0] array_3 [2]; assign array_3[0] = rand_a[2:0] ^ array_3[1] >> 1; assign array_3[1] = array_3[0]; - `signal(ARRAY_3, 3); + `signal(ARRAY_3, 3); // UNOPTFLAT assign ARRAY_3 = array_3[0]; - `signal(ADD, 8); - assign ADD = ((ADD << 4) + 8'(rand_a[3:0])); + `signal(ADD_A, 8); // UNOPTFLAT + `signal(ADD_B, 8); + `signal(ADD_C, 8); + assign ADD_C = rand_b[7:0] + ADD_B; + assign ADD_B = (ADD_A << 4) + rand_a[7:0]; + assign ADD_A = {ADD_C[7], 7'd0}; - `signal(SUB, 8); - assign SUB = ((SUB << 4) - 8'(rand_a[3:0])); + `signal(SUB_A, 8); // UNOPTFLAT + `signal(SUB_B, 8); + `signal(SUB_C, 8); + assign SUB_C = rand_b[7:0] - SUB_B; + assign SUB_B = (SUB_A << 4) - rand_a[7:0]; + assign SUB_A = {SUB_C[7], 7'd0}; - `signal(EQ, 2); - assign EQ = {rand_a[0], EQ >> 1 == rand_b[1:0]}; + `signal(EQ_A, 1); // UNOPTFLAT + `signal(EQ_B, 3); + assign EQ_A = EQ_B >> 1 == rand_b[2:0]; + assign EQ_B = {rand_a[1:0], EQ_A}; - `signal(NEQ, 2); - assign NEQ = {rand_a[0], NEQ >> 1 != rand_b[1:0]}; + `signal(NEQ_A, 1); // UNOPTFLAT + `signal(NEQ_B, 3); + assign NEQ_A = NEQ_B >> 1 != rand_b[2:0]; + assign NEQ_B = {rand_a[1:0], NEQ_A}; - `signal(LT, 2); - assign LT = {rand_a[0], LT >> 1 < rand_b[1:0]}; + `signal(LT_A, 1); // UNOPTFLAT + `signal(LT_B, 3); + assign LT_A = LT_B >> 1 < rand_b[2:0]; + assign LT_B = {rand_a[1:0], LT_A}; - `signal(LTE, 2); - assign LTE = {rand_a[0], LTE >> 1 <= rand_b[1:0]}; + `signal(LTE_A, 1); // UNOPTFLAT + `signal(LTE_B, 3); + assign LTE_A = LTE_B >> 1 <= rand_b[2:0]; + assign LTE_B = {rand_a[1:0], LTE_A}; - `signal(GT, 2); - assign GT = {rand_a[0], GT >> 1 > rand_b[1:0]}; + `signal(GT_A, 1); // UNOPTFLAT + `signal(GT_B, 3); + assign GT_A = GT_B >> 1 > rand_b[2:0]; + assign GT_B = {rand_a[1:0], GT_A}; - `signal(GTE, 2); - assign GTE = {rand_a[0], GTE >> 1 >= rand_b[1:0]}; + `signal(GTE_A, 1); // UNOPTFLAT + `signal(GTE_B, 3); + assign GTE_A = GTE_B >> 1 >= rand_b[2:0]; + assign GTE_B = {rand_a[1:0], GTE_A}; - `signal(COND_THEN, 3); + `signal(COND_THEN, 3); // UNOPTFLAT assign COND_THEN = {rand_a[0], rand_a[0] ? 2'(COND_THEN << 2) : rand_b[1:0]}; - `signal(COND_ELSE, 3); + `signal(COND_ELSE, 3); // UNOPTFLAT assign COND_ELSE = {rand_a[0], rand_a[0] ? rand_b[1:0] : 2'(COND_ELSE << 2)}; - `signal(COND_COND, 3); + `signal(COND_COND, 3); // UNOPTFLAT assign COND_COND = {rand_a[0], (COND_COND >> 2) == 3'b001 ? rand_b[3:2] : rand_b[1:0]}; endmodule From ece44698697f0fd93ead5411e9ca02716fee3a3a Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sat, 16 Aug 2025 02:25:32 +0800 Subject: [PATCH 169/252] Fix PowerPC support (#6292) --- docs/CONTRIBUTORS | 1 + include/verilatedos.h | 4 +++- include/verilatedos_c.h | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index e30c878d2..ba0af2f62 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -214,6 +214,7 @@ Ryszard Rozak Samuel Riedel Sean Cross Sebastien Van Cauwenberghe +Sergey Fedorov Sergi Granell Seth Pellegrino Shou-Li Hsu diff --git a/include/verilatedos.h b/include/verilatedos.h index 6b289292f..7894a4510 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -575,7 +575,9 @@ static inline double VL_ROUND(double n) { # define VL_CPU_RELAX() asm volatile("nop" ::: "memory") #elif defined(__mips64el__) || defined(__mips__) || defined(__mips64__) || defined(__mips64) # define VL_CPU_RELAX() asm volatile("pause" ::: "memory") -#elif defined(__powerpc64__) +#elif defined(__POWERPC__) && defined(__APPLE__) // First check for a special case of macOS +# define VL_CPU_RELAX() asm volatile("or r1, r1, r1; or r2, r2, r2;" ::: "memory") +#elif defined(__powerpc64__) || defined(__powerpc__) // Generic powerpc # define VL_CPU_RELAX() asm volatile("or 1, 1, 1; or 2, 2, 2;" ::: "memory") #elif defined(__riscv) // RiscV does not currently have yield/pause, but one is proposed # define VL_CPU_RELAX() asm volatile("nop" ::: "memory") diff --git a/include/verilatedos_c.h b/include/verilatedos_c.h index 2b4619816..849419701 100644 --- a/include/verilatedos_c.h +++ b/include/verilatedos_c.h @@ -38,7 +38,7 @@ #if defined(__linux) # include // For sched_getcpu() #endif -#if defined(__APPLE__) && !defined(__arm64__) +#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__) # include // For __cpuid_count() #endif // clang-format on @@ -88,7 +88,7 @@ double DeltaWallTime::gettime() VL_MT_SAFE { uint16_t getcpu() VL_MT_SAFE { #if defined(__linux) return sched_getcpu(); // TODO: this is a system call. Not exactly cheap. -#elif defined(__APPLE__) && !defined(__arm64__) +#elif defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__) uint32_t info[4]; __cpuid_count(1, 0, info[0], info[1], info[2], info[3]); // info[1] is EBX, bits 24-31 are APIC ID From d273e2cbd015c418067d8fdf158a97600c5d9f9c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 15 Aug 2025 19:23:16 +0100 Subject: [PATCH 170/252] Internals: Do not astgen useless Dfg vertex subtypes --- src/V3DfgAstToDfg.cpp | 31 +------- src/V3DfgDfgToAst.cpp | 41 ----------- src/V3DfgPeephole.cpp | 20 ------ src/astgen | 114 ++++++++++++++++++++++++++++++ test_regress/t/t_dfg_4104.py | 16 ----- test_regress/t/t_dfg_4104.v | 12 ---- test_regress/t/t_dfg_peephole.v | 5 -- test_regress/t/t_dfg_unhandled.py | 19 ----- test_regress/t/t_dfg_unhandled.v | 16 ----- 9 files changed, 116 insertions(+), 158 deletions(-) delete mode 100755 test_regress/t/t_dfg_4104.py delete mode 100644 test_regress/t/t_dfg_4104.v delete mode 100755 test_regress/t/t_dfg_unhandled.py delete mode 100644 test_regress/t/t_dfg_unhandled.v diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 58b6166ee..e9379dca0 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -50,38 +50,11 @@ DfgArraySel* makeVertex(const AstArraySel* nodep, DfgG // Some earlier passes create malformed ArraySels, just bail on those... // See t_bitsel_wire_array_bad if (VN_IS(nodep->fromp(), Const)) return nullptr; - AstUnpackArrayDType* const fromDtypep - = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType); - if (!fromDtypep) return nullptr; + if (!VN_IS(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) return nullptr; return new DfgArraySel{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; } -//====================================================================== -// Currently unhandled nodes -// LCOV_EXCL_START -// AstCCast changes width, but should not exists where DFG optimization is currently invoked -template <> -DfgCCast* makeVertex(const AstCCast*, DfgGraph&) { - return nullptr; -} -// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway -template <> -DfgAtoN* makeVertex(const AstAtoN*, DfgGraph&) { - return nullptr; -} -// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway -template <> -DfgCompareNN* makeVertex(const AstCompareNN*, DfgGraph&) { - return nullptr; -} -// Unhandled in DfgToAst, but also operates on unpacked arrays which we don't optimize anyway -template <> -DfgSliceSel* makeVertex(const AstSliceSel*, DfgGraph&) { - return nullptr; -} -// LCOV_EXCL_STOP - -} // namespace +} //namespace // Visitor that can convert combinational Ast logic constructs/assignments to Dfg template diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 6a557e088..a96af5864 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -52,15 +52,6 @@ T_Node* makeNode(const T_Vertex* vtxp, Ops... ops) { //====================================================================== // Vertices needing special conversion -template <> -AstCountOnes* makeNode( // - const DfgCountOnes* vtxp, AstNodeExpr* op1) { - AstCountOnes* const nodep = new AstCountOnes{vtxp->fileline(), op1}; - // Set dtype same as V3Width - nodep->dtypeSetLogicSized(32, VSigning::UNSIGNED); - return nodep; -} - template <> AstExtend* makeNode( // const DfgExtend* vtxp, AstNodeExpr* op1) { @@ -91,38 +82,6 @@ AstShiftRS* makeNode( // return new AstShiftRS{vtxp->fileline(), op1, op2, static_cast(vtxp->width())}; } -//====================================================================== -// Currently unhandled nodes - see corresponding AstToDfg functions -// LCOV_EXCL_START -template <> -AstCCast* makeNode(const DfgCCast* vtxp, AstNodeExpr*) { - vtxp->v3fatalSrc("not implemented"); - VL_UNREACHABLE; - return nullptr; // LCOV_EXCL_LINE -} -template <> -AstAtoN* makeNode(const DfgAtoN* vtxp, AstNodeExpr*) { - vtxp->v3fatalSrc("not implemented"); - VL_UNREACHABLE; - return nullptr; // LCOV_EXCL_LINE -} -template <> -AstCompareNN* -makeNode(const DfgCompareNN* vtxp, - AstNodeExpr*, AstNodeExpr*) { - vtxp->v3fatalSrc("not implemented"); - VL_UNREACHABLE; - return nullptr; // LCOV_EXCL_LINE -} -template <> -AstSliceSel* makeNode( - const DfgSliceSel* vtxp, AstNodeExpr*, AstNodeExpr*, AstNodeExpr*) { - vtxp->v3fatalSrc("not implemented"); - VL_UNREACHABLE; - return nullptr; // LCOV_EXCL_LINE -} -// LCOV_EXCL_STOP - } // namespace template diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index a6e990029..deadbf96f 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -79,15 +79,11 @@ using BitwiseToReduction = typename BitwiseToReductionImpl::type; namespace { template void foldOp(V3Number& out, const V3Number& src); -template <> void foldOp (V3Number& out, const V3Number& src) { out.opCLog2(src); } -template <> void foldOp (V3Number& out, const V3Number& src) { out.opCountOnes(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opAssign(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opExtendS(src, src.width()); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opLogNot(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opNegate(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opNot(src); } -template <> void foldOp (V3Number& out, const V3Number& src) { out.opOneHot(src); } -template <> void foldOp (V3Number& out, const V3Number& src) { out.opOneHot0(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opRedAnd(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opRedOr(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opRedXor(src); } @@ -665,14 +661,6 @@ class V3DfgPeephole final : public DfgVisitor { // DfgVertexUnary //========================================================================= - void visit(DfgCLog2* vtxp) override { - if (foldUnary(vtxp)) return; - } - - void visit(DfgCountOnes* vtxp) override { - if (foldUnary(vtxp)) return; - } - void visit(DfgExtend* vtxp) override { UASSERT_OBJ(vtxp->width() > vtxp->srcp()->width(), vtxp, "Invalid zero extend"); @@ -768,14 +756,6 @@ class V3DfgPeephole final : public DfgVisitor { } } - void visit(DfgOneHot* vtxp) override { - if (foldUnary(vtxp)) return; - } - - void visit(DfgOneHot0* vtxp) override { - if (foldUnary(vtxp)) return; - } - void visit(DfgRedOr* vtxp) override { if (optimizeReduction(vtxp)) return; } diff --git a/src/astgen b/src/astgen index 8d64b12c7..9af3a1746 100755 --- a/src/astgen +++ b/src/astgen @@ -1356,6 +1356,116 @@ DfgVertices["Vertex"].addSubClass(DfgVertices["VertexTernary"]) DfgVertices["VertexVariadic"] = Node("VertexVariadic", DfgVertices["Vertex"]) DfgVertices["Vertex"].addSubClass(DfgVertices["VertexVariadic"]) +# AstNodeExpr that are not representable in Dfg +DfgIgnored = ( + # Floating point operations + "AcosD", + "AcoshD", + "AddD", + "AsinD", + "AsinhD", + "Atan2D", + "AtanD", + "AtanhD", + "BitsToRealD", + "CeilD", + "CosD", + "CoshD", + "DivD", + "EqD", + "ExpD", + "FloorD", + "GtD", + "GteD", + "HypotD", + "ISToRD", + "IToRD", + "Log10D", + "LogD", + "LtD", + "LteD", + "MulD", + "NegateD", + "NeqD", + "PowD", + "RealToBits", + "RToIRoundS", + "RToIS", + "SinD", + "SinhD", + "SqrtD", + "SubD", + "TanD", + "TanhD", + # String operations + "AtoN", + "CompareNN", + "ConcatN", + "CvtPackString", + "EqN", + "GetcN", + "GetcRefN", + "GteN", + "GtN", + "LenN", + "LteN", + "LtN", + "NeqN", + "NToI", + "PutcN", + "ReplicateN", + "SubstrN", + "ToLowerN", + "ToUpperN", + # Effectful + "PostAdd", + "PostSub", + "PreAdd", + "PreSub", + # Only used after DFG + "ShiftLOvr", + "ShiftROvr", + "ShiftRSOvr", + "WordSel", + # File operations + "FEof", + "FGetC", + "FGetS", + "FUngetC", + # Dynamic array operations + "AssocSel", + "IsUnbounded", + "WildcardSel", + # Type comparison + "EqT", + "NeqT", + # Distributions + "DistChiSquare", + "DistErlang", + "DistExponential", + "DistNormal", + "DistPoisson", + "DistT", + "DistUniform", + # Specials + "CastDynamic", + "CastWrap", + "CAwait", + "CCast", + "CLog2", + "CountOnes", + "IsUnknown", + "NullCheck", + "OneHot", + "OneHot0", + "ResizeLValue", + "Signed", + "SliceSel", + "TimeImport", + "Unsigned", + "URandomRange", +) + # Read DfgVertex definitions for filename in Args.dfgdef: read_types(os.path.join(Args.I, filename), DfgVertices, "Dfg") @@ -1370,6 +1480,10 @@ for node in AstNodeList: if node.name in DfgVertices: continue + # Ignore expressions types that DFG cannot handle + if node.name in DfgIgnored: + continue + if node.isSubClassOf(AstNodes["NodeUniop"]): base = DfgVertices["VertexUnary"] elif node.isSubClassOf(AstNodes["NodeBiop"]): diff --git a/test_regress/t/t_dfg_4104.py b/test_regress/t/t_dfg_4104.py deleted file mode 100755 index 0b27c3dc0..000000000 --- a/test_regress/t/t_dfg_4104.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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.compile() - -test.passes() diff --git a/test_regress/t/t_dfg_4104.v b/test_regress/t/t_dfg_4104.v deleted file mode 100644 index 508f93a57..000000000 --- a/test_regress/t/t_dfg_4104.v +++ /dev/null @@ -1,12 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2023 by Geza Lore. -// SPDX-License-Identifier: CC0-1.0 - -module v(input logic t); -endmodule - -module top(input logic [2:0] c); - v v1((int'(c) + int'($countones(c))) > 2); -endmodule diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index 1789a7b9d..6392ded77 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -43,14 +43,9 @@ module t ( array[2][7:4] = rand_a[7:4]; end - `signal(FOLD_UNARY_CLog2, $clog2(const_a)); - `signal(FOLD_UNARY_CountOnes, $countones(const_a)); - `signal(FOLD_UNARY_IsUnknown, $isunknown(const_a)); `signal(FOLD_UNARY_LogNot, !const_a[0]); `signal(FOLD_UNARY_Negate, -const_a); `signal(FOLD_UNARY_Not, ~const_a); - `signal(FOLD_UNARY_OneHot, $onehot(const_a)); - `signal(FOLD_UNARY_OneHot0, $onehot0(const_a)); `signal(FOLD_UNARY_RedAnd, &const_a); `signal(FOLD_UNARY_RedOr, |const_a); `signal(FOLD_UNARY_RedXor, ^const_a); diff --git a/test_regress/t/t_dfg_unhandled.py b/test_regress/t/t_dfg_unhandled.py deleted file mode 100755 index 1bb7d5c4a..000000000 --- a/test_regress/t/t_dfg_unhandled.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/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.compile(verilator_flags2=["--stats"]) - -test.file_grep(test.stats, - r'Optimizations, DFG pre inline AstToDfg, non-representable \(impure\)\s+(\d+)', 1) - -test.passes() diff --git a/test_regress/t/t_dfg_unhandled.v b/test_regress/t/t_dfg_unhandled.v deleted file mode 100644 index f01edfeff..000000000 --- a/test_regress/t/t_dfg_unhandled.v +++ /dev/null @@ -1,16 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2022 by Geza Lore. -// SPDX-License-Identifier: CC0-1.0 - -module t ( - input wire clk, - output wire [31:0] o0 - ); - - int file; - - assign o0 = $fgetc(file); // Impure - -endmodule From 6f69c990fdc554a001431d0d17ab3d3a6a15d321 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 15 Aug 2025 23:49:06 +0100 Subject: [PATCH 171/252] Internals: Remove AstCondBound and AstNodeCond (#6293) (#6294) Fixes #6293 --- src/V3AstNodeExpr.h | 71 +++++++++++++++----------------------------- src/V3AstNodes.cpp | 13 ++------ src/V3Cast.cpp | 2 +- src/V3Clean.cpp | 2 +- src/V3Const.cpp | 28 ++++++++--------- src/V3EmitCFunc.h | 2 +- src/V3EmitV.cpp | 2 +- src/V3Expand.cpp | 22 ++++---------- src/V3InstrCount.cpp | 2 +- src/V3LinkInc.cpp | 2 +- src/V3MergeCond.cpp | 12 ++++---- src/V3Premit.cpp | 2 +- src/V3Randomize.cpp | 2 +- src/V3Simulate.h | 2 +- src/V3Slice.cpp | 8 ++--- src/V3StackCount.cpp | 2 +- src/V3Unknown.cpp | 8 ++--- src/V3Width.cpp | 2 +- 18 files changed, 70 insertions(+), 114 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 5b5c17f6d..0a1d5cac1 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -406,31 +406,6 @@ private: return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure(); } }; -class AstNodeCond VL_NOT_FINAL : public AstNodeTriop { - // @astgen alias op1 := condp - // @astgen alias op2 := thenp - // @astgen alias op3 := elsep -protected: - AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, - AstNodeExpr* elsep); - -public: - ASTGEN_MEMBERS_AstNodeCond; - void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) override; - string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; } - string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; } - string emitSMT() const override { return "(ite (__Vbool %l) %r %t)"; } - bool cleanOut() const override { return false; } // clean if e1 & e2 clean - bool cleanLhs() const override { return true; } - bool cleanRhs() const override { return false; } - bool cleanThs() const override { return false; } // Propagates up - bool sizeMattersLhs() const override { return false; } - bool sizeMattersRhs() const override { return false; } - bool sizeMattersThs() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - virtual AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) = 0; -}; class AstNodeDistTriop VL_NOT_FINAL : public AstNodeTriop { public: AstNodeDistTriop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, @@ -4681,6 +4656,30 @@ public: }; // === AstNodeTriop === + +class AstCond final : public AstNodeTriop { + // @astgen alias op1 := condp + // @astgen alias op2 := thenp + // @astgen alias op3 := elsep +public: + AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep); + ASTGEN_MEMBERS_AstCond; + void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, + const V3Number& ths) override { + out.opAssign(lhs.isNeqZero() ? rhs : ths); + } + string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; } + string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; } + string emitSMT() const override { return "(ite (__Vbool %l) %r %t)"; } + bool cleanOut() const override { return false; } // clean if e1 & e2 clean + bool cleanLhs() const override { return true; } + bool cleanRhs() const override { return false; } + bool cleanThs() const override { return false; } // Propagates up + bool sizeMattersLhs() const override { return false; } + bool sizeMattersRhs() const override { return false; } + bool sizeMattersThs() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } +}; class AstPostAdd final : public AstNodeTriop { // Post-increment/add // Children: lhsp: AstConst (1) as currently support only ++ not += @@ -4859,28 +4858,6 @@ public: bool sizeMattersThs() const override { return false; } }; -// === AstNodeCond === -class AstCond final : public AstNodeCond { - // Conditional ?: expression -public: - AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) - : ASTGEN_SUPER_Cond(fl, condp, thenp, elsep) {} - ASTGEN_MEMBERS_AstCond; - AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) override { - return new AstCond{fileline(), condp, thenp, elsep}; - } -}; -class AstCondBound final : public AstNodeCond { - // Conditional ?: expression, specially made for safety checking of array bounds -public: - AstCondBound(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) - : ASTGEN_SUPER_CondBound(fl, condp, thenp, elsep) {} - ASTGEN_MEMBERS_AstCondBound; - AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) override { - return new AstCondBound{fileline(), condp, thenp, elsep}; - } -}; - // === AstNodeDistTriop === class AstDistErlang final : public AstNodeDistTriop { public: diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 660c6a6c2..9757c04eb 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -166,9 +166,8 @@ const char* AstNodeQuadop::broken() const { return nullptr; } -AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, - AstNodeExpr* elsep) - : AstNodeTriop{t, fl, condp, thenp, elsep} { +AstCond::AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) + : ASTGEN_SUPER_Cond(fl, condp, thenp, elsep) { UASSERT_OBJ(thenp, this, "No thenp expression"); UASSERT_OBJ(elsep, this, "No elsep expression"); if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) { @@ -180,14 +179,6 @@ AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr dtypeFrom(thenp); } } -void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) { - if (lhs.isNeqZero()) { - out.opAssign(rhs); - } else { - out.opAssign(ths); - } -} void AstAddrOfCFunc::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 9a2972e96..24e20d3e1 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -115,7 +115,7 @@ class CastVisitor final : public VNVisitor { if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp()); if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp()); } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { // All class types are castable to each other. If they are of different types, // a compilation error will be thrown, so an explicit cast is required. Types were // already checked by V3Width and dtypep of a condition operator is a type of their diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 35a84f778..0fe863e4d 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -261,7 +261,7 @@ class CleanVisitor final : public VNVisitor { } // Control flow operators - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { iterateChildren(nodep); ensureClean(nodep->condp()); setClean(nodep, isClean(nodep->thenp()) && isClean(nodep->elsep())); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ec73e7694..42f5ed1e1 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1099,17 +1099,17 @@ class ConstVisitor final : public VNVisitor { // (otherwise we'd be trading one operation for two operations) // V3Clean often makes this pattern, as it postpones the AND until // as high as possible, which is usually the right choice, except for this. - AstNodeCond* const condp = VN_CAST(nodep->rhsp(), NodeCond); + AstCond* const condp = VN_CAST(nodep->rhsp(), Cond); if (!condp) return false; if (!VN_IS(condp->thenp(), Const) && !VN_IS(condp->elsep(), Const)) return false; AstConst* const maskp = VN_CAST(nodep->lhsp(), Const); if (!maskp) return false; UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep); - AstNodeCond* const newp = static_cast(condp->cloneType( - condp->condp()->unlinkFrBack(), + AstCond* const newp = new AstCond{ + condp->fileline(), condp->condp()->unlinkFrBack(), new AstAnd{nodep->fileline(), maskp->cloneTree(false), condp->thenp()->unlinkFrBack()}, new AstAnd{nodep->fileline(), maskp->cloneTree(false), - condp->elsep()->unlinkFrBack()})); + condp->elsep()->unlinkFrBack()}}; newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths newp->elsep()->dtypeFrom(nodep); nodep->replaceWithKeepDType(newp); @@ -3658,20 +3658,20 @@ class ConstVisitor final : public VNVisitor { TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b) // Trinary ops // Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump! - TREEOP ("AstNodeCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)"); - TREEOP ("AstNodeCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)"); - TREEOPA("AstNodeCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)"); - TREEOPA("AstNodeCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)"); - TREEOP ("AstNodeCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)"); + TREEOP ("AstCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)"); + TREEOP ("AstCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)"); + TREEOPA("AstCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)"); + TREEOPA("AstCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)"); + TREEOP ("AstCond{$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 - TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); + TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b + TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b + TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b + TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b + TREEOP ("AstCond{!$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 TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)"); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 2cadbbcf5..2c1c92d1c 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1349,7 +1349,7 @@ public: iterateAndNextConstNull(nodep->lhsp()); puts(")"); } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { // Widths match up already, so we'll just use C++'s operator w/o any temps. if (nodep->thenp()->isWide()) { emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->thenp(), nodep->elsep()); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 539bd39cd..46f979200 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -616,7 +616,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } puts("}"); } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { putbs("("); iterateAndNextConstNull(nodep->condp()); putfs(nodep, " ? "); diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 952eb2d40..24bedd325 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -221,7 +221,7 @@ class ExpandVisitor final : public VNVisitor { // e.g. "logic [95:0] var[0]; logic [0] sel; out = var[sel];" // Squash before C++ to avoid getting a C++ compiler warning // (even though code would be unreachable as presumably a - // AstCondBound is protecting above this node. + // AstCond is protecting above this node. return new AstConst{fl, AstConst::SizedEData{}, 0}; } else { AstNodeExpr* wordp; @@ -240,25 +240,13 @@ class ExpandVisitor final : public VNVisitor { } } - static AstNodeExpr* dropCondBound(AstNodeExpr* nodep) { - // Experimental only... - // If there's a CONDBOUND safety to keep arrays in bounds, - // we're going to AND it to a value that always fits inside a - // word, so we don't need it. - // if (VN_IS(nodep, CondBound) && VN_IS(VN_AS(nodep, CondBound)->lhsp(), Lte)) { - // nodep = VN_AS(nodep, CondBound)->rhsp(); - //} - return nodep; - } - static AstNodeExpr* newSelBitBit(AstNodeExpr* lsbp) { // Return equation to get the VL_BITBIT of a constant or non-constant FileLine* const fl = lsbp->fileline(); if (VN_IS(lsbp, Const)) { return new AstConst{fl, VL_BITBIT_E(VN_AS(lsbp, Const)->toUInt())}; } else { - return new AstAnd{fl, new AstConst{fl, VL_EDATASIZE - 1}, - dropCondBound(lsbp)->cloneTreePure(true)}; + return new AstAnd{fl, new AstConst{fl, VL_EDATASIZE - 1}, lsbp->cloneTreePure(true)}; } } @@ -343,7 +331,7 @@ class ExpandVisitor final : public VNVisitor { return true; } //-------- Triops - bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { + bool expandWide(AstNodeAssign* nodep, AstCond* rhsp) { UINFO(8, " Wordize ASSIGN(COND) " << nodep); if (!doExpandWide(nodep)) return false; FileLine* const fl = nodep->fileline(); @@ -498,7 +486,7 @@ class ExpandVisitor final : public VNVisitor { AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack(); if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast{fl, fromp, nodep}; // {large}>>32 requires 64-bit shift operation; then cast - AstNodeExpr* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; + AstNodeExpr* newp = new AstShiftR{fl, fromp, lsbp, fromp->width()}; newp->dtypeFrom(fromp); if (!nodep->isQuad() && fromp->isQuad()) newp = new AstCCast{fl, newp, nodep}; newp->dtypeFrom(nodep); @@ -944,7 +932,7 @@ class ExpandVisitor final : public VNVisitor { did = expandWide(nodep, rhsp); } else if (AstXor* const rhsp = VN_CAST(nodep->rhsp(), Xor)) { did = expandWide(nodep, rhsp); - } else if (AstNodeCond* const rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { + } else if (AstCond* const rhsp = VN_CAST(nodep->rhsp(), Cond)) { did = expandWide(nodep, rhsp); } } else if (AstSel* const lhsp = VN_CAST(nodep->lhsp(), Sel)) { diff --git a/src/V3InstrCount.cpp b/src/V3InstrCount.cpp index 15851c9c4..653db37e2 100644 --- a/src/V3InstrCount.cpp +++ b/src/V3InstrCount.cpp @@ -188,7 +188,7 @@ private: if (nodep->thensp()) nodep->thensp()->user2(0); // Don't dump it } } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { if (m_ignoreRemaining) return; // Just like if/else above, the ternary operator only evaluates // one of the two expressions, so only count the max. diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index f8c08fb51..2b60ad92c 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -189,7 +189,7 @@ class LinkIncVisitor final : public VNVisitor { void visit(AstLogOr* nodep) override { unsupported_visit(nodep); } void visit(AstLogEq* nodep) override { unsupported_visit(nodep); } void visit(AstLogIf* nodep) override { unsupported_visit(nodep); } - void visit(AstNodeCond* nodep) override { unsupported_visit(nodep); } + void visit(AstCond* nodep) override { unsupported_visit(nodep); } void visit(AstPropSpec* nodep) override { unsupported_visit(nodep); } void prepost_visit(AstNodeTriop* nodep) { // Check if we are underneath a statement diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 4860affd6..43c647cbc 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -95,11 +95,11 @@ namespace { // if there is one and it is in a supported position, which are: // - RHS is the Cond // - RHS is And(Const, Cond). This And is inserted often by V3Clean. -AstNodeCond* extractCondFromRhs(AstNode* rhsp) { - if (AstNodeCond* const condp = VN_CAST(rhsp, NodeCond)) { +AstCond* extractCondFromRhs(AstNode* rhsp) { + if (AstCond* const condp = VN_CAST(rhsp, Cond)) { return condp; } else if (const AstAnd* const andp = VN_CAST(rhsp, And)) { - if (AstNodeCond* const condp = VN_CAST(andp->rhsp(), NodeCond)) { + if (AstCond* const condp = VN_CAST(andp->rhsp(), Cond)) { if (VN_IS(andp->lhsp(), Const)) return condp; } } @@ -177,7 +177,7 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst { static AstNodeExpr* extractCondition(const AstNodeStmt* nodep) { AstNodeExpr* conditionp = nullptr; if (const AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { - if (AstNodeCond* const conditionalp = extractCondFromRhs(assignp->rhsp())) { + if (AstCond* const conditionalp = extractCondFromRhs(assignp->rhsp())) { conditionp = conditionalp->condp(); } } else if (const AstNodeIf* const ifp = VN_CAST(nodep, NodeIf)) { @@ -564,7 +564,7 @@ class MergeCondVisitor final : public VNVisitor { return yieldsOneOrZero(biopp->lhsp()) && yieldsOneOrZero(biopp->rhsp()); return false; } - if (const AstNodeCond* const condp = VN_CAST(nodep, NodeCond)) { + if (const AstCond* const condp = VN_CAST(nodep, Cond)) { return yieldsOneOrZero(condp->thenp()) && yieldsOneOrZero(condp->elsep()); } if (const AstCCast* const castp = VN_CAST(nodep, CCast)) { @@ -591,7 +591,7 @@ class MergeCondVisitor final : public VNVisitor { AstNodeExpr* foldAndUnlink(AstNodeExpr* rhsp, bool condTrue) { if (rhsp->sameTree(m_mgCondp)) { return new AstConst{rhsp->fileline(), AstConst::BitTrue{}, condTrue}; - } else if (const AstNodeCond* const condp = extractCondFromRhs(rhsp)) { + } else if (const AstCond* const condp = extractCondFromRhs(rhsp)) { AstNodeExpr* const resp = condTrue ? condp->thenp()->unlinkFrBack() : condp->elsep()->unlinkFrBack(); if (condp == rhsp) return resp; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 1883d0929..f8ac42c0f 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -321,7 +321,7 @@ class PremitVisitor final : public VNVisitor { } checkNode(nodep); } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { iterateChildren(nodep); if (nodep->thenp()->isWide() && !VN_IS(nodep->condp(), Const) && !VN_IS(nodep->condp(), VarRef)) { diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 2d0cdb1c7..f13b07e09 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -766,7 +766,7 @@ class ConstraintExprVisitor final : public VNVisitor { if (editFormat(nodep)) return; editSMT(nodep, nodep->lhsp(), nodep->rhsp(), nodep->thsp()); } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { if (editFormat(nodep)) return; if (!nodep->condp()->user1()) { // Do not burden the solver if cond computable: (cond ? "then" : "else") diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 236dd442a..dc09fef88 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -672,7 +672,7 @@ private: } } } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { // We could use above visit(AstNodeTriop), but need to do short circuiting. // It's also slower even O(n^2) to evaluate both sides when we // really only need to evaluate one side. diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 685334575..f9696d1dd 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -190,11 +190,11 @@ class SliceVisitor final : public VNVisitor { } } if (!newp) newp = new AstConst{nodep->fileline(), 0}; - } else if (AstNodeCond* const snodep = VN_CAST(nodep, NodeCond)) { + } else if (AstCond* const snodep = VN_CAST(nodep, Cond)) { UINFO(9, " cloneCond(" << elements << "," << elemIdx << ") " << nodep); - return snodep->cloneType(snodep->condp()->cloneTree(false, needPure), - cloneAndSel(snodep->thenp(), elements, elemIdx, needPure), - cloneAndSel(snodep->elsep(), elements, elemIdx, needPure)); + return new AstCond{snodep->fileline(), snodep->condp()->cloneTree(false, needPure), + cloneAndSel(snodep->thenp(), elements, elemIdx, needPure), + cloneAndSel(snodep->elsep(), elements, elemIdx, needPure)}; } else if (const AstSliceSel* const snodep = VN_CAST(nodep, SliceSel)) { UINFO(9, " cloneSliceSel(" << elements << "," << elemIdx << ") " << nodep); const int leOffset = (snodep->declRange().lo() diff --git a/src/V3StackCount.cpp b/src/V3StackCount.cpp index ed1789adf..f8bf2d952 100644 --- a/src/V3StackCount.cpp +++ b/src/V3StackCount.cpp @@ -112,7 +112,7 @@ private: if (nodep->thensp()) nodep->thensp()->user2(0); // Don't dump it } } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { if (m_ignoreRemaining) return; // Just like if/else above, the ternary operator only evaluates // one of the two expressions, so only count the max. diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 42fb8d822..657905af8 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -411,7 +411,7 @@ class UnknownVisitor final : public VNVisitor { AstNodeExpr* const xexprp = new AstConst{nodep->fileline(), xnum}; AstNodeExpr* const newp = condp->isZero() ? xexprp - : new AstCondBound{nodep->fileline(), condp, nodep, xexprp}; + : new AstCond{nodep->fileline(), condp, nodep, xexprp}; UINFOTREE(9, newp, "", "_new"); // Link in conditional replaceHandle.relink(newp); @@ -485,8 +485,8 @@ class UnknownVisitor final : public VNVisitor { } else { xnum.setAllBitsX(); } - AstNode* const newp = new AstCondBound{nodep->fileline(), condp, nodep, - new AstConst{nodep->fileline(), xnum}}; + AstNode* const newp = new AstCond{nodep->fileline(), condp, nodep, + new AstConst{nodep->fileline(), xnum}}; UINFOTREE(9, newp, "", "_new"); // Link in conditional, can blow away temp xor replaceHandle.relink(newp); @@ -496,7 +496,7 @@ class UnknownVisitor final : public VNVisitor { // ARRAYSEL(...) -> ARRAYSEL(COND(LT(bitbitp()->unlinkFrBack(&replaceHandle); - AstNodeExpr* const newp = new AstCondBound{ + AstNodeExpr* const newp = new AstCond{ bitp->fileline(), condp, bitp, new AstConst{bitp->fileline(), AstConst::WidthedValue{}, bitp->width(), 0}}; // Added X's, tristate them too diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 9af0997ad..4a51af033 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -492,7 +492,7 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetUInt64(); // A pointer, but not that it matters } - void visit(AstNodeCond* nodep) override { + void visit(AstCond* nodep) override { // op = cond ? expr1 : expr2 // See IEEE-2012 11.4.11 and Table 11-21. // LHS is self-determined From a300bfc538a71242a918d16b520a969c5afdcc9d Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 16 Aug 2025 09:46:18 +0100 Subject: [PATCH 172/252] Internals: Add AstNodeStmt.h (#6295) Move AstNodeStmt and all its subtypes into AstNodeStmt.h. This is the first step for #6280. No functional change --- src/CMakeLists.txt | 9 +- src/Makefile_obj.in | 2 + src/V3Ast.h | 1 + src/V3AstNodeOther.h | 1387 ----------------------------------------- src/V3AstNodeStmt.h | 1420 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1428 insertions(+), 1391 deletions(-) create mode 100644 src/V3AstNodeStmt.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd985af00..bd7b97654 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ set(HEADERS V3AstNodeDType.h V3AstNodeExpr.h V3AstNodeOther.h + V3AstNodeStmt.h V3AstUserAllocator.h V3Begin.h V3Branch.h @@ -372,8 +373,8 @@ add_custom_command( COMMAND ${PYTHON3} ARGS ${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef - V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h - --classes + V3AstNodeExpr.h --astdef V3AstNodeOther.h --astdef V3AstNodeStmt.h + --dfgdef V3DfgVertices.h --classes ) list( APPEND @@ -485,8 +486,8 @@ foreach(astgen_name ${ASTGENERATED_NAMES}) COMMAND ${PYTHON3} ARGS ${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef - V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h - ${astgen_name}.cpp + V3AstNodeExpr.h --astdef V3AstNodeOther.h --astdef V3AstNodeStmt.h + --dfgdef V3DfgVertices.h ${astgen_name}.cpp ) list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp) endforeach() diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index cdec03d91..ed9767907 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -331,6 +331,7 @@ NON_STANDALONE_HEADERS = \ V3AstNodeDType.h \ V3AstNodeExpr.h \ V3AstNodeOther.h \ + V3AstNodeStmt.h \ V3DfgVertices.h \ V3ThreadPool.h \ V3WidthRemove.h \ @@ -339,6 +340,7 @@ AST_DEFS := \ V3AstNodeDType.h \ V3AstNodeExpr.h \ V3AstNodeOther.h \ + V3AstNodeStmt.h \ DFG_DEFS := \ V3DfgVertices.h diff --git a/src/V3Ast.h b/src/V3Ast.h index f4a2842bc..217ff2fa7 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -3153,6 +3153,7 @@ AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) { #include "V3AstNodeDType.h" #include "V3AstNodeExpr.h" #include "V3AstNodeOther.h" +#include "V3AstNodeStmt.h" // Inline function definitions need to go last #include "V3AstInlines.h" diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 7b55b7ce9..65f11ca93 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -347,238 +347,6 @@ public: void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; }; -class AstNodeStmt VL_NOT_FINAL : public AstNode { - // Procedural statement -protected: - AstNodeStmt(VNType t, FileLine* fl) - : AstNode{t, fl} {} - -public: - ASTGEN_MEMBERS_AstNodeStmt; - // METHODS - void addNextStmt(AstNode* newp, - AstNode* belowp) override; // Stop statement searchback here - void dump(std::ostream& str = std::cout) const override; - void dumpJson(std::ostream& str = std::cout) const override; -}; -class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt { - // Iteration is in order, and we want rhsp to be visited first (which is the execution order) - // @astgen op1 := rhsp : AstNodeExpr - // @astgen op2 := lhsp : AstNodeExpr - // @astgen op3 := timingControlp : Optional[AstNode] -protected: - AstNodeAssign(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, - AstNode* timingControlp = nullptr) - : AstNodeStmt{t, fl} { - this->rhsp(rhsp); - this->lhsp(lhsp); - this->timingControlp(timingControlp); - dtypeFrom(lhsp); - } - -public: - ASTGEN_MEMBERS_AstNodeAssign; - // Clone single node, just get same type back. - virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0; - bool hasDType() const override VL_MT_SAFE { return true; } - virtual bool cleanRhs() const { return true; } - int instrCount() const override { return widthInstrs(); } - bool sameNode(const AstNode*) const override { return true; } - string verilogKwd() const override { return "="; } - bool isTimingControl() const override { return timingControlp(); } - virtual bool brokeLhsMustBeLvalue() const = 0; -}; -class AstNodeCase VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression - // @astgen op2 := itemsp : List[AstCaseItem] - // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's -protected: - AstNodeCase(VNType t, FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) - : AstNodeStmt{t, fl} { - this->exprp(exprp); - addItemsp(itemsp); - } - -public: - ASTGEN_MEMBERS_AstNodeCase; - int instrCount() const override { return INSTR_COUNT_BRANCH; } -}; -class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt { - // Coverage analysis point declaration - // - // [After V3CoverageJoin] Duplicate declaration to get data from instead - // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] - string m_page; // Coverage point's page tag - string m_text; // Coverage point's text - string m_hier; // Coverage point's hierarchy - int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment -public: - AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) - : AstNodeStmt(t, fl) - , m_page{page} - , m_text{comment} {} - ASTGEN_MEMBERS_AstNodeCoverDecl; - const char* broken() const override { - if (m_dataDeclp - && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing - v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); - } - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - int binNum() const { return m_binNum; } - void binNum(int flag) { m_binNum = flag; } - virtual int size() const = 0; - const string& comment() const { return m_text; } // text to insert in code - const string& page() const { return m_page; } - const string& hier() const { return m_hier; } - void hier(const string& flag) { m_hier = flag; } - void comment(const string& flag) { m_text = flag; } - bool sameNode(const AstNode* samep) const override { - const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); - return (fileline() == asamep->fileline() && hier() == asamep->hier() - && comment() == asamep->comment() && page() == asamep->page()); - } - bool isPredictOptimizable() const override { return false; } - void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } - // dataDecl nullptr means "use this one", but often you want "this" to - // indicate to get data from here - AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } - AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } -}; -class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { - // Cover or Assert - // Parents: {statement list} - // @astgen op1 := propp : AstNode - // @astgen op2 := sentreep : Optional[AstSenTree] - // op3 used by some sub-types only - // @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly - string m_name; // Name to report - const VAssertType m_type; // Assertion/cover type - const VAssertDirectiveType m_directive; // Assertion directive type - -public: - AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type, - VAssertDirectiveType directive, const string& name = "") - : AstNodeStmt{t, fl} - , m_name{name} - , m_type{type} - , m_directive{directive} { - this->propp(propp); - addPasssp(passsp); - } - ASTGEN_MEMBERS_AstNodeCoverOrAssert; - string name() const override VL_MT_STABLE { return m_name; } // * = Var name - bool sameNode(const AstNode* samep) const override { return samep->name() == name(); } - void name(const string& name) override { m_name = name; } - void dump(std::ostream& str = std::cout) const override; - void dumpJson(std::ostream& str = std::cout) const override; - VAssertType type() const VL_MT_SAFE { return m_type; } - VAssertDirectiveType directive() const { return m_directive; } - bool immediate() const { - return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE - | VAssertType::OBSERVED_DEFERRED_IMMEDIATE - | VAssertType::FINAL_DEFERRED_IMMEDIATE) - || this->type() == VAssertType::INTERNAL; - } -}; -class AstNodeFor VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := initsp : List[AstNode] - // @astgen op2 := condp : AstNodeExpr - // @astgen op3 := incsp : List[AstNode] - // @astgen op4 := stmtsp : List[AstNode] -protected: - AstNodeFor(VNType t, FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, - AstNode* stmtsp) - : AstNodeStmt{t, fl} { - addInitsp(initsp); - this->condp(condp); - addIncsp(incsp); - addStmtsp(stmtsp); - } - -public: - ASTGEN_MEMBERS_AstNodeFor; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := arrayp : AstNode - // @astgen op2 := stmtsp : List[AstNode] -public: - AstNodeForeach(VNType t, FileLine* fl, AstNode* arrayp, AstNode* stmtsp) - : AstNodeStmt(t, fl) { - this->arrayp(arrayp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstNodeForeach; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; -class AstNodeIf VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := condp : AstNodeExpr - // @astgen op2 := thensp : List[AstNode] - // @astgen op3 := elsesp : List[AstNode] - VBranchPred m_branchPred; // Branch prediction as taken/untaken? - bool m_isBoundsCheck; // True if this if node is for assertion/bounds checking -protected: - AstNodeIf(VNType t, FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) - : AstNodeStmt{t, fl} { - this->condp(condp); - addThensp(thensp); - addElsesp(elsesp); - isBoundsCheck(false); - } - -public: - ASTGEN_MEMBERS_AstNodeIf; - bool isGateOptimizable() const override { return false; } - bool isGateDedupable() const override { return true; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - void branchPred(VBranchPred flag) { m_branchPred = flag; } - VBranchPred branchPred() const { return m_branchPred; } - void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; } - bool isBoundsCheck() const { return m_isBoundsCheck; } - bool isFirstInMyListOfStatements(AstNode* n) const override { - return n == thensp() || n == elsesp(); - } -}; -class AstNodeReadWriteMem VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := filenamep : AstNodeExpr - // @astgen op2 := memp : AstNodeExpr - // @astgen op3 := lsbp : Optional[AstNodeExpr] - // @astgen op4 := msbp : Optional[AstNodeExpr] - - const bool m_isHex; // readmemh, not readmemb -public: - AstNodeReadWriteMem(VNType t, FileLine* fl, bool hex, AstNodeExpr* filenamep, - AstNodeExpr* memp, AstNodeExpr* lsbp, AstNodeExpr* msbp) - : AstNodeStmt{t, fl} - , m_isHex{hex} { - this->filenamep(filenamep); - this->memp(memp); - this->lsbp(lsbp); - this->msbp(msbp); - } - ASTGEN_MEMBERS_AstNodeReadWriteMem; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* samep) const override { - return isHex() == VN_DBG_AS(samep, NodeReadWriteMem)->isHex(); - } - bool isHex() const { return m_isHex; } - virtual const char* cFuncPrefixp() const = 0; -}; class AstNodeText VL_NOT_FINAL : public AstNode { string m_text; // METHODS @@ -935,7 +703,6 @@ public: void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } }; - class AstCellInlineScope final : public AstNode { // A particular scoped usage of a Cell Inline // Parents: Scope @@ -964,7 +731,6 @@ public: } // * = modp()->origName() before inlining void scopep(AstScope* nodep) { m_scopep = nodep; } }; - class AstClassExtends final : public AstNode { // class extends class name, or class implements class name // Children: List of AstParseRef for packages/classes @@ -2885,1159 +2651,6 @@ public: bool sameNode(const AstNode* /*samep*/) const override { return true; } }; -// === AstNodeStmt === -class AstAlwaysPublic final : public AstNodeStmt { - // "Fake" sensitivity created by /*verilator public_flat_rw @(edgelist)*/ - // Body statements are just AstVarRefs to the public signals - // @astgen op1 := sensesp : List[AstSenTree] - // @astgen op2 := stmtsp : List[AstNode] -public: - AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) - : ASTGEN_SUPER_AlwaysPublic(fl) { - addSensesp(sensesp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstAlwaysPublic; - bool sameNode(const AstNode* /*samep*/) const override { return true; } - // Special accessors - bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); } - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; -class AstAssertCtl final : public AstNodeStmt { - // @astgen op1 := controlTypep : AstNodeExpr - // @astgen op2 := assertTypesp : Optional[AstNodeExpr] - // @astgen op3 := directiveTypesp : Optional[AstNodeExpr] - // Type of assertcontrol task; either known from parser or from evaluated - // controlTypep expression. - VAssertCtlType m_ctlType; // $assert keyword type (control_type) - VAssertType m_assertTypes; // Types of assertions affected - VAssertDirectiveType m_directiveTypes; // Types of directives affected - -public: - AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr, - AstNodeExpr* itemsp = nullptr); - AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr, - AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr, - AstNodeExpr* itemsp = nullptr); - ASTGEN_MEMBERS_AstAssertCtl; - string verilogKwd() const override { return m_ctlType.ascii(); } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - VAssertCtlType ctlType() const { return m_ctlType; } - void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; } - VAssertType ctlAssertTypes() const { return m_assertTypes; } - void ctlAssertTypes(VAssertType types) { m_assertTypes = types; } - VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; } - void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; } - void dump(std::ostream& str = std::cout) const override; - void dumpJson(std::ostream& str = std::cout) const override; -}; -class AstBreak final : public AstNodeStmt { -public: - explicit AstBreak(FileLine* fl) - : ASTGEN_SUPER_Break(fl) {} - ASTGEN_MEMBERS_AstBreak; - string verilogKwd() const override { return "break"; } - bool isBrancher() const override { - return true; // SPECIAL: We don't process code after breaks - } -}; -class AstCReset final : public AstNodeStmt { - // Reset variable at startup - // @astgen op1 := varrefp : AstVarRef - const bool m_constructing; // Previously cleared by constructor -public: - AstCReset(FileLine* fl, AstVarRef* varrefp, bool constructing) - : ASTGEN_SUPER_CReset(fl) - , m_constructing{constructing} { - this->varrefp(varrefp); - } - ASTGEN_MEMBERS_AstCReset; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool sameNode(const AstNode* samep) const override { - return constructing() == VN_DBG_AS(samep, CReset)->constructing(); - } - bool constructing() const { return m_constructing; } -}; -class AstCReturn final : public AstNodeStmt { - // C++ return from a function - // @astgen op1 := lhsp : AstNodeExpr -public: - AstCReturn(FileLine* fl, AstNodeExpr* lhsp) - : ASTGEN_SUPER_CReturn(fl) { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstCReturn; - int instrCount() const override { return widthInstrs(); } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstCStmt final : public AstNodeStmt { - // Emit C statement - // @astgen op1 := exprsp : List[AstNode] -public: - AstCStmt(FileLine* fl, AstNode* exprsp) - : ASTGEN_SUPER_CStmt(fl) { - addExprsp(exprsp); - } - inline AstCStmt(FileLine* fl, const string& textStmt); - ASTGEN_MEMBERS_AstCStmt; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstComment final : public AstNodeStmt { - // Some comment to put into the output stream - const string m_name; // Text of comment - const bool m_showAt; // Show "at " -public: - AstComment(FileLine* fl, const string& name, bool showAt = false) - : ASTGEN_SUPER_Comment(fl) - , m_name{name} - , m_showAt{showAt} {} - ASTGEN_MEMBERS_AstComment; - string name() const override VL_MT_STABLE { return m_name; } // * = Text - bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments - virtual bool showAt() const { return m_showAt; } -}; -class AstConstraintExpr final : public AstNodeStmt { - // Constraint expression - // @astgen op1 := exprp : AstNodeExpr - bool m_isDisableSoft = false; // Disable soft constraint expression - bool m_isSoft = false; // Soft constraint expression -public: - AstConstraintExpr(FileLine* fl, AstNodeExpr* exprp) - : ASTGEN_SUPER_ConstraintExpr(fl) { - this->exprp(exprp); - } - ASTGEN_MEMBERS_AstConstraintExpr; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - bool isDisableSoft() const { return m_isDisableSoft; } - void isDisableSoft(bool flag) { m_isDisableSoft = flag; } - bool isSoft() const { return m_isSoft; } - void isSoft(bool flag) { m_isSoft = flag; } -}; -class AstConstraintUnique final : public AstNodeStmt { - // Constraint unique statement - // @astgen op1 := rangesp : List[AstNode] -public: - AstConstraintUnique(FileLine* fl, AstNode* rangesp) - : ASTGEN_SUPER_ConstraintUnique(fl) { - addRangesp(rangesp); - } - ASTGEN_MEMBERS_AstConstraintUnique; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstContinue final : public AstNodeStmt { -public: - explicit AstContinue(FileLine* fl) - : ASTGEN_SUPER_Continue(fl) {} - ASTGEN_MEMBERS_AstContinue; - string verilogKwd() const override { return "continue"; } - bool isBrancher() const override { - return true; // SPECIAL: We don't process code after breaks - } -}; -class AstCoverInc final : public AstNodeStmt { - // Coverage analysis point; increment coverage count - // @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock] - // @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock] - // These are expressions to which the node corresponds. Used only in toggle coverage - // - // @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration -public: - AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp) - : ASTGEN_SUPER_CoverInc(fl) - , m_declp{declp} {} - ASTGEN_MEMBERS_AstCoverInc; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } - bool sameNode(const AstNode* samep) const override { - return declp() == VN_DBG_AS(samep, CoverInc)->declp(); - } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isOutputter() override { return true; } - bool isPure() override { return false; } - AstNodeCoverDecl* declp() const { return m_declp; } // Where defined -}; -class AstCoverToggle final : public AstNodeStmt { - // Toggle analysis of given signal - // Parents: MODULE - // @astgen op1 := incp : AstCoverInc - // @astgen op2 := origp : AstNodeExpr - // @astgen op3 := changep : AstNodeExpr -public: - AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep) - : ASTGEN_SUPER_CoverToggle(fl) { - this->incp(incp); - this->origp(origp); - this->changep(changep); - } - ASTGEN_MEMBERS_AstCoverToggle; - int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return true; } - bool isOutputter() override { - return false; // Though the AstCoverInc under this is an outputter - } - // but isPure() true -}; -class AstDelay final : public AstNodeStmt { - // Delay statement - // @astgen op1 := lhsp : AstNodeExpr // Delay value - // @astgen op2 := stmtsp : List[AstNode] // Statements under delay - VTimescale m_timeunit; // Delay's time unit - const bool m_isCycle; // True if it is a cycle delay - -public: - AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle) - : ASTGEN_SUPER_Delay(fl) - , m_isCycle{isCycle} { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstDelay; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - bool isTimingControl() const override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - void timeunit(const VTimescale& flag) { m_timeunit = flag; } - VTimescale timeunit() const { return m_timeunit; } - bool isCycleDelay() const { return m_isCycle; } -}; -class AstDisable final : public AstNodeStmt { - // @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot - // @astgen ptr := m_targetp : Optional[AstNode] // Task or block after V3LinkDot -public: - AstDisable(FileLine* fl, AstNodeExpr* targetRefp) - : ASTGEN_SUPER_Disable(fl) { - this->targetRefp(targetRefp); - } - ASTGEN_MEMBERS_AstDisable; - const char* broken() const override; - void dump(std::ostream& str) const override; - void targetp(AstNode* nodep) { m_targetp = nodep; } - AstNode* targetp() const { return m_targetp; } - bool isBrancher() const override { - return true; // SPECIAL: We don't process code after breaks - } -}; -class AstDisableFork final : public AstNodeStmt { - // A "disable fork" statement -public: - explicit AstDisableFork(FileLine* fl) - : ASTGEN_SUPER_DisableFork(fl) {} - ASTGEN_MEMBERS_AstDisableFork; -}; -class AstDisplay final : public AstNodeStmt { - // Parents: stmtlist - // @astgen op1 := fmtp : AstSFormatF - // @astgen op2 := filep : Optional[AstNodeExpr] // file (must resolve to a VarRef) - VDisplayType m_displayType; - -public: - AstDisplay(FileLine* fl, VDisplayType dispType, const string& text, AstNodeExpr* filep, - AstNodeExpr* exprsp, char missingArgChar = 'd') - : ASTGEN_SUPER_Display(fl) - , m_displayType{dispType} { - fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); - this->filep(filep); - } - AstDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* filep, AstNodeExpr* exprsp, - char missingArgChar = 'd') - : ASTGEN_SUPER_Display(fl) - , m_displayType{dispType} { - fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); - this->filep(filep); - } - ASTGEN_MEMBERS_AstDisplay; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - const char* broken() const override { - BROKEN_RTN(!fmtp()); - return nullptr; - } - string verilogKwd() const override { - return (filep() ? "$f"s + string{displayType().ascii()} - : "$"s + string{displayType().ascii()}); - } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() override { return true; } // SPECIAL: $display makes output - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* samep) const override { - return displayType() == VN_DBG_AS(samep, Display)->displayType(); - } - int instrCount() const override { return INSTR_COUNT_PLI; } - VDisplayType displayType() const { return m_displayType; } - void displayType(VDisplayType type) { m_displayType = type; } - // * = Add a newline for $display - bool addNewline() const { return displayType().addNewline(); } -}; -class AstDoWhile final : public AstNodeStmt { - // @astgen op1 := condp : AstNodeExpr - // @astgen op2 := stmtsp : List[AstNode] -public: - AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr) - : ASTGEN_SUPER_DoWhile(fl) { - condp(conditionp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstDoWhile; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - // Stop statement searchback here - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; -class AstDumpCtl final : public AstNodeStmt { - // $dumpon etc - // Parents: expr - // @astgen op1 := exprp : Optional[AstNodeExpr] // Expression based on type of statement - const VDumpCtlType m_ctlType; // Type of operation -public: - AstDumpCtl(FileLine* fl, VDumpCtlType ctlType, AstNodeExpr* exprp = nullptr) - : ASTGEN_SUPER_DumpCtl(fl) - , m_ctlType{ctlType} { - this->exprp(exprp); - } - ASTGEN_MEMBERS_AstDumpCtl; - string verilogKwd() const override { return ctlType().ascii(); } - bool isGateOptimizable() const override { return false; } - bool isOutputter() override { return true; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - virtual bool cleanOut() const { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - VDumpCtlType ctlType() const { return m_ctlType; } -}; -class AstEventControl final : public AstNodeStmt { - // Parents: stmtlist - // @astgen op1 := sensesp : Optional[AstSenTree] - // @astgen op2 := stmtsp : List[AstNode] -public: - AstEventControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) - : ASTGEN_SUPER_EventControl(fl) { - this->sensesp(sensesp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstEventControl; - string verilogKwd() const override { return "@(%l) %r"; } - bool isTimingControl() const override { return true; } - int instrCount() const override { return 0; } -}; -class AstFClose final : public AstNodeStmt { - // Parents: stmtlist - // @astgen op1 := filep : AstNodeExpr // file (must be a VarRef) -public: - AstFClose(FileLine* fl, AstNodeExpr* filep) - : ASTGEN_SUPER_FClose(fl) { - this->filep(filep); - } - ASTGEN_MEMBERS_AstFClose; - string verilogKwd() const override { return "$fclose"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstFFlush final : public AstNodeStmt { - // Parents: stmtlist - // @astgen op1 := filep : Optional[AstNodeExpr] // file (must be a VarRef) -public: - AstFFlush(FileLine* fl, AstNodeExpr* filep) - : ASTGEN_SUPER_FFlush(fl) { - this->filep(filep); - } - ASTGEN_MEMBERS_AstFFlush; - string verilogKwd() const override { return "$fflush"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstFinish final : public AstNodeStmt { -public: - explicit AstFinish(FileLine* fl) - : ASTGEN_SUPER_Finish(fl) {} - ASTGEN_MEMBERS_AstFinish; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() override { return true; } // SPECIAL: $display makes output - bool isUnlikely() const override { return true; } - int instrCount() const override { return 0; } // Rarely executes - bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } -}; -class AstFireEvent final : public AstNodeStmt { - // '-> _' and '->> _' event trigger statements - // @astgen op1 := operandp : AstNodeExpr - const bool m_delayed; // Delayed (->>) vs non-delayed (->) -public: - AstFireEvent(FileLine* fl, AstNodeExpr* operandp, bool delayed) - : ASTGEN_SUPER_FireEvent(fl) - , m_delayed{delayed} { - this->operandp(operandp); - } - ASTGEN_MEMBERS_AstFireEvent; - bool isDelayed() const { return m_delayed; } -}; -class AstJumpBlock final : public AstNodeStmt { - // Block of code that might contain AstJumpGo statements as children, - // which when exectued branch to right after the referenced AstJumpBlock. - // AstJumpBlocks can nest, and an AstJumpGo can reference any of the - // enclosing AstJumpBlocks (can break out of mulitple levels). - // Parents: {statement list} - // Children: {statement list, with JumpGo below} - // @astgen op1 := stmtsp : List[AstNode] - VIsCached m_purity; // Pure state -public: - // After construction must call ->labelp to associate with appropriate label - AstJumpBlock(FileLine* fl, AstNode* stmtsp) - : ASTGEN_SUPER_JumpBlock(fl) { - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstJumpBlock; - int instrCount() const override { return 0; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - bool isPure() override; - -private: - bool getPurityRecurse() const; -}; -class AstJumpGo final : public AstNodeStmt { - // Branch to right after the referenced encloding AstJumpBlock - // Parents: statement, including the referenced AstJumpBlock - // Children: none - // - // @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after -public: - AstJumpGo(FileLine* fl, AstJumpBlock* blockp) - : ASTGEN_SUPER_JumpGo(fl) - , m_blockp{blockp} {} - ASTGEN_MEMBERS_AstJumpGo; - const char* broken() const override; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* samep) const override { - return blockp() == VN_DBG_AS(samep, JumpGo)->blockp(); - } - bool isGateOptimizable() const override { return false; } - bool isBrancher() const override { - return true; // SPECIAL: We don't process code after breaks - } - AstJumpBlock* blockp() const { return m_blockp; } -}; -class AstMonitorOff final : public AstNodeStmt { - const bool m_off; // Monitor off. Using 0=on allows faster init and comparison - -public: - AstMonitorOff(FileLine* fl, bool off) - : ASTGEN_SUPER_MonitorOff(fl) - , m_off{off} {} - ASTGEN_MEMBERS_AstMonitorOff; - string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; } - bool isGateOptimizable() const override { return false; } // Though deleted before opt - bool isPredictOptimizable() const override { return false; } // Though deleted before opt - bool isPure() override { return false; } // Though deleted before opt - bool isOutputter() override { return true; } // Though deleted before opt - int instrCount() const override { return INSTR_COUNT_PLI; } - bool sameNode(const AstNode* samep) const override { - return m_off == VN_DBG_AS(samep, MonitorOff)->m_off; - } - bool off() const { return m_off; } -}; -class AstPrintTimeScale final : public AstNodeStmt { - // Parents: stmtlist - string m_name; // Parent module name - VTimescale m_timeunit; // Parent module time unit -public: - explicit AstPrintTimeScale(FileLine* fl) - : ASTGEN_SUPER_PrintTimeScale(fl) {} - ASTGEN_MEMBERS_AstPrintTimeScale; - void name(const string& name) override { m_name = name; } - string name() const override VL_MT_STABLE { return m_name; } // * = Var name - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - string verilogKwd() const override { return "$printtimescale"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - int instrCount() const override { return INSTR_COUNT_PLI; } - void timeunit(const VTimescale& flag) { m_timeunit = flag; } - VTimescale timeunit() const { return m_timeunit; } -}; -class AstRandCase final : public AstNodeStmt { - // @astgen op2 := itemsp : List[AstCaseItem] -public: - AstRandCase(FileLine* fl, AstCaseItem* itemsp) - : ASTGEN_SUPER_RandCase(fl) { - addItemsp(itemsp); - } - ASTGEN_MEMBERS_AstRandCase; - int instrCount() const override { return INSTR_COUNT_BRANCH; } -}; -class AstRelease final : public AstNodeStmt { - // Procedural 'release' statement - // @astgen op1 := lhsp : AstNodeExpr -public: - AstRelease(FileLine* fl, AstNodeExpr* lhsp) - : ASTGEN_SUPER_Release(fl) { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstRelease; -}; -class AstRepeat final : public AstNodeStmt { - // @astgen op1 := countp : AstNodeExpr - // @astgen op2 := stmtsp : List[AstNode] -public: - AstRepeat(FileLine* fl, AstNodeExpr* countp, AstNode* stmtsp) - : ASTGEN_SUPER_Repeat(fl) { - this->countp(countp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstRepeat; - bool isGateOptimizable() const override { return false; } // Not relevant - converted to FOR - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; -class AstReturn final : public AstNodeStmt { - // @astgen op1 := lhsp : Optional[AstNodeExpr] -public: - explicit AstReturn(FileLine* fl, AstNodeExpr* lhsp = nullptr) - : ASTGEN_SUPER_Return(fl) { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstReturn; - string verilogKwd() const override { return "return"; } - bool isBrancher() const override { - return true; // SPECIAL: We don't process code after breaks - } -}; -class AstSFormat final : public AstNodeStmt { - // Parents: statement container - // @astgen op1 := fmtp : AstSFormatF - // @astgen op2 := lhsp : AstNodeExpr -public: - AstSFormat(FileLine* fl, AstNodeExpr* lhsp, const string& text, AstNodeExpr* exprsp, - char missingArgChar = 'd') - : ASTGEN_SUPER_SFormat(fl) { - fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); - this->lhsp(lhsp); - } - AstSFormat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* exprsp, char missingArgChar = 'd') - : ASTGEN_SUPER_SFormat(fl) { - fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstSFormat; - const char* broken() const override { - BROKEN_RTN(!fmtp()); - return nullptr; - } - string verilogKwd() const override { return "$sformat"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return true; } - bool isPure() override { return true; } - bool isOutputter() override { return false; } - virtual bool cleanOut() const { return false; } - int instrCount() const override { return INSTR_COUNT_PLI; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstSetuphold final : public AstNodeStmt { - // Verilog $setuphold - // @astgen op1 := refevp : AstNodeExpr - // @astgen op2 := dataevp : AstNodeExpr - // @astgen op3 := delrefp : Optional[AstNodeExpr] - // @astgen op4 := deldatap : Optional[AstNodeExpr] -public: - AstSetuphold(FileLine* fl, AstNodeExpr* refevp, AstNodeExpr* dataevp, - AstNodeExpr* delrefp = nullptr, AstNodeExpr* deldatap = nullptr) - : ASTGEN_SUPER_Setuphold(fl) { - this->refevp(refevp); - this->dataevp(dataevp); - this->delrefp(delrefp); - this->deldatap(deldatap); - } - ASTGEN_MEMBERS_AstSetuphold; - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstStackTraceT final : public AstNodeStmt { - // $stacktrace used as task -public: - explicit AstStackTraceT(FileLine* fl) - : ASTGEN_SUPER_StackTraceT(fl) {} - ASTGEN_MEMBERS_AstStackTraceT; - string verilogKwd() const override { return "$stacktrace"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstStmtExpr final : public AstNodeStmt { - // Expression in statement position - // @astgen op1 := exprp : AstNodeExpr -public: - AstStmtExpr(FileLine* fl, AstNodeExpr* exprp) - : ASTGEN_SUPER_StmtExpr(fl) { - this->exprp(exprp); - } - ASTGEN_MEMBERS_AstStmtExpr; - bool isPure() override { return exprp()->isPure(); } -}; -class AstStop final : public AstNodeStmt { - const bool m_isFatal; // $fatal not $stop -public: - AstStop(FileLine* fl, bool isFatal) - : ASTGEN_SUPER_Stop(fl) - , m_isFatal{isFatal} {} - ASTGEN_MEMBERS_AstStop; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() override { return true; } // SPECIAL: $display makes output - bool isUnlikely() const override { return true; } - int instrCount() const override { return 0; } // Rarely executes - bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } - string emitVerilog() const { return m_isFatal ? "$fatal" : "$stop"; } - bool isFatal() const { return m_isFatal; } -}; -class AstSystemT final : public AstNodeStmt { - // $system used as task - // @astgen op1 := lhsp : AstNodeExpr -public: - AstSystemT(FileLine* fl, AstNodeExpr* lhsp) - : ASTGEN_SUPER_SystemT(fl) { - this->lhsp(lhsp); - } - ASTGEN_MEMBERS_AstSystemT; - string verilogKwd() const override { return "$system"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool isUnlikely() const override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstTimeFormat final : public AstNodeStmt { - // Parents: stmtlist - // @astgen op1 := unitsp : Optional[AstNodeExpr] - // @astgen op2 := precisionp : Optional[AstNodeExpr] - // @astgen op3 := suffixp : Optional[AstNodeExpr] - // @astgen op4 := widthp : Optional[AstNodeExpr] -public: - AstTimeFormat(FileLine* fl, AstNodeExpr* unitsp, AstNodeExpr* precisionp, AstNodeExpr* suffixp, - AstNodeExpr* widthp) - : ASTGEN_SUPER_TimeFormat(fl) { - this->unitsp(unitsp); - this->precisionp(precisionp); - this->suffixp(suffixp); - this->widthp(widthp); - } - ASTGEN_MEMBERS_AstTimeFormat; - string verilogKwd() const override { return "$timeformat"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - int instrCount() const override { return INSTR_COUNT_PLI; } -}; -class AstTraceDecl final : public AstNodeStmt { - // Trace point declaration - // Separate from AstTraceInc; as a declaration can't be deleted - // Parents: {statement list} - // Expression being traced - Moved to AstTraceInc by V3Trace - // @astgen op1 := valuep : Optional[AstNodeExpr] - uint32_t m_code{0}; // Trace identifier code - uint32_t m_fidx{0}; // Trace function index - const string m_showname; // Name of variable - const VNumRange m_bitRange; // Property of var the trace details - const VNumRange m_arrayRange; // Property of var the trace details - const VVarType m_varType; // Type of variable (for localparam vs. param) - const VDirection m_declDirection; // Declared direction input/output etc -public: - AstTraceDecl(FileLine* fl, const string& showname, - AstVar* varp, // For input/output state etc - AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange) - : ASTGEN_SUPER_TraceDecl(fl) - , m_showname{showname} - , m_bitRange{bitRange} - , m_arrayRange{arrayRange} - , m_varType{varp->varType()} - , m_declDirection{varp->declDirection()} { - dtypeFrom(valuep); - this->valuep(valuep); - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 100; } // Large... - ASTGEN_MEMBERS_AstTraceDecl; - string name() const override VL_MT_STABLE { return m_showname; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - bool hasDType() const override VL_MT_SAFE { return true; } - bool sameNode(const AstNode* samep) const override { return false; } - string showname() const { return m_showname; } // * = Var name - // Details on what we're tracing - uint32_t code() const { return m_code; } - void code(uint32_t code) { m_code = code; } - uint32_t fidx() const { return m_fidx; } - void fidx(uint32_t fidx) { m_fidx = fidx; } - uint32_t codeInc() const { - return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1) - * valuep()->dtypep()->widthWords() - * (VL_EDATASIZE / 32); // A code is always 32-bits - } - const VNumRange& bitRange() const { return m_bitRange; } - const VNumRange& arrayRange() const { return m_arrayRange; } - VVarType varType() const { return m_varType; } - VDirection declDirection() const { return m_declDirection; } -}; -class AstTraceInc final : public AstNodeStmt { - // Trace point dump - // @astgen op1 := valuep : AstNodeExpr // Expression being traced (from decl) - // - // @astgen ptr := m_declp : AstTraceDecl // Pointer to declaration - const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc - const VTraceType m_traceType; // Is this a const/full/incremental dump - -public: - AstTraceInc(FileLine* fl, AstTraceDecl* declp, VTraceType traceType, uint32_t baseCode = 0) - : ASTGEN_SUPER_TraceInc(fl) - , m_baseCode{baseCode} - , m_traceType{traceType} - , m_declp{declp} { - dtypeFrom(declp); - // Note: A clone is necessary (instead of using declp()->valuep()), - // for insertion of local temporaries in V3Premit - valuep(declp->valuep()->cloneTree(true)); - } - ASTGEN_MEMBERS_AstTraceInc; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; } - bool hasDType() const override VL_MT_SAFE { return true; } - bool sameNode(const AstNode* samep) const override { - return declp() == VN_DBG_AS(samep, TraceInc)->declp(); - } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isOutputter() override { return true; } - bool isPure() override { return false; } - AstTraceDecl* declp() const { return m_declp; } - VTraceType traceType() const { return m_traceType; } - uint32_t baseCode() const { return m_baseCode; } -}; -class AstTracePopPrefix final : public AstNodeStmt { -public: - explicit AstTracePopPrefix(FileLine* fl) - : ASTGEN_SUPER_TracePopPrefix(fl) {} - ASTGEN_MEMBERS_AstTracePopPrefix; - bool sameNode(const AstNode* samep) const override { return false; } -}; -class AstTracePushPrefix final : public AstNodeStmt { - const string m_prefix; // Prefix to add to signal names - const VTracePrefixType m_prefixType; // Type of prefix being pushed -public: - AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType) - : ASTGEN_SUPER_TracePushPrefix(fl) - , m_prefix{prefix} - , m_prefixType{prefixType} {} - ASTGEN_MEMBERS_AstTracePushPrefix; - bool sameNode(const AstNode* samep) const override { return false; } - string prefix() const { return m_prefix; } - VTracePrefixType prefixType() const { return m_prefixType; } -}; -class AstUCStmt final : public AstNodeStmt { - // User $c statement - // @astgen op1 := exprsp : List[AstNode] // (some are AstText) -public: - AstUCStmt(FileLine* fl, AstNode* exprsp) - : ASTGEN_SUPER_UCStmt(fl) { - addExprsp(exprsp); - } - ASTGEN_MEMBERS_AstUCStmt; - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } - bool isOutputter() override { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; -class AstWait final : public AstNodeStmt { - // @astgen op1 := condp : AstNodeExpr - // @astgen op2 := stmtsp : List[AstNode] -public: - AstWait(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp) - : ASTGEN_SUPER_Wait(fl) { - this->condp(condp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstWait; - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } - bool isTimingControl() const override { return true; } -}; -class AstWaitFork final : public AstNodeStmt { - // A "wait fork" statement -public: - explicit AstWaitFork(FileLine* fl) - : ASTGEN_SUPER_WaitFork(fl) {} - ASTGEN_MEMBERS_AstWaitFork; - bool isTimingControl() const override { return true; } -}; -class AstWhile final : public AstNodeStmt { - // @astgen op1 := condp : AstNodeExpr - // @astgen op2 := stmtsp : List[AstNode] - // @astgen op3 := incsp : List[AstNode] - VOptionBool m_unrollFull; // Full, disable, or default unrolling -public: - AstWhile(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp = nullptr, AstNode* incsp = nullptr) - : ASTGEN_SUPER_While(fl) { - this->condp(condp); - addStmtsp(stmtsp); - addIncsp(incsp); - } - ASTGEN_MEMBERS_AstWhile; - void dump(std::ostream& str) const override; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - // Stop statement searchback here - void addNextStmt(AstNode* newp, AstNode* belowp) override; - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } - VOptionBool unrollFull() const { return m_unrollFull; } - void unrollFull(const VOptionBool flag) { m_unrollFull = flag; } -}; - -// === AstNodeAssign === -class AstAssign final : public AstNodeAssign { -public: - AstAssign(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, - AstNode* timingControlp = nullptr) - : ASTGEN_SUPER_Assign(fl, lhsp, rhsp, timingControlp) { - dtypeFrom(lhsp); - } - ASTGEN_MEMBERS_AstAssign; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; - return new AstAssign{fileline(), lhsp, rhsp, controlp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignAlias final : public AstNodeAssign { - // Like AstAssignW, but a true bidirect interconnection alias - // If both sides are wires, there's no LHS vs RHS, -public: - AstAssignAlias(FileLine* fl, AstVarRef* lhsp, AstVarRef* rhsp) - : ASTGEN_SUPER_AssignAlias(fl, (AstNodeExpr*)lhsp, (AstNodeExpr*)rhsp) {} - ASTGEN_MEMBERS_AstAssignAlias; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - V3ERROR_NA_RETURN(nullptr); - } - bool brokeLhsMustBeLvalue() const override { return false; } -}; -class AstAssignDly final : public AstNodeAssign { -public: - AstAssignDly(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, - AstNode* timingControlp = nullptr) - : ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp, timingControlp) {} - ASTGEN_MEMBERS_AstAssignDly; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; - return new AstAssignDly{fileline(), lhsp, rhsp, controlp}; - } - bool isGateOptimizable() const override { return false; } - string verilogKwd() const override { return "<="; } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignForce final : public AstNodeAssign { - // Procedural 'force' statement -public: - AstAssignForce(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {} - ASTGEN_MEMBERS_AstAssignForce; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignForce{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignPost final : public AstNodeAssign { - // Like Assign, but predelayed assignment requiring special order handling -public: - AstAssignPost(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignPost(fl, lhsp, rhsp) {} - ASTGEN_MEMBERS_AstAssignPost; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignPost{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignPre final : public AstNodeAssign { - // Like Assign, but predelayed assignment requiring special order handling -public: - AstAssignPre(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignPre(fl, lhsp, rhsp) {} - ASTGEN_MEMBERS_AstAssignPre; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignPre{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignVarScope final : public AstNodeAssign { - // Assign two VarScopes to each other -public: - AstAssignVarScope(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignVarScope(fl, lhsp, rhsp) { - dtypeFrom(rhsp); - } - ASTGEN_MEMBERS_AstAssignVarScope; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignVarScope{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return false; } -}; -class AstAssignW final : public AstNodeAssign { - // Like assign, but wire/assign's in verilog, the only setting of the specified variable - // @astgen op4 := strengthSpecp : Optional[AstStrengthSpec] -public: - AstAssignW(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, - AstNode* timingControlp = nullptr) - : ASTGEN_SUPER_AssignW(fl, lhsp, rhsp, timingControlp) {} - ASTGEN_MEMBERS_AstAssignW; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; - return new AstAssignW{fileline(), lhsp, rhsp, controlp}; - } - bool isTimingControl() const override { - return timingControlp() || lhsp()->exists([](const AstNodeVarRef* refp) { - return refp->access().isWriteOrRW() && refp->varp()->delayp(); - }); - } - bool brokeLhsMustBeLvalue() const override { return true; } - AstDelay* getLhsNetDelay() const; - AstAlways* convertToAlways(); -}; - -// === AstNodeCase === -class AstCase final : public AstNodeCase { - // Case statement - VCaseType m_casex; // 0=case, 1=casex, 2=casez - bool m_fullPragma = false; // Synthesis full_case - bool m_parallelPragma = false; // Synthesis parallel_case - bool m_uniquePragma = false; // unique case - bool m_unique0Pragma = false; // unique0 case - bool m_priorityPragma = false; // priority case -public: - AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp) - : ASTGEN_SUPER_Case(fl, exprp, itemsp) - , m_casex{casex} {} - ASTGEN_MEMBERS_AstCase; - string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } - bool sameNode(const AstNode* samep) const override { - return m_casex == VN_DBG_AS(samep, Case)->m_casex; - } - bool casex() const { return m_casex == VCaseType::CT_CASEX; } - bool casez() const { return m_casex == VCaseType::CT_CASEZ; } - bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; } - bool caseSimple() const { return m_casex == VCaseType::CT_CASE; } - void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } - bool fullPragma() const { return m_fullPragma; } - void fullPragma(bool flag) { m_fullPragma = flag; } - bool parallelPragma() const { return m_parallelPragma; } - void parallelPragma(bool flag) { m_parallelPragma = flag; } - bool uniquePragma() const { return m_uniquePragma; } - void uniquePragma(bool flag) { m_uniquePragma = flag; } - bool unique0Pragma() const { return m_unique0Pragma; } - void unique0Pragma(bool flag) { m_unique0Pragma = flag; } - bool priorityPragma() const { return m_priorityPragma; } - void priorityPragma(bool flag) { m_priorityPragma = flag; } - string pragmaString() const; -}; -class AstGenCase final : public AstNodeCase { - // Generate Case statement -public: - AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) - : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} - ASTGEN_MEMBERS_AstGenCase; -}; -class AstCoverOtherDecl final : public AstNodeCoverDecl { - // Coverage analysis point declaration - // Used for other than toggle types of coverage - string m_linescov; - int m_offset; // Offset column numbers to uniq-ify IFs -public: - AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, - const string& linescov, int offset) - : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) - , m_linescov{linescov} - , m_offset{offset} {} - ASTGEN_MEMBERS_AstCoverOtherDecl; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int offset() const { return m_offset; } - int size() const override { return 1; } - const string& linescov() const { return m_linescov; } - bool sameNode(const AstNode* samep) const override { - const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); - return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); - } -}; -class AstCoverToggleDecl final : public AstNodeCoverDecl { - // Coverage analysis point declaration - // Used for toggle coverage - const VNumRange m_range; // Packed array range covering each toggle bit -public: - AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, - const VNumRange& range) - : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) - , m_range{range} {} - ASTGEN_MEMBERS_AstCoverToggleDecl; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int size() const override { return m_range.elements(); } - const VNumRange& range() const { return m_range; } - bool sameNode(const AstNode* samep) const override { - const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); - return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); - } -}; - -// === AstNodeCoverOrAssert === -class AstAssert final : public AstNodeCoverOrAssert { - // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey - -public: - ASTGEN_MEMBERS_AstAssert; - AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, VAssertType type, - VAssertDirectiveType directive, const string& name = "") - : ASTGEN_SUPER_Assert(fl, propp, passsp, type, directive, name) { - addFailsp(failsp); - } -}; -class AstAssertIntrinsic final : public AstNodeCoverOrAssert { - // A $cast or other compiler inserted assert, that must run even without --assert option - // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey -public: - ASTGEN_MEMBERS_AstAssertIntrinsic; - AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, - const string& name = "") - // Intrinsic asserts are always enabled thus 'type' field is set to INTERNAL. - : ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, VAssertType::INTERNAL, - VAssertDirectiveType::INTRINSIC, name) { - addFailsp(failsp); - } -}; -class AstCover final : public AstNodeCoverOrAssert { - // @astgen op3 := coverincsp: List[AstNode] // Coverage node -public: - ASTGEN_MEMBERS_AstCover; - AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type, - const string& name = "") - : ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {} -}; -class AstRestrict final : public AstNodeCoverOrAssert { -public: - ASTGEN_MEMBERS_AstRestrict; - AstRestrict(FileLine* fl, AstNode* propp) - // Intrinsic asserts are always ignored thus 'type' field is set to INTERNAL. - : ASTGEN_SUPER_Restrict(fl, propp, nullptr, VAssertType::INTERNAL, - VAssertDirectiveType::RESTRICT) {} -}; - -// === AstNodeFor === -class AstGenFor final : public AstNodeFor { -public: - AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* stmtsp) - : ASTGEN_SUPER_GenFor(fl, initsp, condp, incsp, stmtsp) {} - ASTGEN_MEMBERS_AstGenFor; -}; - -// === AstNodeForeach === -class AstConstraintForeach final : public AstNodeForeach { - // Constraint foreach statement -public: - AstConstraintForeach(FileLine* fl, AstNodeExpr* exprp, AstNode* bodysp) - : ASTGEN_SUPER_ConstraintForeach(fl, exprp, bodysp) {} - ASTGEN_MEMBERS_AstConstraintForeach; -}; -class AstForeach final : public AstNodeForeach { -public: - AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp) - : ASTGEN_SUPER_Foreach(fl, arrayp, stmtsp) {} - ASTGEN_MEMBERS_AstForeach; -}; - -// === AstNodeIf === -class AstConstraintIf final : public AstNodeIf { -public: - AstConstraintIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) - : ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {} - ASTGEN_MEMBERS_AstConstraintIf; -}; -class AstGenIf final : public AstNodeIf { -public: - AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) - : ASTGEN_SUPER_GenIf(fl, condp, thensp, elsesp) {} - ASTGEN_MEMBERS_AstGenIf; -}; -class AstIf final : public AstNodeIf { - bool m_uniquePragma = false; // unique case - bool m_unique0Pragma = false; // unique0 case - bool m_priorityPragma = false; // priority case -public: - AstIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp = nullptr, AstNode* elsesp = nullptr) - : ASTGEN_SUPER_If(fl, condp, thensp, elsesp) {} - ASTGEN_MEMBERS_AstIf; - bool uniquePragma() const { return m_uniquePragma; } - void uniquePragma(bool flag) { m_uniquePragma = flag; } - bool unique0Pragma() const { return m_unique0Pragma; } - void unique0Pragma(bool flag) { m_unique0Pragma = flag; } - bool priorityPragma() const { return m_priorityPragma; } - void priorityPragma(bool flag) { m_priorityPragma = flag; } -}; - -// === AstNodeReadWriteMem === -class AstReadMem final : public AstNodeReadWriteMem { -public: - AstReadMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, - AstNodeExpr* lsbp, AstNodeExpr* msbp) - : ASTGEN_SUPER_ReadMem(fl, hex, filenamep, memp, lsbp, msbp) {} - ASTGEN_MEMBERS_AstReadMem; - string verilogKwd() const override { return (isHex() ? "$readmemh" : "$readmemb"); } - const char* cFuncPrefixp() const override { return "VL_READMEM_"; } -}; -class AstWriteMem final : public AstNodeReadWriteMem { -public: - AstWriteMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, - AstNodeExpr* lsbp, AstNodeExpr* msbp) - : ASTGEN_SUPER_WriteMem(fl, hex, filenamep, memp, lsbp, msbp) {} - ASTGEN_MEMBERS_AstWriteMem; - string verilogKwd() const override { return (isHex() ? "$writememh" : "$writememb"); } - const char* cFuncPrefixp() const override { return "VL_WRITEMEM_"; } -}; - // === AstNodeText === class AstScCtor final : public AstNodeText { public: diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h new file mode 100644 index 000000000..7ba9e187d --- /dev/null +++ b/src/V3AstNodeStmt.h @@ -0,0 +1,1420 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: AstNode sub-types representing statements +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// This files contains all 'AstNode' sub-types that are statements. +// +//************************************************************************* + +#ifndef VERILATOR_V3ASTNODESTMT_H_ +#define VERILATOR_V3ASTNODESTMT_H_ + +#ifndef VERILATOR_V3AST_H_ +#error "Use V3Ast.h as the include" +#include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h +#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE +#endif + +// === Abstract base node types (AstNode*) ===================================== + +class AstNodeStmt VL_NOT_FINAL : public AstNode { + // Procedural statement +protected: + AstNodeStmt(VNType t, FileLine* fl) + : AstNode{t, fl} {} + +public: + ASTGEN_MEMBERS_AstNodeStmt; + // METHODS + void addNextStmt(AstNode* newp, + AstNode* belowp) override; // Stop statement searchback here + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; +}; +class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt { + // Iteration is in order, and we want rhsp to be visited first (which is the execution order) + // @astgen op1 := rhsp : AstNodeExpr + // @astgen op2 := lhsp : AstNodeExpr + // @astgen op3 := timingControlp : Optional[AstNode] +protected: + AstNodeAssign(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, + AstNode* timingControlp = nullptr) + : AstNodeStmt{t, fl} { + this->rhsp(rhsp); + this->lhsp(lhsp); + this->timingControlp(timingControlp); + dtypeFrom(lhsp); + } + +public: + ASTGEN_MEMBERS_AstNodeAssign; + // Clone single node, just get same type back. + virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0; + bool hasDType() const override VL_MT_SAFE { return true; } + virtual bool cleanRhs() const { return true; } + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode*) const override { return true; } + string verilogKwd() const override { return "="; } + bool isTimingControl() const override { return timingControlp(); } + virtual bool brokeLhsMustBeLvalue() const = 0; +}; +class AstNodeCase VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression + // @astgen op2 := itemsp : List[AstCaseItem] + // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's +protected: + AstNodeCase(VNType t, FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) + : AstNodeStmt{t, fl} { + this->exprp(exprp); + addItemsp(itemsp); + } + +public: + ASTGEN_MEMBERS_AstNodeCase; + int instrCount() const override { return INSTR_COUNT_BRANCH; } +}; +class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt { + // Coverage analysis point declaration + // + // [After V3CoverageJoin] Duplicate declaration to get data from instead + // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] + string m_page; // Coverage point's page tag + string m_text; // Coverage point's text + string m_hier; // Coverage point's hierarchy + int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment +public: + AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) + : AstNodeStmt(t, fl) + , m_page{page} + , m_text{comment} {} + ASTGEN_MEMBERS_AstNodeCoverDecl; + const char* broken() const override { + if (m_dataDeclp + && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing + v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); + } + return nullptr; + } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + int binNum() const { return m_binNum; } + void binNum(int flag) { m_binNum = flag; } + virtual int size() const = 0; + const string& comment() const { return m_text; } // text to insert in code + const string& page() const { return m_page; } + const string& hier() const { return m_hier; } + void hier(const string& flag) { m_hier = flag; } + void comment(const string& flag) { m_text = flag; } + bool sameNode(const AstNode* samep) const override { + const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); + return (fileline() == asamep->fileline() && hier() == asamep->hier() + && comment() == asamep->comment() && page() == asamep->page()); + } + bool isPredictOptimizable() const override { return false; } + void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } + // dataDecl nullptr means "use this one", but often you want "this" to + // indicate to get data from here + AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } + AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } +}; +class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { + // Cover or Assert + // Parents: {statement list} + // @astgen op1 := propp : AstNode + // @astgen op2 := sentreep : Optional[AstSenTree] + // op3 used by some sub-types only + // @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly + string m_name; // Name to report + const VAssertType m_type; // Assertion/cover type + const VAssertDirectiveType m_directive; // Assertion directive type + +public: + AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type, + VAssertDirectiveType directive, const string& name = "") + : AstNodeStmt{t, fl} + , m_name{name} + , m_type{type} + , m_directive{directive} { + this->propp(propp); + addPasssp(passsp); + } + ASTGEN_MEMBERS_AstNodeCoverOrAssert; + string name() const override VL_MT_STABLE { return m_name; } // * = Var name + bool sameNode(const AstNode* samep) const override { return samep->name() == name(); } + void name(const string& name) override { m_name = name; } + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; + VAssertType type() const VL_MT_SAFE { return m_type; } + VAssertDirectiveType directive() const { return m_directive; } + bool immediate() const { + return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE + | VAssertType::OBSERVED_DEFERRED_IMMEDIATE + | VAssertType::FINAL_DEFERRED_IMMEDIATE) + || this->type() == VAssertType::INTERNAL; + } +}; +class AstNodeFor VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := initsp : List[AstNode] + // @astgen op2 := condp : AstNodeExpr + // @astgen op3 := incsp : List[AstNode] + // @astgen op4 := stmtsp : List[AstNode] +protected: + AstNodeFor(VNType t, FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, + AstNode* stmtsp) + : AstNodeStmt{t, fl} { + addInitsp(initsp); + this->condp(condp); + addIncsp(incsp); + addStmtsp(stmtsp); + } + +public: + ASTGEN_MEMBERS_AstNodeFor; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := arrayp : AstNode + // @astgen op2 := stmtsp : List[AstNode] +public: + AstNodeForeach(VNType t, FileLine* fl, AstNode* arrayp, AstNode* stmtsp) + : AstNodeStmt(t, fl) { + this->arrayp(arrayp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstNodeForeach; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; +class AstNodeIf VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := thensp : List[AstNode] + // @astgen op3 := elsesp : List[AstNode] + VBranchPred m_branchPred; // Branch prediction as taken/untaken? + bool m_isBoundsCheck; // True if this if node is for assertion/bounds checking +protected: + AstNodeIf(VNType t, FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) + : AstNodeStmt{t, fl} { + this->condp(condp); + addThensp(thensp); + addElsesp(elsesp); + isBoundsCheck(false); + } + +public: + ASTGEN_MEMBERS_AstNodeIf; + bool isGateOptimizable() const override { return false; } + bool isGateDedupable() const override { return true; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + void branchPred(VBranchPred flag) { m_branchPred = flag; } + VBranchPred branchPred() const { return m_branchPred; } + void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; } + bool isBoundsCheck() const { return m_isBoundsCheck; } + bool isFirstInMyListOfStatements(AstNode* n) const override { + return n == thensp() || n == elsesp(); + } +}; +class AstNodeReadWriteMem VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := filenamep : AstNodeExpr + // @astgen op2 := memp : AstNodeExpr + // @astgen op3 := lsbp : Optional[AstNodeExpr] + // @astgen op4 := msbp : Optional[AstNodeExpr] + + const bool m_isHex; // readmemh, not readmemb +public: + AstNodeReadWriteMem(VNType t, FileLine* fl, bool hex, AstNodeExpr* filenamep, + AstNodeExpr* memp, AstNodeExpr* lsbp, AstNodeExpr* msbp) + : AstNodeStmt{t, fl} + , m_isHex{hex} { + this->filenamep(filenamep); + this->memp(memp); + this->lsbp(lsbp); + this->msbp(msbp); + } + ASTGEN_MEMBERS_AstNodeReadWriteMem; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* samep) const override { + return isHex() == VN_DBG_AS(samep, NodeReadWriteMem)->isHex(); + } + bool isHex() const { return m_isHex; } + virtual const char* cFuncPrefixp() const = 0; +}; + +// === Concrete node types ===================================================== + +// === AstNodeStmt === +class AstAlwaysPublic final : public AstNodeStmt { + // "Fake" sensitivity created by /*verilator public_flat_rw @(edgelist)*/ + // Body statements are just AstVarRefs to the public signals + // @astgen op1 := sensesp : List[AstSenTree] + // @astgen op2 := stmtsp : List[AstNode] +public: + AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) + : ASTGEN_SUPER_AlwaysPublic(fl) { + addSensesp(sensesp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstAlwaysPublic; + bool sameNode(const AstNode* /*samep*/) const override { return true; } + // Special accessors + bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; +class AstAssertCtl final : public AstNodeStmt { + // @astgen op1 := controlTypep : AstNodeExpr + // @astgen op2 := assertTypesp : Optional[AstNodeExpr] + // @astgen op3 := directiveTypesp : Optional[AstNodeExpr] + // Type of assertcontrol task; either known from parser or from evaluated + // controlTypep expression. + VAssertCtlType m_ctlType; // $assert keyword type (control_type) + VAssertType m_assertTypes; // Types of assertions affected + VAssertDirectiveType m_directiveTypes; // Types of directives affected + +public: + AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr, + AstNodeExpr* itemsp = nullptr); + AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr, + AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr, + AstNodeExpr* itemsp = nullptr); + ASTGEN_MEMBERS_AstAssertCtl; + string verilogKwd() const override { return m_ctlType.ascii(); } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + VAssertCtlType ctlType() const { return m_ctlType; } + void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; } + VAssertType ctlAssertTypes() const { return m_assertTypes; } + void ctlAssertTypes(VAssertType types) { m_assertTypes = types; } + VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; } + void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; } + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; +}; +class AstBreak final : public AstNodeStmt { +public: + explicit AstBreak(FileLine* fl) + : ASTGEN_SUPER_Break(fl) {} + ASTGEN_MEMBERS_AstBreak; + string verilogKwd() const override { return "break"; } + bool isBrancher() const override { + return true; // SPECIAL: We don't process code after breaks + } +}; +class AstCReset final : public AstNodeStmt { + // Reset variable at startup + // @astgen op1 := varrefp : AstVarRef + const bool m_constructing; // Previously cleared by constructor +public: + AstCReset(FileLine* fl, AstVarRef* varrefp, bool constructing) + : ASTGEN_SUPER_CReset(fl) + , m_constructing{constructing} { + this->varrefp(varrefp); + } + ASTGEN_MEMBERS_AstCReset; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool sameNode(const AstNode* samep) const override { + return constructing() == VN_DBG_AS(samep, CReset)->constructing(); + } + bool constructing() const { return m_constructing; } +}; +class AstCReturn final : public AstNodeStmt { + // C++ return from a function + // @astgen op1 := lhsp : AstNodeExpr +public: + AstCReturn(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_CReturn(fl) { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstCReturn; + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstCStmt final : public AstNodeStmt { + // Emit C statement + // @astgen op1 := exprsp : List[AstNode] +public: + AstCStmt(FileLine* fl, AstNode* exprsp) + : ASTGEN_SUPER_CStmt(fl) { + addExprsp(exprsp); + } + inline AstCStmt(FileLine* fl, const string& textStmt); + ASTGEN_MEMBERS_AstCStmt; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstComment final : public AstNodeStmt { + // Some comment to put into the output stream + const string m_name; // Text of comment + const bool m_showAt; // Show "at " +public: + AstComment(FileLine* fl, const string& name, bool showAt = false) + : ASTGEN_SUPER_Comment(fl) + , m_name{name} + , m_showAt{showAt} {} + ASTGEN_MEMBERS_AstComment; + string name() const override VL_MT_STABLE { return m_name; } // * = Text + bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments + virtual bool showAt() const { return m_showAt; } +}; +class AstConstraintExpr final : public AstNodeStmt { + // Constraint expression + // @astgen op1 := exprp : AstNodeExpr + bool m_isDisableSoft = false; // Disable soft constraint expression + bool m_isSoft = false; // Soft constraint expression +public: + AstConstraintExpr(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_ConstraintExpr(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstConstraintExpr; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + bool isDisableSoft() const { return m_isDisableSoft; } + void isDisableSoft(bool flag) { m_isDisableSoft = flag; } + bool isSoft() const { return m_isSoft; } + void isSoft(bool flag) { m_isSoft = flag; } +}; +class AstConstraintUnique final : public AstNodeStmt { + // Constraint unique statement + // @astgen op1 := rangesp : List[AstNode] +public: + AstConstraintUnique(FileLine* fl, AstNode* rangesp) + : ASTGEN_SUPER_ConstraintUnique(fl) { + addRangesp(rangesp); + } + ASTGEN_MEMBERS_AstConstraintUnique; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstContinue final : public AstNodeStmt { +public: + explicit AstContinue(FileLine* fl) + : ASTGEN_SUPER_Continue(fl) {} + ASTGEN_MEMBERS_AstContinue; + string verilogKwd() const override { return "continue"; } + bool isBrancher() const override { + return true; // SPECIAL: We don't process code after breaks + } +}; +class AstCoverInc final : public AstNodeStmt { + // Coverage analysis point; increment coverage count + // @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock] + // @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock] + // These are expressions to which the node corresponds. Used only in toggle coverage + // + // @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration +public: + AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp) + : ASTGEN_SUPER_CoverInc(fl) + , m_declp{declp} {} + ASTGEN_MEMBERS_AstCoverInc; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } + bool sameNode(const AstNode* samep) const override { + return declp() == VN_DBG_AS(samep, CoverInc)->declp(); + } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isOutputter() override { return true; } + bool isPure() override { return false; } + AstNodeCoverDecl* declp() const { return m_declp; } // Where defined +}; +class AstCoverToggle final : public AstNodeStmt { + // Toggle analysis of given signal + // Parents: MODULE + // @astgen op1 := incp : AstCoverInc + // @astgen op2 := origp : AstNodeExpr + // @astgen op3 := changep : AstNodeExpr +public: + AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep) + : ASTGEN_SUPER_CoverToggle(fl) { + this->incp(incp); + this->origp(origp); + this->changep(changep); + } + ASTGEN_MEMBERS_AstCoverToggle; + int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return true; } + bool isOutputter() override { + return false; // Though the AstCoverInc under this is an outputter + } + // but isPure() true +}; +class AstDelay final : public AstNodeStmt { + // Delay statement + // @astgen op1 := lhsp : AstNodeExpr // Delay value + // @astgen op2 := stmtsp : List[AstNode] // Statements under delay + VTimescale m_timeunit; // Delay's time unit + const bool m_isCycle; // True if it is a cycle delay + +public: + AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle) + : ASTGEN_SUPER_Delay(fl) + , m_isCycle{isCycle} { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstDelay; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + bool isTimingControl() const override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + void timeunit(const VTimescale& flag) { m_timeunit = flag; } + VTimescale timeunit() const { return m_timeunit; } + bool isCycleDelay() const { return m_isCycle; } +}; +class AstDisable final : public AstNodeStmt { + // @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot + // @astgen ptr := m_targetp : Optional[AstNode] // Task or block after V3LinkDot +public: + AstDisable(FileLine* fl, AstNodeExpr* targetRefp) + : ASTGEN_SUPER_Disable(fl) { + this->targetRefp(targetRefp); + } + ASTGEN_MEMBERS_AstDisable; + const char* broken() const override; + void dump(std::ostream& str) const override; + void targetp(AstNode* nodep) { m_targetp = nodep; } + AstNode* targetp() const { return m_targetp; } + bool isBrancher() const override { + return true; // SPECIAL: We don't process code after breaks + } +}; +class AstDisableFork final : public AstNodeStmt { + // A "disable fork" statement +public: + explicit AstDisableFork(FileLine* fl) + : ASTGEN_SUPER_DisableFork(fl) {} + ASTGEN_MEMBERS_AstDisableFork; +}; +class AstDisplay final : public AstNodeStmt { + // Parents: stmtlist + // @astgen op1 := fmtp : AstSFormatF + // @astgen op2 := filep : Optional[AstNodeExpr] // file (must resolve to a VarRef) + VDisplayType m_displayType; + +public: + AstDisplay(FileLine* fl, VDisplayType dispType, const string& text, AstNodeExpr* filep, + AstNodeExpr* exprsp, char missingArgChar = 'd') + : ASTGEN_SUPER_Display(fl) + , m_displayType{dispType} { + fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); + this->filep(filep); + } + AstDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* filep, AstNodeExpr* exprsp, + char missingArgChar = 'd') + : ASTGEN_SUPER_Display(fl) + , m_displayType{dispType} { + fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); + this->filep(filep); + } + ASTGEN_MEMBERS_AstDisplay; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + const char* broken() const override { + BROKEN_RTN(!fmtp()); + return nullptr; + } + string verilogKwd() const override { + return (filep() ? "$f"s + string{displayType().ascii()} + : "$"s + string{displayType().ascii()}); + } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* samep) const override { + return displayType() == VN_DBG_AS(samep, Display)->displayType(); + } + int instrCount() const override { return INSTR_COUNT_PLI; } + VDisplayType displayType() const { return m_displayType; } + void displayType(VDisplayType type) { m_displayType = type; } + // * = Add a newline for $display + bool addNewline() const { return displayType().addNewline(); } +}; +class AstDoWhile final : public AstNodeStmt { + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] +public: + AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr) + : ASTGEN_SUPER_DoWhile(fl) { + condp(conditionp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstDoWhile; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + // Stop statement searchback here + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; +class AstDumpCtl final : public AstNodeStmt { + // $dumpon etc + // Parents: expr + // @astgen op1 := exprp : Optional[AstNodeExpr] // Expression based on type of statement + const VDumpCtlType m_ctlType; // Type of operation +public: + AstDumpCtl(FileLine* fl, VDumpCtlType ctlType, AstNodeExpr* exprp = nullptr) + : ASTGEN_SUPER_DumpCtl(fl) + , m_ctlType{ctlType} { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstDumpCtl; + string verilogKwd() const override { return ctlType().ascii(); } + bool isGateOptimizable() const override { return false; } + bool isOutputter() override { return true; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + virtual bool cleanOut() const { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + VDumpCtlType ctlType() const { return m_ctlType; } +}; +class AstEventControl final : public AstNodeStmt { + // Parents: stmtlist + // @astgen op1 := sensesp : Optional[AstSenTree] + // @astgen op2 := stmtsp : List[AstNode] +public: + AstEventControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) + : ASTGEN_SUPER_EventControl(fl) { + this->sensesp(sensesp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstEventControl; + string verilogKwd() const override { return "@(%l) %r"; } + bool isTimingControl() const override { return true; } + int instrCount() const override { return 0; } +}; +class AstFClose final : public AstNodeStmt { + // Parents: stmtlist + // @astgen op1 := filep : AstNodeExpr // file (must be a VarRef) +public: + AstFClose(FileLine* fl, AstNodeExpr* filep) + : ASTGEN_SUPER_FClose(fl) { + this->filep(filep); + } + ASTGEN_MEMBERS_AstFClose; + string verilogKwd() const override { return "$fclose"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstFFlush final : public AstNodeStmt { + // Parents: stmtlist + // @astgen op1 := filep : Optional[AstNodeExpr] // file (must be a VarRef) +public: + AstFFlush(FileLine* fl, AstNodeExpr* filep) + : ASTGEN_SUPER_FFlush(fl) { + this->filep(filep); + } + ASTGEN_MEMBERS_AstFFlush; + string verilogKwd() const override { return "$fflush"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstFinish final : public AstNodeStmt { +public: + explicit AstFinish(FileLine* fl) + : ASTGEN_SUPER_Finish(fl) {} + ASTGEN_MEMBERS_AstFinish; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output + bool isUnlikely() const override { return true; } + int instrCount() const override { return 0; } // Rarely executes + bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } +}; +class AstFireEvent final : public AstNodeStmt { + // '-> _' and '->> _' event trigger statements + // @astgen op1 := operandp : AstNodeExpr + const bool m_delayed; // Delayed (->>) vs non-delayed (->) +public: + AstFireEvent(FileLine* fl, AstNodeExpr* operandp, bool delayed) + : ASTGEN_SUPER_FireEvent(fl) + , m_delayed{delayed} { + this->operandp(operandp); + } + ASTGEN_MEMBERS_AstFireEvent; + bool isDelayed() const { return m_delayed; } +}; +class AstJumpBlock final : public AstNodeStmt { + // Block of code that might contain AstJumpGo statements as children, + // which when exectued branch to right after the referenced AstJumpBlock. + // AstJumpBlocks can nest, and an AstJumpGo can reference any of the + // enclosing AstJumpBlocks (can break out of mulitple levels). + // Parents: {statement list} + // Children: {statement list, with JumpGo below} + // @astgen op1 := stmtsp : List[AstNode] + VIsCached m_purity; // Pure state +public: + // After construction must call ->labelp to associate with appropriate label + AstJumpBlock(FileLine* fl, AstNode* stmtsp) + : ASTGEN_SUPER_JumpBlock(fl) { + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstJumpBlock; + int instrCount() const override { return 0; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + bool isPure() override; + +private: + bool getPurityRecurse() const; +}; +class AstJumpGo final : public AstNodeStmt { + // Branch to right after the referenced encloding AstJumpBlock + // Parents: statement, including the referenced AstJumpBlock + // Children: none + // + // @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after +public: + AstJumpGo(FileLine* fl, AstJumpBlock* blockp) + : ASTGEN_SUPER_JumpGo(fl) + , m_blockp{blockp} {} + ASTGEN_MEMBERS_AstJumpGo; + const char* broken() const override; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* samep) const override { + return blockp() == VN_DBG_AS(samep, JumpGo)->blockp(); + } + bool isGateOptimizable() const override { return false; } + bool isBrancher() const override { + return true; // SPECIAL: We don't process code after breaks + } + AstJumpBlock* blockp() const { return m_blockp; } +}; +class AstMonitorOff final : public AstNodeStmt { + const bool m_off; // Monitor off. Using 0=on allows faster init and comparison + +public: + AstMonitorOff(FileLine* fl, bool off) + : ASTGEN_SUPER_MonitorOff(fl) + , m_off{off} {} + ASTGEN_MEMBERS_AstMonitorOff; + string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; } + bool isGateOptimizable() const override { return false; } // Though deleted before opt + bool isPredictOptimizable() const override { return false; } // Though deleted before opt + bool isPure() override { return false; } // Though deleted before opt + bool isOutputter() override { return true; } // Though deleted before opt + int instrCount() const override { return INSTR_COUNT_PLI; } + bool sameNode(const AstNode* samep) const override { + return m_off == VN_DBG_AS(samep, MonitorOff)->m_off; + } + bool off() const { return m_off; } +}; +class AstPrintTimeScale final : public AstNodeStmt { + // Parents: stmtlist + string m_name; // Parent module name + VTimescale m_timeunit; // Parent module time unit +public: + explicit AstPrintTimeScale(FileLine* fl) + : ASTGEN_SUPER_PrintTimeScale(fl) {} + ASTGEN_MEMBERS_AstPrintTimeScale; + void name(const string& name) override { m_name = name; } + string name() const override VL_MT_STABLE { return m_name; } // * = Var name + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + string verilogKwd() const override { return "$printtimescale"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + int instrCount() const override { return INSTR_COUNT_PLI; } + void timeunit(const VTimescale& flag) { m_timeunit = flag; } + VTimescale timeunit() const { return m_timeunit; } +}; +class AstRandCase final : public AstNodeStmt { + // @astgen op2 := itemsp : List[AstCaseItem] +public: + AstRandCase(FileLine* fl, AstCaseItem* itemsp) + : ASTGEN_SUPER_RandCase(fl) { + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstRandCase; + int instrCount() const override { return INSTR_COUNT_BRANCH; } +}; +class AstRelease final : public AstNodeStmt { + // Procedural 'release' statement + // @astgen op1 := lhsp : AstNodeExpr +public: + AstRelease(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_Release(fl) { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstRelease; +}; +class AstRepeat final : public AstNodeStmt { + // @astgen op1 := countp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] +public: + AstRepeat(FileLine* fl, AstNodeExpr* countp, AstNode* stmtsp) + : ASTGEN_SUPER_Repeat(fl) { + this->countp(countp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstRepeat; + bool isGateOptimizable() const override { return false; } // Not relevant - converted to FOR + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; +class AstReturn final : public AstNodeStmt { + // @astgen op1 := lhsp : Optional[AstNodeExpr] +public: + explicit AstReturn(FileLine* fl, AstNodeExpr* lhsp = nullptr) + : ASTGEN_SUPER_Return(fl) { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstReturn; + string verilogKwd() const override { return "return"; } + bool isBrancher() const override { + return true; // SPECIAL: We don't process code after breaks + } +}; +class AstSFormat final : public AstNodeStmt { + // Parents: statement container + // @astgen op1 := fmtp : AstSFormatF + // @astgen op2 := lhsp : AstNodeExpr +public: + AstSFormat(FileLine* fl, AstNodeExpr* lhsp, const string& text, AstNodeExpr* exprsp, + char missingArgChar = 'd') + : ASTGEN_SUPER_SFormat(fl) { + fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); + this->lhsp(lhsp); + } + AstSFormat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* exprsp, char missingArgChar = 'd') + : ASTGEN_SUPER_SFormat(fl) { + fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstSFormat; + const char* broken() const override { + BROKEN_RTN(!fmtp()); + return nullptr; + } + string verilogKwd() const override { return "$sformat"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return true; } + bool isPure() override { return true; } + bool isOutputter() override { return false; } + virtual bool cleanOut() const { return false; } + int instrCount() const override { return INSTR_COUNT_PLI; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstSetuphold final : public AstNodeStmt { + // Verilog $setuphold + // @astgen op1 := refevp : AstNodeExpr + // @astgen op2 := dataevp : AstNodeExpr + // @astgen op3 := delrefp : Optional[AstNodeExpr] + // @astgen op4 := deldatap : Optional[AstNodeExpr] +public: + AstSetuphold(FileLine* fl, AstNodeExpr* refevp, AstNodeExpr* dataevp, + AstNodeExpr* delrefp = nullptr, AstNodeExpr* deldatap = nullptr) + : ASTGEN_SUPER_Setuphold(fl) { + this->refevp(refevp); + this->dataevp(dataevp); + this->delrefp(delrefp); + this->deldatap(deldatap); + } + ASTGEN_MEMBERS_AstSetuphold; + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstStackTraceT final : public AstNodeStmt { + // $stacktrace used as task +public: + explicit AstStackTraceT(FileLine* fl) + : ASTGEN_SUPER_StackTraceT(fl) {} + ASTGEN_MEMBERS_AstStackTraceT; + string verilogKwd() const override { return "$stacktrace"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstStmtExpr final : public AstNodeStmt { + // Expression in statement position + // @astgen op1 := exprp : AstNodeExpr +public: + AstStmtExpr(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_StmtExpr(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstStmtExpr; + bool isPure() override { return exprp()->isPure(); } +}; +class AstStop final : public AstNodeStmt { + const bool m_isFatal; // $fatal not $stop +public: + AstStop(FileLine* fl, bool isFatal) + : ASTGEN_SUPER_Stop(fl) + , m_isFatal{isFatal} {} + ASTGEN_MEMBERS_AstStop; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output + bool isUnlikely() const override { return true; } + int instrCount() const override { return 0; } // Rarely executes + bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } + string emitVerilog() const { return m_isFatal ? "$fatal" : "$stop"; } + bool isFatal() const { return m_isFatal; } +}; +class AstSystemT final : public AstNodeStmt { + // $system used as task + // @astgen op1 := lhsp : AstNodeExpr +public: + AstSystemT(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_SystemT(fl) { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstSystemT; + string verilogKwd() const override { return "$system"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool isUnlikely() const override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstTimeFormat final : public AstNodeStmt { + // Parents: stmtlist + // @astgen op1 := unitsp : Optional[AstNodeExpr] + // @astgen op2 := precisionp : Optional[AstNodeExpr] + // @astgen op3 := suffixp : Optional[AstNodeExpr] + // @astgen op4 := widthp : Optional[AstNodeExpr] +public: + AstTimeFormat(FileLine* fl, AstNodeExpr* unitsp, AstNodeExpr* precisionp, AstNodeExpr* suffixp, + AstNodeExpr* widthp) + : ASTGEN_SUPER_TimeFormat(fl) { + this->unitsp(unitsp); + this->precisionp(precisionp); + this->suffixp(suffixp); + this->widthp(widthp); + } + ASTGEN_MEMBERS_AstTimeFormat; + string verilogKwd() const override { return "$timeformat"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + int instrCount() const override { return INSTR_COUNT_PLI; } +}; +class AstTraceDecl final : public AstNodeStmt { + // Trace point declaration + // Separate from AstTraceInc; as a declaration can't be deleted + // Parents: {statement list} + // Expression being traced - Moved to AstTraceInc by V3Trace + // @astgen op1 := valuep : Optional[AstNodeExpr] + uint32_t m_code{0}; // Trace identifier code + uint32_t m_fidx{0}; // Trace function index + const string m_showname; // Name of variable + const VNumRange m_bitRange; // Property of var the trace details + const VNumRange m_arrayRange; // Property of var the trace details + const VVarType m_varType; // Type of variable (for localparam vs. param) + const VDirection m_declDirection; // Declared direction input/output etc +public: + AstTraceDecl(FileLine* fl, const string& showname, + AstVar* varp, // For input/output state etc + AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange) + : ASTGEN_SUPER_TraceDecl(fl) + , m_showname{showname} + , m_bitRange{bitRange} + , m_arrayRange{arrayRange} + , m_varType{varp->varType()} + , m_declDirection{varp->declDirection()} { + dtypeFrom(valuep); + this->valuep(valuep); + } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 100; } // Large... + ASTGEN_MEMBERS_AstTraceDecl; + string name() const override VL_MT_STABLE { return m_showname; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + bool hasDType() const override VL_MT_SAFE { return true; } + bool sameNode(const AstNode* samep) const override { return false; } + string showname() const { return m_showname; } // * = Var name + // Details on what we're tracing + uint32_t code() const { return m_code; } + void code(uint32_t code) { m_code = code; } + uint32_t fidx() const { return m_fidx; } + void fidx(uint32_t fidx) { m_fidx = fidx; } + uint32_t codeInc() const { + return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1) + * valuep()->dtypep()->widthWords() + * (VL_EDATASIZE / 32); // A code is always 32-bits + } + const VNumRange& bitRange() const { return m_bitRange; } + const VNumRange& arrayRange() const { return m_arrayRange; } + VVarType varType() const { return m_varType; } + VDirection declDirection() const { return m_declDirection; } +}; +class AstTraceInc final : public AstNodeStmt { + // Trace point dump + // @astgen op1 := valuep : AstNodeExpr // Expression being traced (from decl) + // + // @astgen ptr := m_declp : AstTraceDecl // Pointer to declaration + const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc + const VTraceType m_traceType; // Is this a const/full/incremental dump + +public: + AstTraceInc(FileLine* fl, AstTraceDecl* declp, VTraceType traceType, uint32_t baseCode = 0) + : ASTGEN_SUPER_TraceInc(fl) + , m_baseCode{baseCode} + , m_traceType{traceType} + , m_declp{declp} { + dtypeFrom(declp); + // Note: A clone is necessary (instead of using declp()->valuep()), + // for insertion of local temporaries in V3Premit + valuep(declp->valuep()->cloneTree(true)); + } + ASTGEN_MEMBERS_AstTraceInc; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; } + bool hasDType() const override VL_MT_SAFE { return true; } + bool sameNode(const AstNode* samep) const override { + return declp() == VN_DBG_AS(samep, TraceInc)->declp(); + } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isOutputter() override { return true; } + bool isPure() override { return false; } + AstTraceDecl* declp() const { return m_declp; } + VTraceType traceType() const { return m_traceType; } + uint32_t baseCode() const { return m_baseCode; } +}; +class AstTracePopPrefix final : public AstNodeStmt { +public: + explicit AstTracePopPrefix(FileLine* fl) + : ASTGEN_SUPER_TracePopPrefix(fl) {} + ASTGEN_MEMBERS_AstTracePopPrefix; + bool sameNode(const AstNode* samep) const override { return false; } +}; +class AstTracePushPrefix final : public AstNodeStmt { + const string m_prefix; // Prefix to add to signal names + const VTracePrefixType m_prefixType; // Type of prefix being pushed +public: + AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType) + : ASTGEN_SUPER_TracePushPrefix(fl) + , m_prefix{prefix} + , m_prefixType{prefixType} {} + ASTGEN_MEMBERS_AstTracePushPrefix; + bool sameNode(const AstNode* samep) const override { return false; } + string prefix() const { return m_prefix; } + VTracePrefixType prefixType() const { return m_prefixType; } +}; +class AstUCStmt final : public AstNodeStmt { + // User $c statement + // @astgen op1 := exprsp : List[AstNode] // (some are AstText) +public: + AstUCStmt(FileLine* fl, AstNode* exprsp) + : ASTGEN_SUPER_UCStmt(fl) { + addExprsp(exprsp); + } + ASTGEN_MEMBERS_AstUCStmt; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; +class AstWait final : public AstNodeStmt { + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] +public: + AstWait(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp) + : ASTGEN_SUPER_Wait(fl) { + this->condp(condp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstWait; + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } + bool isTimingControl() const override { return true; } +}; +class AstWaitFork final : public AstNodeStmt { + // A "wait fork" statement +public: + explicit AstWaitFork(FileLine* fl) + : ASTGEN_SUPER_WaitFork(fl) {} + ASTGEN_MEMBERS_AstWaitFork; + bool isTimingControl() const override { return true; } +}; +class AstWhile final : public AstNodeStmt { + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] + // @astgen op3 := incsp : List[AstNode] + VOptionBool m_unrollFull; // Full, disable, or default unrolling +public: + AstWhile(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp = nullptr, AstNode* incsp = nullptr) + : ASTGEN_SUPER_While(fl) { + this->condp(condp); + addStmtsp(stmtsp); + addIncsp(incsp); + } + ASTGEN_MEMBERS_AstWhile; + void dump(std::ostream& str) const override; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool sameNode(const AstNode* /*samep*/) const override { return true; } + // Stop statement searchback here + void addNextStmt(AstNode* newp, AstNode* belowp) override; + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } + VOptionBool unrollFull() const { return m_unrollFull; } + void unrollFull(const VOptionBool flag) { m_unrollFull = flag; } +}; + +// === AstNodeAssign === +class AstAssign final : public AstNodeAssign { +public: + AstAssign(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, + AstNode* timingControlp = nullptr) + : ASTGEN_SUPER_Assign(fl, lhsp, rhsp, timingControlp) { + dtypeFrom(lhsp); + } + ASTGEN_MEMBERS_AstAssign; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; + return new AstAssign{fileline(), lhsp, rhsp, controlp}; + } + bool brokeLhsMustBeLvalue() const override { return true; } +}; +class AstAssignAlias final : public AstNodeAssign { + // Like AstAssignW, but a true bidirect interconnection alias + // If both sides are wires, there's no LHS vs RHS, +public: + AstAssignAlias(FileLine* fl, AstVarRef* lhsp, AstVarRef* rhsp) + : ASTGEN_SUPER_AssignAlias(fl, (AstNodeExpr*)lhsp, (AstNodeExpr*)rhsp) {} + ASTGEN_MEMBERS_AstAssignAlias; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + V3ERROR_NA_RETURN(nullptr); + } + bool brokeLhsMustBeLvalue() const override { return false; } +}; +class AstAssignDly final : public AstNodeAssign { +public: + AstAssignDly(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, + AstNode* timingControlp = nullptr) + : ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp, timingControlp) {} + ASTGEN_MEMBERS_AstAssignDly; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; + return new AstAssignDly{fileline(), lhsp, rhsp, controlp}; + } + bool isGateOptimizable() const override { return false; } + string verilogKwd() const override { return "<="; } + bool brokeLhsMustBeLvalue() const override { return true; } +}; +class AstAssignForce final : public AstNodeAssign { + // Procedural 'force' statement +public: + AstAssignForce(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {} + ASTGEN_MEMBERS_AstAssignForce; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstAssignForce{fileline(), lhsp, rhsp}; + } + bool brokeLhsMustBeLvalue() const override { return true; } +}; +class AstAssignPost final : public AstNodeAssign { + // Like Assign, but predelayed assignment requiring special order handling +public: + AstAssignPost(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_AssignPost(fl, lhsp, rhsp) {} + ASTGEN_MEMBERS_AstAssignPost; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstAssignPost{fileline(), lhsp, rhsp}; + } + bool brokeLhsMustBeLvalue() const override { return true; } +}; +class AstAssignPre final : public AstNodeAssign { + // Like Assign, but predelayed assignment requiring special order handling +public: + AstAssignPre(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_AssignPre(fl, lhsp, rhsp) {} + ASTGEN_MEMBERS_AstAssignPre; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstAssignPre{fileline(), lhsp, rhsp}; + } + bool brokeLhsMustBeLvalue() const override { return true; } +}; +class AstAssignVarScope final : public AstNodeAssign { + // Assign two VarScopes to each other +public: + AstAssignVarScope(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_AssignVarScope(fl, lhsp, rhsp) { + dtypeFrom(rhsp); + } + ASTGEN_MEMBERS_AstAssignVarScope; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstAssignVarScope{fileline(), lhsp, rhsp}; + } + bool brokeLhsMustBeLvalue() const override { return false; } +}; +class AstAssignW final : public AstNodeAssign { + // Like assign, but wire/assign's in verilog, the only setting of the specified variable + // @astgen op4 := strengthSpecp : Optional[AstStrengthSpec] +public: + AstAssignW(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, + AstNode* timingControlp = nullptr) + : ASTGEN_SUPER_AssignW(fl, lhsp, rhsp, timingControlp) {} + ASTGEN_MEMBERS_AstAssignW; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; + return new AstAssignW{fileline(), lhsp, rhsp, controlp}; + } + bool isTimingControl() const override { + return timingControlp() || lhsp()->exists([](const AstNodeVarRef* refp) { + return refp->access().isWriteOrRW() && refp->varp()->delayp(); + }); + } + bool brokeLhsMustBeLvalue() const override { return true; } + AstDelay* getLhsNetDelay() const; + AstAlways* convertToAlways(); +}; + +// === AstNodeCase === +class AstCase final : public AstNodeCase { + // Case statement + VCaseType m_casex; // 0=case, 1=casex, 2=casez + bool m_fullPragma = false; // Synthesis full_case + bool m_parallelPragma = false; // Synthesis parallel_case + bool m_uniquePragma = false; // unique case + bool m_unique0Pragma = false; // unique0 case + bool m_priorityPragma = false; // priority case +public: + AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp) + : ASTGEN_SUPER_Case(fl, exprp, itemsp) + , m_casex{casex} {} + ASTGEN_MEMBERS_AstCase; + string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } + bool sameNode(const AstNode* samep) const override { + return m_casex == VN_DBG_AS(samep, Case)->m_casex; + } + bool casex() const { return m_casex == VCaseType::CT_CASEX; } + bool casez() const { return m_casex == VCaseType::CT_CASEZ; } + bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; } + bool caseSimple() const { return m_casex == VCaseType::CT_CASE; } + void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } + bool fullPragma() const { return m_fullPragma; } + void fullPragma(bool flag) { m_fullPragma = flag; } + bool parallelPragma() const { return m_parallelPragma; } + void parallelPragma(bool flag) { m_parallelPragma = flag; } + bool uniquePragma() const { return m_uniquePragma; } + void uniquePragma(bool flag) { m_uniquePragma = flag; } + bool unique0Pragma() const { return m_unique0Pragma; } + void unique0Pragma(bool flag) { m_unique0Pragma = flag; } + bool priorityPragma() const { return m_priorityPragma; } + void priorityPragma(bool flag) { m_priorityPragma = flag; } + string pragmaString() const; +}; +class AstGenCase final : public AstNodeCase { + // Generate Case statement +public: + AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) + : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} + ASTGEN_MEMBERS_AstGenCase; +}; +class AstCoverOtherDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for other than toggle types of coverage + string m_linescov; + int m_offset; // Offset column numbers to uniq-ify IFs +public: + AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, + const string& linescov, int offset) + : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) + , m_linescov{linescov} + , m_offset{offset} {} + ASTGEN_MEMBERS_AstCoverOtherDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int offset() const { return m_offset; } + int size() const override { return 1; } + const string& linescov() const { return m_linescov; } + bool sameNode(const AstNode* samep) const override { + const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); + return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); + } +}; +class AstCoverToggleDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for toggle coverage + const VNumRange m_range; // Packed array range covering each toggle bit +public: + AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, + const VNumRange& range) + : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) + , m_range{range} {} + ASTGEN_MEMBERS_AstCoverToggleDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int size() const override { return m_range.elements(); } + const VNumRange& range() const { return m_range; } + bool sameNode(const AstNode* samep) const override { + const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); + return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); + } +}; + +// === AstNodeCoverOrAssert === +class AstAssert final : public AstNodeCoverOrAssert { + // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey + +public: + ASTGEN_MEMBERS_AstAssert; + AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, VAssertType type, + VAssertDirectiveType directive, const string& name = "") + : ASTGEN_SUPER_Assert(fl, propp, passsp, type, directive, name) { + addFailsp(failsp); + } +}; +class AstAssertIntrinsic final : public AstNodeCoverOrAssert { + // A $cast or other compiler inserted assert, that must run even without --assert option + // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey +public: + ASTGEN_MEMBERS_AstAssertIntrinsic; + AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, + const string& name = "") + // Intrinsic asserts are always enabled thus 'type' field is set to INTERNAL. + : ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, VAssertType::INTERNAL, + VAssertDirectiveType::INTRINSIC, name) { + addFailsp(failsp); + } +}; +class AstCover final : public AstNodeCoverOrAssert { + // @astgen op3 := coverincsp: List[AstNode] // Coverage node +public: + ASTGEN_MEMBERS_AstCover; + AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type, + const string& name = "") + : ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {} +}; +class AstRestrict final : public AstNodeCoverOrAssert { +public: + ASTGEN_MEMBERS_AstRestrict; + AstRestrict(FileLine* fl, AstNode* propp) + // Intrinsic asserts are always ignored thus 'type' field is set to INTERNAL. + : ASTGEN_SUPER_Restrict(fl, propp, nullptr, VAssertType::INTERNAL, + VAssertDirectiveType::RESTRICT) {} +}; + +// === AstNodeFor === +class AstGenFor final : public AstNodeFor { +public: + AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* stmtsp) + : ASTGEN_SUPER_GenFor(fl, initsp, condp, incsp, stmtsp) {} + ASTGEN_MEMBERS_AstGenFor; +}; + +// === AstNodeForeach === +class AstConstraintForeach final : public AstNodeForeach { + // Constraint foreach statement +public: + AstConstraintForeach(FileLine* fl, AstNodeExpr* exprp, AstNode* bodysp) + : ASTGEN_SUPER_ConstraintForeach(fl, exprp, bodysp) {} + ASTGEN_MEMBERS_AstConstraintForeach; +}; +class AstForeach final : public AstNodeForeach { +public: + AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp) + : ASTGEN_SUPER_Foreach(fl, arrayp, stmtsp) {} + ASTGEN_MEMBERS_AstForeach; +}; + +// === AstNodeIf === +class AstConstraintIf final : public AstNodeIf { +public: + AstConstraintIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) + : ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {} + ASTGEN_MEMBERS_AstConstraintIf; +}; +class AstGenIf final : public AstNodeIf { +public: + AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) + : ASTGEN_SUPER_GenIf(fl, condp, thensp, elsesp) {} + ASTGEN_MEMBERS_AstGenIf; +}; +class AstIf final : public AstNodeIf { + bool m_uniquePragma = false; // unique case + bool m_unique0Pragma = false; // unique0 case + bool m_priorityPragma = false; // priority case +public: + AstIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp = nullptr, AstNode* elsesp = nullptr) + : ASTGEN_SUPER_If(fl, condp, thensp, elsesp) {} + ASTGEN_MEMBERS_AstIf; + bool uniquePragma() const { return m_uniquePragma; } + void uniquePragma(bool flag) { m_uniquePragma = flag; } + bool unique0Pragma() const { return m_unique0Pragma; } + void unique0Pragma(bool flag) { m_unique0Pragma = flag; } + bool priorityPragma() const { return m_priorityPragma; } + void priorityPragma(bool flag) { m_priorityPragma = flag; } +}; + +// === AstNodeReadWriteMem === +class AstReadMem final : public AstNodeReadWriteMem { +public: + AstReadMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, + AstNodeExpr* lsbp, AstNodeExpr* msbp) + : ASTGEN_SUPER_ReadMem(fl, hex, filenamep, memp, lsbp, msbp) {} + ASTGEN_MEMBERS_AstReadMem; + string verilogKwd() const override { return (isHex() ? "$readmemh" : "$readmemb"); } + const char* cFuncPrefixp() const override { return "VL_READMEM_"; } +}; +class AstWriteMem final : public AstNodeReadWriteMem { +public: + AstWriteMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, + AstNodeExpr* lsbp, AstNodeExpr* msbp) + : ASTGEN_SUPER_WriteMem(fl, hex, filenamep, memp, lsbp, msbp) {} + ASTGEN_MEMBERS_AstWriteMem; + string verilogKwd() const override { return (isHex() ? "$writememh" : "$writememb"); } + const char* cFuncPrefixp() const override { return "VL_WRITEMEM_"; } +}; + +#endif // Guard From 000d697b510bd9fcd24bf0101b57c58e3f9502a5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 09:08:09 -0400 Subject: [PATCH 173/252] Commentary: Changes update --- Changes | 6 +++++- docs/spelling.txt | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index cabe4e2ac..bbb198a4b 100644 --- a/Changes +++ b/Changes @@ -18,7 +18,7 @@ Verilator 5.039 devel * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add PARAMNODEFAULT error, for parameters without defaults. * Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. -* Add error on missing and mismatching prototypes (#6206) (#6207). [Alex Solomatnikov] +* Add PROTOTYPEMIS error on missing and mismatching prototypes (#6206) (#6207). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add error on class 'function static'. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. @@ -73,7 +73,11 @@ Verilator 5.039 devel * Fix dynamic cast purity (#6267). [Igor Zaworski, Antmicro Ltd.] * Fix same variable on the RHS forced to two different LHSs. (#6269). [Artur Bieniek, Antmicro Ltd.] * Fix spurious VPI value change callbacks (#6274). [Todd Strader] +* Fix stray ']' in Verilog code output for non-constant select (#6277). [Geza Lore] * Fix variables hiding package imports (#6289). [Johan Wouters] +* Fix DFG circular driver tracing. [Geza Lore] +* Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290). [Mateusz Gancarz] +* Fix PowerPC support (#6292). [Sergey Fedorov] Verilator 5.038 2025-07-08 diff --git a/docs/spelling.txt b/docs/spelling.txt index 806748456..34dd7da01 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -471,6 +471,7 @@ Verilog Vighnesh Viktor Vilp +VlWide Vlip Vm Vukobratovic From 340d1aff4a76a20d23a939250899504733ad150a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 09:26:42 -0400 Subject: [PATCH 174/252] Tests: Cleanup to favor '--binary'. No test change intended. --- test_regress/t/t_assert_ctl_concurrent.py | 2 +- test_regress/t/t_assert_ctl_concurrent_noinl.py | 2 +- test_regress/t/t_assigndly_dynamic.py | 2 +- test_regress/t/t_assigndly_dynamic_delay.py | 2 +- test_regress/t/t_clocking_empty_block.py | 2 +- test_regress/t/t_clocking_inout.py | 2 +- test_regress/t/t_clocking_out_on_change.py | 2 +- test_regress/t/t_clocking_react.py | 2 +- test_regress/t/t_clocking_timing1.py | 2 +- test_regress/t/t_clocking_timing2.py | 2 +- test_regress/t/t_clocking_virtual.py | 2 +- test_regress/t/t_clocking_xref.py | 2 +- test_regress/t/t_delay_compare.py | 2 +- test_regress/t/t_delay_var.py | 2 +- test_regress/t/t_disable_fork1.py | 2 +- test_regress/t/t_disable_fork2.py | 2 +- test_regress/t/t_disable_fork3.py | 2 +- test_regress/t/t_disable_genfor2.py | 2 +- test_regress/t/t_display_impure.py | 2 +- test_regress/t/t_event_control_assign.py | 2 +- test_regress/t/t_event_control_pass.py | 2 +- test_regress/t/t_force_chained.py | 2 +- test_regress/t/t_force_func.py | 2 +- test_regress/t/t_force_immediate_release.py | 2 +- test_regress/t/t_force_initial.py | 2 +- test_regress/t/t_force_readwrite.py | 2 +- test_regress/t/t_force_release.py | 2 +- test_regress/t/t_force_rhs_ref.py | 2 +- test_regress/t/t_force_rhs_ref_multi_lhs.py | 2 +- test_regress/t/t_force_rhs_ref_multiple.py | 2 +- test_regress/t/t_fork_block_item_declaration.py | 2 +- test_regress/t/t_fork_dynscope.py | 2 +- test_regress/t/t_fork_join_none_class_cap.py | 2 +- test_regress/t/t_fork_join_none_virtual.py | 2 +- test_regress/t/t_fork_jumpblock.py | 2 +- test_regress/t/t_fork_label_timing.py | 2 +- test_regress/t/t_fork_none_var.py | 2 +- test_regress/t/t_fork_output_arg.py | 2 +- test_regress/t/t_fork_port.py | 2 +- test_regress/t/t_interface_virtual_controlflow.py | 2 +- test_regress/t/t_interface_virtual_sched_ico.py | 2 +- test_regress/t/t_interface_virtual_sched_nba.py | 2 +- test_regress/t/t_interface_virtual_timing.py | 2 +- test_regress/t/t_jumps_uninit_destructor_call.py | 2 +- test_regress/t/t_lint_infinite.py | 2 +- test_regress/t/t_mailbox.py | 2 +- test_regress/t/t_mailbox_std.py | 2 +- test_regress/t/t_mailbox_unbounded.py | 2 +- test_regress/t/t_math_signed3.py | 2 +- test_regress/t/t_math_signed3_noopt.py | 2 +- test_regress/t/t_nba_commit_queue_suspenable.py | 2 +- test_regress/t/t_no_std_bad.py | 2 +- test_regress/t/t_no_std_pkg_bad.py | 2 +- test_regress/t/t_param_slice.py | 2 +- test_regress/t/t_process_propagation.py | 2 +- test_regress/t/t_process_task.py | 2 +- test_regress/t/t_semaphore.py | 2 +- test_regress/t/t_semaphore_std.py | 2 +- test_regress/t/t_std_process_self.py | 2 +- test_regress/t/t_std_process_self_std.py | 2 +- test_regress/t/t_stream5.py | 2 +- test_regress/t/t_sys_delta_monitor.py | 2 +- test_regress/t/t_time.py | 2 +- test_regress/t/t_timing_always.py | 2 +- test_regress/t/t_timing_class.py | 2 +- test_regress/t/t_timing_class_static_delay.py | 2 +- test_regress/t/t_timing_clkgen1.py | 2 +- test_regress/t/t_timing_clkgen2.py | 2 +- test_regress/t/t_timing_clkgen3.py | 2 +- test_regress/t/t_timing_delay_callstack.py | 2 +- test_regress/t/t_timing_dlyassign.py | 2 +- test_regress/t/t_timing_dynscope.py | 2 +- test_regress/t/t_timing_events.py | 2 +- test_regress/t/t_timing_fork_comb.py | 2 +- test_regress/t/t_timing_fork_join.py | 2 +- test_regress/t/t_timing_fork_join_forkproc.py | 2 +- test_regress/t/t_timing_fork_many.py | 2 +- test_regress/t/t_timing_fork_no_timing_ctrl.py | 2 +- test_regress/t/t_timing_fork_rec_method.py | 2 +- test_regress/t/t_timing_fork_taskcall.py | 2 +- test_regress/t/t_timing_initial_always.py | 2 +- test_regress/t/t_timing_localevent.py | 2 +- test_regress/t/t_timing_long.py | 4 ++-- test_regress/t/t_timing_nba_1.py | 2 +- test_regress/t/t_timing_nba_2.py | 2 +- test_regress/t/t_timing_off.py | 2 +- test_regress/t/t_timing_osc.py | 2 +- test_regress/t/t_timing_pong.py | 2 +- test_regress/t/t_timing_protect.py | 2 +- test_regress/t/t_timing_reentry.py | 2 +- test_regress/t/t_timing_sched.py | 2 +- test_regress/t/t_timing_sched_if.py | 2 +- test_regress/t/t_timing_sched_nba.py | 2 +- test_regress/t/t_timing_strobe.py | 2 +- test_regress/t/t_timing_timescale.py | 2 +- test_regress/t/t_timing_trace.py | 2 +- test_regress/t/t_timing_trace_fst.py | 2 +- test_regress/t/t_timing_wait1.py | 2 +- test_regress/t/t_timing_wait2.py | 2 +- test_regress/t/t_timing_wait3.py | 2 +- test_regress/t/t_timing_wait_long.py | 2 +- test_regress/t/t_timing_write_expr.py | 2 +- test_regress/t/t_timing_zerodly.py | 2 +- test_regress/t/t_timing_zerodly_consecutive.py | 2 +- test_regress/t/t_tri_no_top.py | 9 ++++----- test_regress/t/t_tri_struct.py | 2 +- test_regress/t/t_tri_struct_packed.py | 2 +- test_regress/t/t_tri_struct_pins_inout.py | 2 +- test_regress/t/t_var_extern_method_lifetime.py | 2 +- test_regress/t/t_vlt_timing.py | 2 +- test_regress/t/t_wait_fork.py | 2 +- test_regress/t/t_wide_temp_while_cond.py | 3 +-- 112 files changed, 116 insertions(+), 118 deletions(-) diff --git a/test_regress/t/t_assert_ctl_concurrent.py b/test_regress/t/t_assert_ctl_concurrent.py index bf8998d5c..dddff8e39 100755 --- a/test_regress/t/t_assert_ctl_concurrent.py +++ b/test_regress/t/t_assert_ctl_concurrent.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing --assert"]) +test.compile(verilator_flags2=["--binary --assert"]) test.execute() diff --git a/test_regress/t/t_assert_ctl_concurrent_noinl.py b/test_regress/t/t_assert_ctl_concurrent_noinl.py index d96a4a589..92c1105ac 100755 --- a/test_regress/t/t_assert_ctl_concurrent_noinl.py +++ b/test_regress/t/t_assert_ctl_concurrent_noinl.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('vlt') test.top_filename = "t_assert_ctl_concurrent.v" -test.compile(verilator_flags2=["--exe --main --timing --assert --fno-inline"]) +test.compile(verilator_flags2=["--binary --assert --fno-inline"]) test.execute() diff --git a/test_regress/t/t_assigndly_dynamic.py b/test_regress/t/t_assigndly_dynamic.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_assigndly_dynamic.py +++ b/test_regress/t/t_assigndly_dynamic.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_assigndly_dynamic_delay.py b/test_regress/t/t_assigndly_dynamic_delay.py index 6fc5a819d..a3df69d2b 100755 --- a/test_regress/t/t_assigndly_dynamic_delay.py +++ b/test_regress/t/t_assigndly_dynamic_delay.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_assigndly_dynamic" -test.compile(verilator_flags2=["--exe --main --timing +define+WITH_DELAY"]) +test.compile(verilator_flags2=["--binary +define+WITH_DELAY"]) test.execute() diff --git a/test_regress/t/t_clocking_empty_block.py b/test_regress/t/t_clocking_empty_block.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_clocking_empty_block.py +++ b/test_regress/t/t_clocking_empty_block.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_clocking_inout.py b/test_regress/t/t_clocking_inout.py index d05629d98..a4406cb80 100755 --- a/test_regress/t/t_clocking_inout.py +++ b/test_regress/t/t_clocking_inout.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"], make_main=False) +test.compile(verilator_flags2=["--binary"], make_main=False) test.execute() diff --git a/test_regress/t/t_clocking_out_on_change.py b/test_regress/t/t_clocking_out_on_change.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_clocking_out_on_change.py +++ b/test_regress/t/t_clocking_out_on_change.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_clocking_react.py b/test_regress/t/t_clocking_react.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_clocking_react.py +++ b/test_regress/t/t_clocking_react.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_clocking_timing1.py b/test_regress/t/t_clocking_timing1.py index 01e5d2048..23fe7bdeb 100755 --- a/test_regress/t/t_clocking_timing1.py +++ b/test_regress/t/t_clocking_timing1.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_clocking_timing.v" -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_clocking_timing2.py b/test_regress/t/t_clocking_timing2.py index 702df2678..f34cb10f1 100755 --- a/test_regress/t/t_clocking_timing2.py +++ b/test_regress/t/t_clocking_timing2.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_clocking_timing.v" -test.compile(verilator_flags2=["--exe --main --timing -DTEST_INPUT_SKEW=12 -DTEST_OUTPUT_SKEW=16"]) +test.compile(verilator_flags2=["--binary -DTEST_INPUT_SKEW=12 -DTEST_OUTPUT_SKEW=16"]) test.execute() diff --git a/test_regress/t/t_clocking_virtual.py b/test_regress/t/t_clocking_virtual.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_clocking_virtual.py +++ b/test_regress/t/t_clocking_virtual.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_clocking_xref.py b/test_regress/t/t_clocking_xref.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_clocking_xref.py +++ b/test_regress/t/t_clocking_xref.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_delay_compare.py b/test_regress/t/t_delay_compare.py index 1806fa071..9185571ea 100755 --- a/test_regress/t/t_delay_compare.py +++ b/test_regress/t/t_delay_compare.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) if not test.vlt: test.execute() diff --git a/test_regress/t/t_delay_var.py b/test_regress/t/t_delay_var.py index 1806fa071..9185571ea 100755 --- a/test_regress/t/t_delay_var.py +++ b/test_regress/t/t_delay_var.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) if not test.vlt: test.execute() diff --git a/test_regress/t/t_disable_fork1.py b/test_regress/t/t_disable_fork1.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_disable_fork1.py +++ b/test_regress/t/t_disable_fork1.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_disable_fork2.py b/test_regress/t/t_disable_fork2.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_disable_fork2.py +++ b/test_regress/t/t_disable_fork2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_disable_fork3.py b/test_regress/t/t_disable_fork3.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_disable_fork3.py +++ b/test_regress/t/t_disable_fork3.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_disable_genfor2.py b/test_regress/t/t_disable_genfor2.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_disable_genfor2.py +++ b/test_regress/t/t_disable_genfor2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_display_impure.py b/test_regress/t/t_display_impure.py index 90d5ab862..0dc2c2a5d 100755 --- a/test_regress/t/t_display_impure.py +++ b/test_regress/t/t_display_impure.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('linter') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_event_control_assign.py b/test_regress/t/t_event_control_assign.py index 9516162d0..913d8d7fd 100755 --- a/test_regress/t/t_event_control_assign.py +++ b/test_regress/t/t_event_control_assign.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.compile( - verilator_flags2=["--exe --main --timing"], + verilator_flags2=["--binary"], make_main=False, # Multithreading would cause a warning on event assignments threads=1) diff --git a/test_regress/t/t_event_control_pass.py b/test_regress/t/t_event_control_pass.py index 483af3382..263880576 100755 --- a/test_regress/t/t_event_control_pass.py +++ b/test_regress/t/t_event_control_pass.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"], threads=1) +test.compile(verilator_flags2=["--binary"], threads=1) test.execute() diff --git a/test_regress/t/t_force_chained.py b/test_regress/t/t_force_chained.py index 61632dd07..8084f4e71 100755 --- a/test_regress/t/t_force_chained.py +++ b/test_regress/t/t_force_chained.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_force_func.py b/test_regress/t/t_force_func.py index 61632dd07..8084f4e71 100755 --- a/test_regress/t/t_force_func.py +++ b/test_regress/t/t_force_func.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_force_immediate_release.py b/test_regress/t/t_force_immediate_release.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_immediate_release.py +++ b/test_regress/t/t_force_immediate_release.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_force_initial.py b/test_regress/t/t_force_initial.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_initial.py +++ b/test_regress/t/t_force_initial.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_force_readwrite.py b/test_regress/t/t_force_readwrite.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_readwrite.py +++ b/test_regress/t/t_force_readwrite.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_force_release.py b/test_regress/t/t_force_release.py index 61632dd07..8084f4e71 100755 --- a/test_regress/t/t_force_release.py +++ b/test_regress/t/t_force_release.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_force_rhs_ref.py b/test_regress/t/t_force_rhs_ref.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_rhs_ref.py +++ b/test_regress/t/t_force_rhs_ref.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_force_rhs_ref_multi_lhs.py b/test_regress/t/t_force_rhs_ref_multi_lhs.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_rhs_ref_multi_lhs.py +++ b/test_regress/t/t_force_rhs_ref_multi_lhs.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_force_rhs_ref_multiple.py b/test_regress/t/t_force_rhs_ref_multiple.py index 4ff4dfaa5..aa5dacc93 100755 --- a/test_regress/t/t_force_rhs_ref_multiple.py +++ b/test_regress/t/t_force_rhs_ref_multiple.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe", "--main", "--timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_block_item_declaration.py b/test_regress/t/t_fork_block_item_declaration.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_block_item_declaration.py +++ b/test_regress/t/t_fork_block_item_declaration.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_dynscope.py b/test_regress/t/t_fork_dynscope.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_dynscope.py +++ b/test_regress/t/t_fork_dynscope.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_join_none_class_cap.py b/test_regress/t/t_fork_join_none_class_cap.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_join_none_class_cap.py +++ b/test_regress/t/t_fork_join_none_class_cap.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_join_none_virtual.py b/test_regress/t/t_fork_join_none_virtual.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_join_none_virtual.py +++ b/test_regress/t/t_fork_join_none_virtual.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_jumpblock.py b/test_regress/t/t_fork_jumpblock.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_jumpblock.py +++ b/test_regress/t/t_fork_jumpblock.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_label_timing.py b/test_regress/t/t_fork_label_timing.py index 21739800c..e7139372c 100755 --- a/test_regress/t/t_fork_label_timing.py +++ b/test_regress/t/t_fork_label_timing.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_fork_label.v" -test.compile(verilator_flags2=["--exe --main --timing"], make_main=False) +test.compile(verilator_flags2=["--binary"], make_main=False) test.execute() diff --git a/test_regress/t/t_fork_none_var.py b/test_regress/t/t_fork_none_var.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_none_var.py +++ b/test_regress/t/t_fork_none_var.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_output_arg.py b/test_regress/t/t_fork_output_arg.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_fork_output_arg.py +++ b/test_regress/t/t_fork_output_arg.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_fork_port.py b/test_regress/t/t_fork_port.py index 664ad3bec..a1700c6d9 100755 --- a/test_regress/t/t_fork_port.py +++ b/test_regress/t/t_fork_port.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_interface_virtual_controlflow.py b/test_regress/t/t_interface_virtual_controlflow.py index f163beb3f..65da60c84 100755 --- a/test_regress/t/t_interface_virtual_controlflow.py +++ b/test_regress/t/t_interface_virtual_controlflow.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -fno-reorder"]) +test.compile(verilator_flags2=["--binary -fno-reorder"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_ico.py b/test_regress/t/t_interface_virtual_sched_ico.py index 980753f6d..79de66837 100755 --- a/test_regress/t/t_interface_virtual_sched_ico.py +++ b/test_regress/t/t_interface_virtual_sched_ico.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_nba.py b/test_regress/t/t_interface_virtual_sched_nba.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_interface_virtual_sched_nba.py +++ b/test_regress/t/t_interface_virtual_sched_nba.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_timing.py b/test_regress/t/t_interface_virtual_timing.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_interface_virtual_timing.py +++ b/test_regress/t/t_interface_virtual_timing.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_jumps_uninit_destructor_call.py b/test_regress/t/t_jumps_uninit_destructor_call.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_jumps_uninit_destructor_call.py +++ b/test_regress/t/t_jumps_uninit_destructor_call.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_lint_infinite.py b/test_regress/t/t_lint_infinite.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_lint_infinite.py +++ b/test_regress/t/t_lint_infinite.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_mailbox.py b/test_regress/t/t_mailbox.py index e84a7d7e7..b2249f588 100755 --- a/test_regress/t/t_mailbox.py +++ b/test_regress/t/t_mailbox.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wall"]) +test.compile(verilator_flags2=["--binary -Wall"]) test.execute() diff --git a/test_regress/t/t_mailbox_std.py b/test_regress/t/t_mailbox_std.py index 10f8af694..dd4ee7dba 100755 --- a/test_regress/t/t_mailbox_std.py +++ b/test_regress/t/t_mailbox_std.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_mailbox.v" -test.compile(verilator_flags2=["--exe --main --timing -Wall --Wpedantic -DMAILBOX_T=std::mailbox"]) +test.compile(verilator_flags2=["--binary -Wall --Wpedantic -DMAILBOX_T=std::mailbox"]) test.execute() diff --git a/test_regress/t/t_mailbox_unbounded.py b/test_regress/t/t_mailbox_unbounded.py index e84a7d7e7..b2249f588 100755 --- a/test_regress/t/t_mailbox_unbounded.py +++ b/test_regress/t/t_mailbox_unbounded.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wall"]) +test.compile(verilator_flags2=["--binary -Wall"]) test.execute() diff --git a/test_regress/t/t_math_signed3.py b/test_regress/t/t_math_signed3.py index 43eddedd0..5579de2ce 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(make_main=False, verilator_flags2=["--main", "--exe", "--timing"]) +test.compile(make_main=False, verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_math_signed3_noopt.py b/test_regress/t/t_math_signed3_noopt.py index 75d6e33d2..d3177f773 100755 --- a/test_regress/t/t_math_signed3_noopt.py +++ b/test_regress/t/t_math_signed3_noopt.py @@ -13,7 +13,7 @@ test.scenarios('simulator') test.top_filename = "t/t_math_signed3.v" -test.compile(make_main=False, verilator_flags2=["-O0", "--main", "--exe", "--timing"]) +test.compile(make_main=False, verilator_flags2=["-O0", "--binary"]) test.execute() diff --git a/test_regress/t/t_nba_commit_queue_suspenable.py b/test_regress/t/t_nba_commit_queue_suspenable.py index edf692f39..c27c4ac25 100755 --- a/test_regress/t/t_nba_commit_queue_suspenable.py +++ b/test_regress/t/t_nba_commit_queue_suspenable.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile(verilator_flags2=["--exe", "--main", "--timing", "-unroll-count 1", "--stats"]) +test.compile(verilator_flags2=["--binary", "-unroll-count 1", "--stats"]) test.execute() diff --git a/test_regress/t/t_no_std_bad.py b/test_regress/t/t_no_std_bad.py index 4099b8baf..e186f97ef 100755 --- a/test_regress/t/t_no_std_bad.py +++ b/test_regress/t/t_no_std_bad.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('linter') test.lint(fails=True, - verilator_flags2=["--no-std", "--exe --main --timing -Wall"], + verilator_flags2=["--no-std", "--binary -Wall"], expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_no_std_pkg_bad.py b/test_regress/t/t_no_std_pkg_bad.py index 1334e5b4f..efc541e29 100755 --- a/test_regress/t/t_no_std_pkg_bad.py +++ b/test_regress/t/t_no_std_pkg_bad.py @@ -14,7 +14,7 @@ test.top_filename = "t/t_no_std_bad.v" test.golden_filename = "t/t_no_std_bad.out" test.lint(fails=True, - verilator_flags2=["--no-std-package", "--exe --main --timing -Wall"], + verilator_flags2=["--no-std-package", "--binary -Wall"], expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_param_slice.py b/test_regress/t/t_param_slice.py index 9a62155e4..25010f779 100755 --- a/test_regress/t/t_param_slice.py +++ b/test_regress/t/t_param_slice.py @@ -10,7 +10,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=['--exe', '--main', '--timing']) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_process_propagation.py b/test_regress/t/t_process_propagation.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_process_propagation.py +++ b/test_regress/t/t_process_propagation.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_process_task.py b/test_regress/t/t_process_task.py index a57ed056c..80206c4ae 100755 --- a/test_regress/t/t_process_task.py +++ b/test_regress/t/t_process_task.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(v_flags2=["--exe --main --timing"]) +test.compile(v_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_semaphore.py b/test_regress/t/t_semaphore.py index e84a7d7e7..b2249f588 100755 --- a/test_regress/t/t_semaphore.py +++ b/test_regress/t/t_semaphore.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wall"]) +test.compile(verilator_flags2=["--binary -Wall"]) test.execute() diff --git a/test_regress/t/t_semaphore_std.py b/test_regress/t/t_semaphore_std.py index 1b61c0548..fa28f1d2d 100755 --- a/test_regress/t/t_semaphore_std.py +++ b/test_regress/t/t_semaphore_std.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_semaphore.v" -test.compile(verilator_flags2=["--exe --main --timing -Wall -DSEMAPHORE_T=std::semaphore"]) +test.compile(verilator_flags2=["--binary -Wall -DSEMAPHORE_T=std::semaphore"]) test.execute() diff --git a/test_regress/t/t_std_process_self.py b/test_regress/t/t_std_process_self.py index d8b6a325d..401e3d56e 100755 --- a/test_regress/t/t_std_process_self.py +++ b/test_regress/t/t_std_process_self.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('simulator') -test.lint(verilator_flags2=["--exe --main --timing"]) +test.lint(verilator_flags2=["--binary"]) test.passes() diff --git a/test_regress/t/t_std_process_self_std.py b/test_regress/t/t_std_process_self_std.py index 94cce2d22..ca3d1c05c 100755 --- a/test_regress/t/t_std_process_self_std.py +++ b/test_regress/t/t_std_process_self_std.py @@ -12,6 +12,6 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_std_process_self.v" -test.lint(verilator_flags2=["--exe --main --timing --DUSE_STD_PREFIX"]) +test.lint(verilator_flags2=["--binary --DUSE_STD_PREFIX"]) test.passes() diff --git a/test_regress/t/t_stream5.py b/test_regress/t/t_stream5.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_stream5.py +++ b/test_regress/t/t_stream5.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_sys_delta_monitor.py b/test_regress/t/t_sys_delta_monitor.py index f9e3a2b05..957a57046 100755 --- a/test_regress/t/t_sys_delta_monitor.py +++ b/test_regress/t/t_sys_delta_monitor.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing --timescale 1ns/1ns"]) +test.compile(verilator_flags2=["--binary --timescale 1ns/1ns"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_time.py b/test_regress/t/t_time.py index 88cfbe63f..2bf2a6f4f 100755 --- a/test_regress/t/t_time.py +++ b/test_regress/t/t_time.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('linter') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_always.py b/test_regress/t/t_timing_always.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_always.py +++ b/test_regress/t/t_timing_always.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_class.py b/test_regress/t/t_timing_class.py index 664ad3bec..a1700c6d9 100755 --- a/test_regress/t/t_timing_class.py +++ b/test_regress/t/t_timing_class.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_class_static_delay.py b/test_regress/t/t_timing_class_static_delay.py index 664ad3bec..a1700c6d9 100755 --- a/test_regress/t/t_timing_class_static_delay.py +++ b/test_regress/t/t_timing_class_static_delay.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_clkgen1.py b/test_regress/t/t_timing_clkgen1.py index 19a494154..4fb18ff23 100755 --- a/test_regress/t/t_timing_clkgen1.py +++ b/test_regress/t/t_timing_clkgen1.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wno-MINTYPMAXDLY"]) +test.compile(verilator_flags2=["--binary -Wno-MINTYPMAXDLY"]) test.execute() diff --git a/test_regress/t/t_timing_clkgen2.py b/test_regress/t/t_timing_clkgen2.py index 500334384..c09383bcc 100755 --- a/test_regress/t/t_timing_clkgen2.py +++ b/test_regress/t/t_timing_clkgen2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wwarn-BLKSEQ"]) +test.compile(verilator_flags2=["--binary -Wwarn-BLKSEQ"]) test.execute() diff --git a/test_regress/t/t_timing_clkgen3.py b/test_regress/t/t_timing_clkgen3.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_clkgen3.py +++ b/test_regress/t/t_timing_clkgen3.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_delay_callstack.py b/test_regress/t/t_timing_delay_callstack.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_delay_callstack.py +++ b/test_regress/t/t_timing_delay_callstack.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_dlyassign.py b/test_regress/t/t_timing_dlyassign.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_dlyassign.py +++ b/test_regress/t/t_timing_dlyassign.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_dynscope.py b/test_regress/t/t_timing_dynscope.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_dynscope.py +++ b/test_regress/t/t_timing_dynscope.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_events.py b/test_regress/t/t_timing_events.py index 664ad3bec..a1700c6d9 100755 --- a/test_regress/t/t_timing_events.py +++ b/test_regress/t/t_timing_events.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_fork_comb.py b/test_regress/t/t_timing_fork_comb.py index cf21ef4d2..3f559445e 100755 --- a/test_regress/t/t_timing_fork_comb.py +++ b/test_regress/t/t_timing_fork_comb.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wno-UNOPTFLAT"]) +test.compile(verilator_flags2=["--binary -Wno-UNOPTFLAT"]) test.execute() diff --git a/test_regress/t/t_timing_fork_join.py b/test_regress/t/t_timing_fork_join.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_timing_fork_join.py +++ b/test_regress/t/t_timing_fork_join.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_fork_join_forkproc.py b/test_regress/t/t_timing_fork_join_forkproc.py index 75f4712aa..e4bcc8e1a 100755 --- a/test_regress/t/t_timing_fork_join_forkproc.py +++ b/test_regress/t/t_timing_fork_join_forkproc.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_timing_fork_join.v" -test.compile(verilator_flags2=["--exe --main --timing --ftaskify-all-forked"]) +test.compile(verilator_flags2=["--binary --ftaskify-all-forked"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_fork_many.py b/test_regress/t/t_timing_fork_many.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_fork_many.py +++ b/test_regress/t/t_timing_fork_many.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_fork_no_timing_ctrl.py b/test_regress/t/t_timing_fork_no_timing_ctrl.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_fork_no_timing_ctrl.py +++ b/test_regress/t/t_timing_fork_no_timing_ctrl.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_fork_rec_method.py b/test_regress/t/t_timing_fork_rec_method.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_fork_rec_method.py +++ b/test_regress/t/t_timing_fork_rec_method.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_fork_taskcall.py b/test_regress/t/t_timing_fork_taskcall.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_fork_taskcall.py +++ b/test_regress/t/t_timing_fork_taskcall.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_initial_always.py b/test_regress/t/t_timing_initial_always.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_initial_always.py +++ b/test_regress/t/t_timing_initial_always.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_localevent.py b/test_regress/t/t_timing_localevent.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_localevent.py +++ b/test_regress/t/t_timing_localevent.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_long.py b/test_regress/t/t_timing_long.py index a523eb21d..1762e724c 100755 --- a/test_regress/t/t_timing_long.py +++ b/test_regress/t/t_timing_long.py @@ -52,11 +52,11 @@ def gen(filename): gen(test.top_filename) if test.have_coroutines: - test.compile(verilator_flags2=["--exe --build --main --timing"], make_top=1) + test.compile(verilator_flags2=["--binary"], make_top=1) test.execute() -test.compile(verilator_flags2=["--exe --build --main --no-timing -Wno-STMTDLY"], make_top=1) +test.compile(verilator_flags2=["--binary --no-timing -Wno-STMTDLY"], make_top=1) test.execute() diff --git a/test_regress/t/t_timing_nba_1.py b/test_regress/t/t_timing_nba_1.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_nba_1.py +++ b/test_regress/t/t_timing_nba_1.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_nba_2.py b/test_regress/t/t_timing_nba_2.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_nba_2.py +++ b/test_regress/t/t_timing_nba_2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_off.py b/test_regress/t/t_timing_off.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_off.py +++ b/test_regress/t/t_timing_off.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_osc.py b/test_regress/t/t_timing_osc.py index 6bff8c8f0..ad21d33c5 100755 --- a/test_regress/t/t_timing_osc.py +++ b/test_regress/t/t_timing_osc.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing --trace-vcd"], make_main=False) +test.compile(verilator_flags2=["--binary --trace-vcd"], make_main=False) test.execute() diff --git a/test_regress/t/t_timing_pong.py b/test_regress/t/t_timing_pong.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_pong.py +++ b/test_regress/t/t_timing_pong.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_protect.py b/test_regress/t/t_timing_protect.py index 9e828acb2..293a137e9 100755 --- a/test_regress/t/t_timing_protect.py +++ b/test_regress/t/t_timing_protect.py @@ -15,7 +15,7 @@ test.top_filename = "t/t_timing_fork_join.v" # Contains all relevant constructs if not test.have_coroutines: test.skip("No coroutine support") -test.compile(verilator_flags2=["--exe --main --timing --protect-ids", "--protect-key SECRET_KEY"]) +test.compile(verilator_flags2=["--binary --protect-ids", "--protect-key SECRET_KEY"]) test.execute() diff --git a/test_regress/t/t_timing_reentry.py b/test_regress/t/t_timing_reentry.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_reentry.py +++ b/test_regress/t/t_timing_reentry.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_sched.py b/test_regress/t/t_timing_sched.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_sched.py +++ b/test_regress/t/t_timing_sched.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_sched_if.py b/test_regress/t/t_timing_sched_if.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_sched_if.py +++ b/test_regress/t/t_timing_sched_if.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_sched_nba.py b/test_regress/t/t_timing_sched_nba.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_sched_nba.py +++ b/test_regress/t/t_timing_sched_nba.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_strobe.py b/test_regress/t/t_timing_strobe.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_timing_strobe.py +++ b/test_regress/t/t_timing_strobe.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_timescale.py b/test_regress/t/t_timing_timescale.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_timing_timescale.py +++ b/test_regress/t/t_timing_timescale.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_trace.py b/test_regress/t/t_timing_trace.py index 1adbf685b..a9e870917 100755 --- a/test_regress/t/t_timing_trace.py +++ b/test_regress/t/t_timing_trace.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing --trace-vcd -Wno-MINTYPMAXDLY"]) +test.compile(verilator_flags2=["--binary --trace-vcd -Wno-MINTYPMAXDLY"]) test.execute() diff --git a/test_regress/t/t_timing_trace_fst.py b/test_regress/t/t_timing_trace_fst.py index 727cb9ff6..07adb63b1 100755 --- a/test_regress/t/t_timing_trace_fst.py +++ b/test_regress/t/t_timing_trace_fst.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_timing_trace.v" -test.compile(verilator_flags2=["--exe --main --timing --trace-fst -Wno-MINTYPMAXDLY"]) +test.compile(verilator_flags2=["--binary --trace-fst -Wno-MINTYPMAXDLY"]) test.execute() diff --git a/test_regress/t/t_timing_wait1.py b/test_regress/t/t_timing_wait1.py index d1f037fe6..c363d0e6e 100755 --- a/test_regress/t/t_timing_wait1.py +++ b/test_regress/t/t_timing_wait1.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing -Wno-WAITCONST"]) +test.compile(verilator_flags2=["--binary -Wno-WAITCONST"]) test.execute() diff --git a/test_regress/t/t_timing_wait2.py b/test_regress/t/t_timing_wait2.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_timing_wait2.py +++ b/test_regress/t/t_timing_wait2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_wait3.py b/test_regress/t/t_timing_wait3.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_wait3.py +++ b/test_regress/t/t_timing_wait3.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_wait_long.py b/test_regress/t/t_timing_wait_long.py index bded720e2..1407fff26 100755 --- a/test_regress/t/t_timing_wait_long.py +++ b/test_regress/t/t_timing_wait_long.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_timing_write_expr.py b/test_regress/t/t_timing_write_expr.py index d05629d98..a4406cb80 100755 --- a/test_regress/t/t_timing_write_expr.py +++ b/test_regress/t/t_timing_write_expr.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"], make_main=False) +test.compile(verilator_flags2=["--binary"], make_main=False) test.execute() diff --git a/test_regress/t/t_timing_zerodly.py b/test_regress/t/t_timing_zerodly.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_zerodly.py +++ b/test_regress/t/t_timing_zerodly.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_timing_zerodly_consecutive.py b/test_regress/t/t_timing_zerodly_consecutive.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_timing_zerodly_consecutive.py +++ b/test_regress/t/t_timing_zerodly_consecutive.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_tri_no_top.py b/test_regress/t/t_tri_no_top.py index 22d83a776..0e7d8d554 100755 --- a/test_regress/t/t_tri_no_top.py +++ b/test_regress/t/t_tri_no_top.py @@ -11,11 +11,10 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(make_top_shell=False, - make_main=False, - verilator_flags2=[ - "--exe --pins-inout-enables --timing --main", test.t_dir + "/t_tri_top_en_out.v" - ]) +test.compile( + make_top_shell=False, + make_main=False, + verilator_flags2=["--binary --pins-inout-enables", test.t_dir + "/t_tri_top_en_out.v"]) test.execute() diff --git a/test_regress/t/t_tri_struct.py b/test_regress/t/t_tri_struct.py index 2ffd2b3e0..2b4837fcc 100755 --- a/test_regress/t/t_tri_struct.py +++ b/test_regress/t/t_tri_struct.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator_st') -test.compile(verilator_flags2=['--exe --main --timing']) +test.compile(verilator_flags2=['--binary']) test.execute() diff --git a/test_regress/t/t_tri_struct_packed.py b/test_regress/t/t_tri_struct_packed.py index b5b4b8643..8c78c7f82 100755 --- a/test_regress/t/t_tri_struct_packed.py +++ b/test_regress/t/t_tri_struct_packed.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=['--exe --main --timing']) +test.compile(verilator_flags2=['--binary']) test.execute(fails=test.vlt_all, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_tri_struct_pins_inout.py b/test_regress/t/t_tri_struct_pins_inout.py index 3bcf9231a..bc97e2d68 100755 --- a/test_regress/t/t_tri_struct_pins_inout.py +++ b/test_regress/t/t_tri_struct_pins_inout.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator_st') test.top_filename = "t/t_tri_struct.v" -test.compile(verilator_flags2=['--exe --pins-inout-enables --main --timing']) +test.compile(verilator_flags2=['--binary --pins-inout-enables']) test.execute() diff --git a/test_regress/t/t_var_extern_method_lifetime.py b/test_regress/t/t_var_extern_method_lifetime.py index a4e75e7ad..c53b55262 100755 --- a/test_regress/t/t_var_extern_method_lifetime.py +++ b/test_regress/t/t_var_extern_method_lifetime.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_vlt_timing.py b/test_regress/t/t_vlt_timing.py index 0feac9429..fb26f1e48 100755 --- a/test_regress/t/t_vlt_timing.py +++ b/test_regress/t/t_vlt_timing.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_timing_off.v" -test.compile(verilator_flags2=["--exe --main --timing t/t_vlt_timing.vlt"]) +test.compile(verilator_flags2=["--binary t/t_vlt_timing.vlt"]) test.execute() diff --git a/test_regress/t/t_wait_fork.py b/test_regress/t/t_wait_fork.py index 88cfbe63f..2bf2a6f4f 100755 --- a/test_regress/t/t_wait_fork.py +++ b/test_regress/t/t_wait_fork.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('linter') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_wide_temp_while_cond.py b/test_regress/t/t_wide_temp_while_cond.py index 9df9caa86..d64df839f 100755 --- a/test_regress/t/t_wide_temp_while_cond.py +++ b/test_regress/t/t_wide_temp_while_cond.py @@ -11,8 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile( - verilator_flags2=["--exe", "--main", "--timing", "--stats", "t/t_wide_temp_while_cond.cpp"]) +test.compile(verilator_flags2=["--binary", "--stats", "t/t_wide_temp_while_cond.cpp"]) if test.vlt or test.vltmt: test.file_grep(test.stats, r'Optimizations, Prelim temporary variables created\s+(\d+)', 3) From 48a12fb0f4e4082b1dbb715df9394d651fa6f49d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 11:47:19 -0400 Subject: [PATCH 175/252] Document and test `+verilator+rand+reset+2` usage (#6285 partial) --- docs/guide/exe_verilator.rst | 23 ++++-- test_regress/t/t_alw_split_rst.v | 30 +++---- test_regress/t/t_clk_vecgen1.py | 2 +- test_regress/t/t_clocking_inout.v | 2 +- test_regress/t/t_clocking_xref.v | 8 +- test_regress/t/t_dpi_accessors.cpp | 81 ++++++++++--------- test_regress/t/t_dpi_shortcircuit.v | 2 +- test_regress/t/t_dpi_shortcircuit2.v | 2 +- test_regress/t/t_event_control_scope_var.v | 2 +- test_regress/t/t_for_count.v | 4 +- test_regress/t/t_func_check.v | 2 +- test_regress/t/t_interface_array.v | 8 +- test_regress/t/t_mem_multi_io.v | 12 ++- test_regress/t/t_mem_slot.v | 4 +- test_regress/t/t_mod_interface_array0.v | 1 + test_regress/t/t_mod_interface_array1.v | 1 + test_regress/t/t_mod_interface_array2.v | 1 + test_regress/t/t_nba_mixed_update_clocked.v | 2 +- test_regress/t/t_nba_mixed_update_comb.v | 14 ++-- test_regress/t/t_nba_shared_flag_reuse.v | 1 + test_regress/t/t_opt_const.v | 4 + ..._const_no_opt.py => t_opt_const_no_opt.py} | 0 test_regress/t/t_order_dpi_export_2.v | 10 +-- test_regress/t/t_order_dpi_export_3.v | 12 +-- test_regress/t/t_order_dpi_export_4.v | 12 +-- test_regress/t/t_order_dpi_export_6.v | 10 +-- test_regress/t/t_past.v | 22 ++--- test_regress/t/t_past_funcs.v | 12 +-- test_regress/t/t_property_named.v | 2 +- test_regress/t/t_property_untyped.v | 2 +- test_regress/t/t_protect_ids.v | 10 +-- test_regress/t/t_queue.v | 10 +-- test_regress/t/t_savable.v | 2 +- test_regress/t/t_threads_counter.v | 2 +- test_regress/t/t_threads_crazy.v | 2 +- test_regress/t/t_timing_always.v | 6 +- test_regress/t/t_timing_fork_comb.v | 4 +- .../t/t_timing_nested_assignment_on_lhs.py | 2 +- test_regress/t/t_timing_sched_if.v | 6 +- test_regress/t/t_timing_sched_nba.v | 4 +- test_regress/t/t_tri_dangle.py | 2 +- test_regress/t/t_tri_dangle.v | 1 + test_regress/t/t_unpacked_slice_range.v | 8 +- test_regress/t/t_unpacked_struct_eq.py | 2 +- test_regress/t/t_unpacked_struct_eq.v | 9 ++- test_regress/t/t_verilated_all.v | 2 +- test_regress/t/t_vpi_escape.cpp | 5 +- test_regress/t/t_vpi_var.cpp | 4 +- test_regress/t/t_vpi_var.v | 2 +- test_regress/t/t_vpi_var2.v | 2 +- 50 files changed, 201 insertions(+), 172 deletions(-) rename test_regress/t/{t_const_no_opt.py => t_opt_const_no_opt.py} (100%) diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 1a1c3d802..945d944d3 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1969,14 +1969,13 @@ Summary: different seeds on different executions. This method is the slowest, but safest for finding reset bugs. - If using `--x-assign unique`, you may want to seed your random number - generator such that each regression run gets a different randomization - sequence. The simplest is to use the - :vlopt:`+verilator+seed+\` runtime option. Alternatively, use - the system's :code:`srand48()` or for Windows :code:`srand()` function - to do this. You'll probably also want to print any seeds selected, - and code to enable rerunning with that same seed so you can reproduce - bugs. + If using `--x-assign unique`, use the + :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\>` + runtime option, and seed the runtime random number generator such that + each regression run gets a different randomization sequence with + :vlopt:`+verilator+seed+\`. You'll probably also want to + print any seeds selected, and code to enable rerunning with that same + seed, so you can reproduce bugs. .. note:: @@ -1999,6 +1998,14 @@ Summary: use for each initialization. This gives the greatest flexibility and allows for finding reset bugs. See :ref:`Unknown states`. + If using `--x-initial unique`, use the + :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\>` + runtime option, and seed the runtime random number generator such that + each regression run gets a different randomization sequence with + :vlopt:`+verilator+seed+\`. You'll probably also want to + print any seeds selected, and code to enable rerunning with that same + seed, so you can reproduce bugs. + With "--x-initial fast", is best for performance, and initializes all variables to a state Verilator determines is optimal. This may allow further code diff --git a/test_regress/t/t_alw_split_rst.v b/test_regress/t/t_alw_split_rst.v index 5e39edbcb..b72d32c93 100644 --- a/test_regress/t/t_alw_split_rst.v +++ b/test_regress/t/t_alw_split_rst.v @@ -6,9 +6,9 @@ module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; integer cyc = 0; @@ -44,26 +44,23 @@ module t (/*AUTOARG*/ // Test loop always @ (posedge clk) begin `ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); + $write("[%0t] cyc==%0d crc=%x result=%x rstn=%x\n", $time, cyc, crc, result, rstn); `endif cyc <= cyc + 1; crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; - if (cyc==0) begin + if (cyc == 0) begin // Setup crc <= 64'h5aef0c8d_d70a4497; - sum <= '0; end - else if (cyc<10) begin + else if (cyc < 10) begin sum <= '0; end - else if (cyc<90) begin - end else if (cyc==99) begin $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); if (crc !== 64'hc77bb9b3784ea091) $stop; // What checksum will we end up with (above print should match) -`define EXPECTED_SUM 64'h77979747fd1b3a5a +`define EXPECTED_SUM 64'h77979747fd86e9fd if (sum !== `EXPECTED_SUM) $stop; $write("*-* All Finished *-*\n"); $finish; @@ -75,11 +72,11 @@ endmodule module Test (/*AUTOARG*/ - // Outputs - ff_out, fg_out, fh_out, - // Inputs - clk, clken, rstn, in - ); + // Outputs + ff_out, fg_out, fh_out, + // Inputs + clk, clken, rstn, in + ); input clk; input clken; @@ -98,6 +95,7 @@ module Test ff_11 <= 0; ff_12 <= 0; ff_13 <= 0; + ff_out <= 0; end else begin ff_10 <= in; @@ -120,6 +118,7 @@ module Test fg_11 <= 0; fg_12 <= 0; fg_13 <= 0; + fg_out <= 0; end else begin fg_10 <= in; @@ -142,6 +141,7 @@ module Test fh_11 <= 0; fh_12 <= 0; fh_13 <= 0; + fh_out <= 0; end else begin if (clken) begin diff --git a/test_regress/t/t_clk_vecgen1.py b/test_regress/t/t_clk_vecgen1.py index 12a6cc2e5..a61cd74d6 100755 --- a/test_regress/t/t_clk_vecgen1.py +++ b/test_regress/t/t_clk_vecgen1.py @@ -14,6 +14,6 @@ test.top_filename = "t/t_clk_vecgen1.v" test.compile(v_flags2=['+define+T_TEST1']) -test.execute() +test.execute(all_run_flags=["+verilator+rand+reset+0"]) test.passes() diff --git a/test_regress/t/t_clocking_inout.v b/test_regress/t/t_clocking_inout.v index 885c26e0a..bda438f11 100644 --- a/test_regress/t/t_clocking_inout.v +++ b/test_regress/t/t_clocking_inout.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 module t; - logic clk = 0, foo = 0, bar = 0; + bit clk = 0, foo = 0, bar = 0; always #5 clk = ~clk; diff --git a/test_regress/t/t_clocking_xref.v b/test_regress/t/t_clocking_xref.v index b5531d901..8d4d021ad 100644 --- a/test_regress/t/t_clocking_xref.v +++ b/test_regress/t/t_clocking_xref.v @@ -5,8 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 module mod; - logic clk = 1'b0; - logic inp = 1'b0; + bit clk = 1'b0; + bit inp = 1'b0; clocking cb @(posedge clk); input #3 inp; endclocking @@ -15,8 +15,8 @@ module mod; endmodule module main; - logic clk = 1'b0; - logic inp = 1'b0; + bit clk = 1'b0; + bit inp = 1'b0; always begin #2 if (t.mod1.cb.inp != 1'b0) $stop; diff --git a/test_regress/t/t_dpi_accessors.cpp b/test_regress/t/t_dpi_accessors.cpp index b5952071c..52ee5e539 100644 --- a/test_regress/t/t_dpi_accessors.cpp +++ b/test_regress/t/t_dpi_accessors.cpp @@ -45,9 +45,10 @@ static void logRegHex(int clk, const char* desc, int bitWidth, int val, const ch } // Convenience function to check we got an expected result. Silent on success. -static void checkResult(bool p, const char* msg_fail) { - if (!p) vl_fatal(__FILE__, __LINE__, "dut", msg_fail); -} +#define CHECK_RESULT(p, msg_fail) \ + do { \ + if (!(p)) vl_fatal(__FILE__, __LINE__, "dut", (msg_fail)); \ + } while (0) // Main function instantiates the model and steps through the test. int main() { @@ -91,9 +92,9 @@ int main() { std::cout << std::endl; #endif - checkResult((0 == a) && (0x00 == b) && (0x20 == mem32) && (1 == c) && (0xff == d) - && (0x00 == e) && (0x00 == f), - "Bad initial DPI values."); + CHECK_RESULT((0 == a) && (0x00 == b) && (0x20 == mem32) && (1 == c) && (0xff == d) + && (0x00 == e) && (0x00 == f), + "Bad initial DPI values."); // Initialize the clock dut->clk = 0; @@ -117,9 +118,9 @@ int main() { #endif // On a posedge, a should toggle, on a negedge it should stay the // same. - checkResult(((dut->clk == 1) && (a_after == (1 - a))) - || ((dut->clk == 0) && (a_after == a)), - "Test of scalar register reading failed."); + CHECK_RESULT(((dut->clk == 1) && (a_after == (1 - a))) + || ((dut->clk == 0) && (a_after == a)), + "Test of scalar register reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -140,9 +141,9 @@ int main() { int b_after = (int)b_read(); logRegHex(dut->clk, "read b", 8, b_after, " (after clk)"); // b should increment on a posedge and stay the same on a negedge. - checkResult(((dut->clk == 1) && (b_after == (b + 1))) - || ((dut->clk == 0) && (b_after == b)), - "Test of vector register reading failed."); + CHECK_RESULT(((dut->clk == 1) && (b_after == (b + 1))) + || ((dut->clk == 0) && (b_after == b)), + "Test of vector register reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -166,7 +167,7 @@ int main() { // In this case, the value never changes. But we should check it is // what we expect (0x20). - checkResult(mem32 == 0x20, "Test of array element reading failed."); + CHECK_RESULT(mem32 == 0x20, "Test of array element reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -195,7 +196,7 @@ int main() { // Verilator, that means that it will only change value when "a" // changes on the posedge of a clock. That is "c" always holds the // inverse of the "after clock" value of "a". - checkResult(c == (1 - a), "Test of scalar wire reading failed."); + CHECK_RESULT(c == (1 - a), "Test of scalar wire reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -225,7 +226,7 @@ int main() { // but in Verilator, that means that it will only change value when // "b" changes on the posedge of a clock. That is "d" always holds // the inverse of the "after clock" value of "b". - checkResult(d == ((~b) & 0xff), "Test of vector wire reading failed."); + CHECK_RESULT(d == ((~b) & 0xff), "Test of vector wire reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -252,9 +253,9 @@ int main() { // On a posedge clock, the value of a that is written should toggle, // on a negedge, it should not. - checkResult(((dut->clk == 1) && (a_after == (1 - a))) - || ((dut->clk == 0) && (a_after == a)), - "Test of scalar register writing failed."); + CHECK_RESULT(((dut->clk == 1) && (a_after == (1 - a))) + || ((dut->clk == 0) && (a_after == a)), + "Test of scalar register writing failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -281,9 +282,9 @@ int main() { // The value of "b" written should increment on a posedge and stay the // same on a negedge. - checkResult(((dut->clk == 1) && (b_after == (b + 1))) - || ((dut->clk == 0) && (b_after == b)), - "Test of vector register writing failed."); + CHECK_RESULT(((dut->clk == 1) && (b_after == (b + 1))) + || ((dut->clk == 0) && (b_after == b)), + "Test of vector register writing failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -312,7 +313,7 @@ int main() { // happen if this part of the test coincided with the 32nd element // being overwritten, which it does not. Check that the value after // the clock is the same as before the clock. - checkResult(mem32_after == mem32, "Test of array element writing failed."); + CHECK_RESULT(mem32_after == mem32, "Test of array element writing failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -339,7 +340,7 @@ int main() { logRegHex(dut->clk, "read b [3:0]", 4, b_slice, " (after clk)"); // The slice of "b" should always be the bottom 4 bits of "b" - checkResult(b_slice == (b & 0x0f), "Test of vector register slice reading failed."); + CHECK_RESULT(b_slice == (b & 0x0f), "Test of vector register slice reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -367,8 +368,8 @@ int main() { // The slice of "mem32" should always be the concatenation of the top // 2 and bottom 3 bits of "mem32" - checkResult(mem32_slice == (((mem32 & 0xc0) >> 3) | (mem32 & 0x07)), - "Test of array element slice reading failed."); + CHECK_RESULT(mem32_slice == (((mem32 & 0xc0) >> 3) | (mem32 & 0x07)), + "Test of array element slice reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -399,7 +400,7 @@ int main() { logRegHex(dut->clk, "read d [6:1]", 6, d_slice, " (after clk)"); // The slice of "d" should always be the middle 6 bits of "d". - checkResult(d_slice == ((d & 0x7e) >> 1), "Test of vector wire slice reading failed."); + CHECK_RESULT(d_slice == ((d & 0x7e) >> 1), "Test of vector wire slice reading failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -431,7 +432,7 @@ int main() { // We must test that when we wrote the slice of "b", we only wrote the // correct bits. The slice of b is b[3:0] int b_new = (b & 0xf0) | (b_slice & 0x0f); - checkResult(b_after == b_new, "Test of vector register slice writing failed."); + CHECK_RESULT(b_after == b_new, "Test of vector register slice writing failed."); dut->eval(); @@ -470,7 +471,7 @@ int main() { // We must test that when we wrote the slice of "mem32", we only wrote // the correct bits. The slice of "mem32" is {mem32[7:6], mem32[2:0]}. int mem32_new = (mem32 & 0x38) | ((mem32_slice & 0x18) << 3) | (mem32_slice & 0x7); - checkResult(mem32_after == mem32_new, "Test of vector register slice writing failed."); + CHECK_RESULT(mem32_after == mem32_new, "Test of vector register slice writing failed."); dut->eval(); @@ -482,8 +483,8 @@ int main() { // We have already tested that array element writing works, so we just // check that the slice of "mem32" after the clock is the // concatenation of the top 2 and bottom 3 bits of "mem32" - checkResult(mem32_slice == (((mem32 & 0xc0) >> 3) | (mem32 & 0x07)), - "Test of array element slice writing failed."); + CHECK_RESULT(mem32_slice == (((mem32 & 0xc0) >> 3) | (mem32 & 0x07)), + "Test of array element slice writing failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -522,10 +523,10 @@ int main() { // and wires works. So we just need to check that l1 reads back as the // correct combination of bits after the clock. It should be the 15 // bits: {b[3:0],mem[32][7:6],e[6:1],mem[32][2:0]}. - checkResult(l1 - == ((((b & 0x0f) >> 0) << 11) | (((mem32 & 0xc0) >> 6) << 9) - | (((e & 0x7e) >> 1) << 3) | (((mem32 & 0x07) >> 0) << 0)), - "Test of complex register reading l1 failed."); + CHECK_RESULT(l1 + == ((((b & 0x0f) >> 0) << 11) | (((mem32 & 0xc0) >> 6) << 9) + | (((e & 0x7e) >> 1) << 3) | (((mem32 & 0x07) >> 0) << 0)), + "Test of complex register reading l1 failed."); } #ifdef TEST_VERBOSE @@ -562,8 +563,8 @@ int main() { // and wires works. So we just need to check that l1 reads back as the // correct combination of bits after the clock. It should be the 8 // bits: {e[7:4], f[3:0]}. - checkResult(l2 == ((e & 0xf0) | (f & 0x0f)), - "Test of complex register reading l2 failed."); + CHECK_RESULT(l2 == ((e & 0xf0) | (f & 0x0f)), + "Test of complex register reading l2 failed."); } checkFinish(contextp.get(), "t_dpi_accessors unexpected finish"); @@ -605,8 +606,8 @@ int main() { int b_new = (b & 0xf0) | ((l1 & 0x7800) >> 11); int mem32_new = (mem32 & 0x38) | ((l1 & 0x0600) >> 3) | (l1 & 0x0007); int e_new = (e & 0x81) | ((l1 & 0x01f8) >> 2); - checkResult((b_new == b_after) && (mem32_new == mem32_after) && (e_new == e_after), - "Test of complex register writing l1 failed."); + CHECK_RESULT((b_new == b_after) && (mem32_new == mem32_after) && (e_new == e_after), + "Test of complex register writing l1 failed."); dut->eval(); @@ -650,8 +651,8 @@ int main() { // bits: {e[5:2], f[5:2]} int e_new = (e & 0xc3) | ((l2 & 0xf0) >> 2); int f_new = (f & 0xc3) | ((l2 & 0x0f) << 2); - checkResult((e_new == e_after) && (f_new == f_after), - "Test of complex register writing l2 failed."); + CHECK_RESULT((e_new == e_after) && (f_new == f_after), + "Test of complex register writing l2 failed."); dut->eval(); diff --git a/test_regress/t/t_dpi_shortcircuit.v b/test_regress/t/t_dpi_shortcircuit.v index e6be0315a..709a8b1cd 100644 --- a/test_regress/t/t_dpi_shortcircuit.v +++ b/test_regress/t/t_dpi_shortcircuit.v @@ -28,7 +28,7 @@ module t (/*AUTOARG*/); integer i; integer j; bit b; - integer errors; + int errors; task check1(integer line, bit got, bit ex); if (got != ex) begin diff --git a/test_regress/t/t_dpi_shortcircuit2.v b/test_regress/t/t_dpi_shortcircuit2.v index 2fcd47c7d..120e3f19e 100644 --- a/test_regress/t/t_dpi_shortcircuit2.v +++ b/test_regress/t/t_dpi_shortcircuit2.v @@ -29,7 +29,7 @@ module t (/*AUTOARG*/); integer j; integer k; bit b; - integer errors; + int errors; task check1(integer line, bit got, bit ex); if (got != ex) begin diff --git a/test_regress/t/t_event_control_scope_var.v b/test_regress/t/t_event_control_scope_var.v index e976b1c60..a83c8577e 100644 --- a/test_regress/t/t_event_control_scope_var.v +++ b/test_regress/t/t_event_control_scope_var.v @@ -11,7 +11,7 @@ module test_mod(input reg clk, input reg reset, output integer result); endmodule module Dut(input clk); - integer num; + int num; integer result1; integer result2; reg reset1; diff --git a/test_regress/t/t_for_count.v b/test_regress/t/t_for_count.v index 362902e79..97a2bbaee 100644 --- a/test_regress/t/t_for_count.v +++ b/test_regress/t/t_for_count.v @@ -51,7 +51,7 @@ module t (/*AUTOARG*/ end // While loop - integer w; + int w; initial begin while (w<10) w=w+1; if (w!=10) $stop; @@ -61,7 +61,7 @@ module t (/*AUTOARG*/ end // Do-While loop - integer dw; + int dw; initial begin do dw=dw+1; while (dw<10); if (dw!=10) $stop; diff --git a/test_regress/t/t_func_check.v b/test_regress/t/t_func_check.v index 9163d9406..28425e791 100644 --- a/test_regress/t/t_func_check.v +++ b/test_regress/t/t_func_check.v @@ -35,7 +35,7 @@ endmodule module chk (input clk, input rst_l, input expr); - integer errors; initial errors = 0; + int errors; task printerr; input [8*64:1] msg; diff --git a/test_regress/t/t_interface_array.v b/test_regress/t/t_interface_array.v index a847a0250..6f302e012 100644 --- a/test_regress/t/t_interface_array.v +++ b/test_regress/t/t_interface_array.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 interface foo_intf; - logic a; + bit a; modport source ( output a @@ -29,9 +29,9 @@ module t (/*AUTOARG*/ localparam N = 5; - logic [N-1:0] a_in; - logic [N-1:0] a_out; - logic [N-1:0] ack_out; + bit [N-1:0] a_in; + bit [N-1:0] a_out; + bit [N-1:0] ack_out; foo_intf foos [N-1:0] (); diff --git a/test_regress/t/t_mem_multi_io.v b/test_regress/t/t_mem_multi_io.v index cd753cffa..67641c734 100644 --- a/test_regress/t/t_mem_multi_io.v +++ b/test_regress/t/t_mem_multi_io.v @@ -21,6 +21,7 @@ module t (/*AUTOARG*/ initial begin for (int i = 0; i < 8; i++) begin arr[i] = 0; + arri[i] = 0; end end @@ -29,10 +30,12 @@ module t (/*AUTOARG*/ if (cyc == 5 && arri[1] != 8) begin $stop; end - for (int i = 0; i < 7; ++i) begin - arr[i+1] <= arr[i]; + if (cyc >= 2) begin + for (int i = 0; i < 7; ++i) begin + arr[i+1] <= arr[i]; + end + arr[0] <= arr[0] + 1; end - arr[0] <= arr[0] + 1; end endmodule : t @@ -47,7 +50,8 @@ module has_array ( always @(posedge clk) begin cyc <= cyc + 1; - if (arri[0] == 10 && cyc == 10) begin + if (cyc == 10) begin + if (arri[0] != 8) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_mem_slot.v b/test_regress/t/t_mem_slot.v index 9c03ffac3..63ce7d137 100644 --- a/test_regress/t/t_mem_slot.v +++ b/test_regress/t/t_mem_slot.v @@ -13,9 +13,9 @@ module t_mem_slot (Clk, SlotIdx, BitToChange, BitVal, SlotToReturn, OutputVal); input BitToChange; input BitVal; input [1:0] SlotToReturn; - output reg [1:0] OutputVal; + output bit [1:0] OutputVal; - reg [1:0] Array[2:0]; + bit [1:0] Array[2:0]; always @(posedge Clk) begin diff --git a/test_regress/t/t_mod_interface_array0.v b/test_regress/t/t_mod_interface_array0.v index ea9a4914e..b77eaae2d 100644 --- a/test_regress/t/t_mod_interface_array0.v +++ b/test_regress/t/t_mod_interface_array0.v @@ -49,6 +49,7 @@ module t intf_sink sink(a_out, tl_intf); initial a_in = '0; + initial ack_out = '0; always @(posedge clk) begin a_in <= a_in + { {N-1 {1'b0}}, 1'b1 }; ack_out <= ack_out + { {N-1 {1'b0}}, 1'b1 }; diff --git a/test_regress/t/t_mod_interface_array1.v b/test_regress/t/t_mod_interface_array1.v index f16a65b41..0cd46fe78 100644 --- a/test_regress/t/t_mod_interface_array1.v +++ b/test_regress/t/t_mod_interface_array1.v @@ -51,6 +51,7 @@ module t intf_sink sink(a_out, tl_intf); initial a_in = '0; + initial ack_out = '0; always @(posedge clk) begin a_in <= a_in + { {N-1 {1'b0}}, 1'b1 }; ack_out <= ack_out + { {N-1 {1'b0}}, 1'b1 }; diff --git a/test_regress/t/t_mod_interface_array2.v b/test_regress/t/t_mod_interface_array2.v index b2f15dc04..30ee8ae74 100644 --- a/test_regress/t/t_mod_interface_array2.v +++ b/test_regress/t/t_mod_interface_array2.v @@ -50,6 +50,7 @@ module t intf_sink sink(a_out, tl_intf); initial a_in = '0; + initial ack_out = '0; always @(posedge clk) begin a_in <= a_in + { {N-1 {1'b0}}, 1'b1 }; ack_out <= ack_out + { {N-1 {1'b0}}, 1'b1 }; diff --git a/test_regress/t/t_nba_mixed_update_clocked.v b/test_regress/t/t_nba_mixed_update_clocked.v index abdf54e54..86f274928 100644 --- a/test_regress/t/t_nba_mixed_update_clocked.v +++ b/test_regress/t/t_nba_mixed_update_clocked.v @@ -22,7 +22,7 @@ module t (/*AUTOARG*/ reg y0 = 1'b0; reg y1 = 1'b0; // 'z[0]' should equal '{8{x[0]}', 'z[1]' should equal '{8{x[1]}}' - reg [1:0][7:0] z; + reg [1:0][7:0] z = '{default: 0}; // 'pair.a' should equal 'x[0]', 'pair.b' should equal 'x[1]' struct { logic a; diff --git a/test_regress/t/t_nba_mixed_update_comb.v b/test_regress/t/t_nba_mixed_update_comb.v index aa91ac00e..e9ff73c76 100644 --- a/test_regress/t/t_nba_mixed_update_comb.v +++ b/test_regress/t/t_nba_mixed_update_comb.v @@ -8,16 +8,16 @@ `define check(got ,exp) do if ((got) !== (exp)) begin $write("%%Error: %s:%0d: cyc=%0d got='h%x exp='h%x\n", `__FILE__,`__LINE__, cyc, (got), (exp)); `stop; end while(0) module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; reg [31:0] cyc = 0; - reg [31:0] sameAsCycButCantBeOptimized_0; - reg [31:0] sameAsCycButCantBeOptimized_1; - reg [31:0] sameAsCycButCantBeOptimized_2; + reg [31:0] sameAsCycButCantBeOptimized_0 = '0; + reg [31:0] sameAsCycButCantBeOptimized_1 = '0; + reg [31:0] sameAsCycButCantBeOptimized_2 = '0; // 'x' has both blocking and non-blocking update, with the blocking // update in **combinational** logic @@ -27,7 +27,7 @@ module t (/*AUTOARG*/ reg y1 = 1'b0; // 'z[0]' should equal '{8{x[0]}', 'z[1]' should equal '{8{x[1]}}' // verilator lint_off BLKANDNBLK - reg [1:0][7:0] z; + bit [1:0][7:0] z; // verilator lint_on BLKANDNBLK // 'pair.a' should equal 'x[0]', 'pair.b' should equal 'x[1]' // verilator lint_off BLKANDNBLK diff --git a/test_regress/t/t_nba_shared_flag_reuse.v b/test_regress/t/t_nba_shared_flag_reuse.v index 133b7e61d..0b3ae9555 100644 --- a/test_regress/t/t_nba_shared_flag_reuse.v +++ b/test_regress/t/t_nba_shared_flag_reuse.v @@ -52,6 +52,7 @@ module t; always @(posedge clk) begin if (reset) begin r_valid <= 0; + addr[0] <= '0; end else begin if (r_valid) begin diff --git a/test_regress/t/t_opt_const.v b/test_regress/t/t_opt_const.v index be0e361c1..d4982f11c 100644 --- a/test_regress/t/t_opt_const.v +++ b/test_regress/t/t_opt_const.v @@ -255,6 +255,10 @@ endmodule // Of course the correct result is ^d[38:0] = ^d module bug3470(input wire clk, input wire [31:0] in, output wire out); logic [38:0] d; + initial d = 0; + initial tmp = 0; + initial expected = 0; + always_ff @(posedge clk) d <= {d[6:0], in}; diff --git a/test_regress/t/t_const_no_opt.py b/test_regress/t/t_opt_const_no_opt.py similarity index 100% rename from test_regress/t/t_const_no_opt.py rename to test_regress/t/t_opt_const_no_opt.py diff --git a/test_regress/t/t_order_dpi_export_2.v b/test_regress/t/t_order_dpi_export_2.v index 0f1e8a577..be8af3616 100644 --- a/test_regress/t/t_order_dpi_export_2.v +++ b/test_regress/t/t_order_dpi_export_2.v @@ -8,12 +8,12 @@ module testbench( /*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; // Top level input clock - logic other_clk; // Dependent clock set via DPI + bit other_clk; // Dependent clock set via DPI export "DPI-C" function set_other_clk; function void set_other_clk(bit val); @@ -30,7 +30,7 @@ module testbench( int n = 0; always @(posedge other_clk) begin - $display("t=%t n=%d", $time, n); + $display("[%0t] n=%0d", $time, n); if ($time != (4*n+1) * 500) $stop; if (n == 20) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_order_dpi_export_3.v b/test_regress/t/t_order_dpi_export_3.v index 782d56cb7..49a41e235 100644 --- a/test_regress/t/t_order_dpi_export_3.v +++ b/test_regress/t/t_order_dpi_export_3.v @@ -8,13 +8,13 @@ module testbench( /*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; // Top level input clock - logic other_clk; // Dependent clock set via DPI - logic third_clk; // Additional dependent clock set via DPI + bit other_clk; // Dependent clock set via DPI + bit third_clk; // Additional dependent clock set via DPI export "DPI-C" function set_other_clk; function void set_other_clk(bit val); @@ -43,7 +43,7 @@ module testbench( int n = 0; always @(posedge third_clk) begin - $display("t=%d n=%d", $time, n); + $display("[%0t] n=%0d", $time, n); if ($time != (8*n+1) * 500) $stop; if (n == 20) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_order_dpi_export_4.v b/test_regress/t/t_order_dpi_export_4.v index 88529466b..f0ad6ae66 100644 --- a/test_regress/t/t_order_dpi_export_4.v +++ b/test_regress/t/t_order_dpi_export_4.v @@ -8,13 +8,13 @@ module testbench( /*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; // Top level input clock - logic other_clk; // Dependent clock set via DPI - logic third_clk; // Additional dependent clock set via DPI + bit other_clk; // Dependent clock set via DPI + bit third_clk; // Additional dependent clock set via DPI export "DPI-C" function set_other_clk; function void set_other_clk(bit val); @@ -45,7 +45,7 @@ module testbench( wire final_clk = $c1("1") & third_clk; always @(posedge final_clk) begin - $display("t=%d n=%d", $time, n); + $display("[%0t] n=%0d", $time, n); if ($time != (8*n+1) * 500) $stop; if (n == 20) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_order_dpi_export_6.v b/test_regress/t/t_order_dpi_export_6.v index 36a33d141..b49c5c879 100644 --- a/test_regress/t/t_order_dpi_export_6.v +++ b/test_regress/t/t_order_dpi_export_6.v @@ -8,12 +8,12 @@ module testbench( /*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; // Top level input clock - logic other_clk; // Dependent clock set via DPI + bit other_clk; // Dependent clock set via DPI export "DPI-C" function set_other_clk; function void set_other_clk(bit val); @@ -36,7 +36,7 @@ module testbench( // above is committed, as setting clocks via the set_other_clk uses // blocking assignment. if (even_other !== current_even_other) $stop; - $display("t=%t n=%d", $time, n); + $display("[%0t] n=%0d", $time, n); if ($time != (2*n+1) * 500) $stop; if (n == 20) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_past.v b/test_regress/t/t_past.v index 24b82ad92..1a5e25ab3 100644 --- a/test_regress/t/t_past.v +++ b/test_regress/t/t_past.v @@ -71,18 +71,20 @@ module Test (/*AUTOARG*/ dly1 <= dly0; dly2 <= dly1; dly3 <= dly2; - // $past(expression, ticks, expression, clocking) - // In clock expression - if (dly0 != $past(in)) $stop; - if (dly0 != $past(in,)) $stop; - if (dly1 != $past(in, 2)) $stop; - if (dly1 != $past(in, 2, )) $stop; - if (dly1 != $past(in, 2, , )) $stop; - // $sampled(expression) -> expression - if (in != $sampled(in)) $stop; + if ($time > 40) begin + // $past(expression, ticks, expression, clocking) + // In clock expression + if (dly0 != $past(in)) $stop; + if (dly0 != $past(in,)) $stop; + if (dly1 != $past(in, 2)) $stop; + if (dly1 != $past(in, 2, )) $stop; + if (dly1 != $past(in, 2, , )) $stop; + // $sampled(expression) -> expression + if (in != $sampled(in)) $stop; + end end - assert property (@(posedge clk) dly0 == $past(in)); + assert property (@(posedge clk) $time < 40 || dly0 == $past(in)); endmodule diff --git a/test_regress/t/t_past_funcs.v b/test_regress/t/t_past_funcs.v index 78969cd0d..d8e987c96 100644 --- a/test_regress/t/t_past_funcs.v +++ b/test_regress/t/t_past_funcs.v @@ -48,9 +48,9 @@ module Test (/*AUTOARG*/ input clk; input [31:0] in; - reg [31:0] dly0 = 0; - reg [31:0] dly1 = 0; - reg [31:0] dly2 = 0; + bit [31:0] dly0 = 0; + bit [31:0] dly1 = 0; + bit [31:0] dly2 = 0; // If called in an assertion, sequence, or property, the appropriate clocking event. // Otherwise, if called in a disable condition or a clock expression in an assertion, sequence, or prop, explicit. @@ -107,9 +107,9 @@ module Test2 (/*AUTOARG*/ input clk; input [31:0] in; - reg [31:0] dly0 = 0; - reg [31:0] dly1 = 0; - reg [31:0] dly2 = 0; + bit [31:0] dly0 = 0; + bit [31:0] dly1 = 0; + bit [31:0] dly2 = 0; always @(posedge clk) begin dly0 <= in; diff --git a/test_regress/t/t_property_named.v b/test_regress/t/t_property_named.v index 7fed4f620..ceb2ad90c 100644 --- a/test_regress/t/t_property_named.v +++ b/test_regress/t/t_property_named.v @@ -43,7 +43,7 @@ module t (/*AUTOARG*/ assert property(check(0, 1)) else begin // Assertion should pass - $display("Assert failed, but shouldn't"); + $display("[%0t] Assert failed, but shouldn't", $time); $stop; end assert property(check(1, 1)) diff --git a/test_regress/t/t_property_untyped.v b/test_regress/t/t_property_untyped.v index 29fbd99c2..15f1bc81c 100644 --- a/test_regress/t/t_property_untyped.v +++ b/test_regress/t/t_property_untyped.v @@ -25,7 +25,7 @@ module t (/*AUTOARG*/ assert property(check(0, 5'b11111)) else begin // Assertion should pass - $display("Assert failed, but shouldn't"); + $display("[%0t] Assert failed, but shouldn't", $time); $stop; end diff --git a/test_regress/t/t_protect_ids.v b/test_regress/t/t_protect_ids.v index dcc0fc41d..031f12406 100644 --- a/test_regress/t/t_protect_ids.v +++ b/test_regress/t/t_protect_ids.v @@ -10,9 +10,9 @@ interface secret_intf(); endinterface module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; secret_sub secret_inst (.*); @@ -30,7 +30,7 @@ module secret_sub integer secret_field_r; } secret_st; - integer secret_cyc; + int secret_cyc; real secret_cyc_r; integer secret_o; real secret_r; @@ -63,7 +63,7 @@ module secret_other ( input clk); - integer secret_cyc; + int secret_cyc; always @ (posedge clk) begin secret_cyc <= secret_cyc + 1; diff --git a/test_regress/t/t_queue.v b/test_regress/t/t_queue.v index 497c9a7ee..20660e2c4 100644 --- a/test_regress/t/t_queue.v +++ b/test_regress/t/t_queue.v @@ -10,18 +10,18 @@ `define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) != (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; integer cyc = 0; integer i; - typedef integer q_t[$]; + typedef int q_t[$]; - function void set_val(ref integer lhs, input integer rhs); + function void set_val(ref int lhs, input int rhs); lhs = rhs; endfunction diff --git a/test_regress/t/t_savable.v b/test_regress/t/t_savable.v index e9acda757..c5e2cac1d 100644 --- a/test_regress/t/t_savable.v +++ b/test_regress/t/t_savable.v @@ -35,7 +35,7 @@ module sub (/*AUTOARG*/ reg [127:0] save128; reg [47:0] save48; reg [1:0] save2; - reg [255:0] cycdone; // Make sure each cycle executes exactly once + bit [255:0] cycdone; // Make sure each cycle executes exactly once reg [31:0] vec[2:1][2:1]; reg [2:1][2:1][31:0] pvec; real r; diff --git a/test_regress/t/t_threads_counter.v b/test_regress/t/t_threads_counter.v index fad058267..b853d69c8 100644 --- a/test_regress/t/t_threads_counter.v +++ b/test_regress/t/t_threads_counter.v @@ -11,7 +11,7 @@ module t (/*AUTOARG*/ input clk; - integer cyc; + int cyc; always @ (posedge clk) begin cyc <= cyc + 1; diff --git a/test_regress/t/t_threads_crazy.v b/test_regress/t/t_threads_crazy.v index fad058267..b853d69c8 100644 --- a/test_regress/t/t_threads_crazy.v +++ b/test_regress/t/t_threads_crazy.v @@ -11,7 +11,7 @@ module t (/*AUTOARG*/ input clk; - integer cyc; + int cyc; always @ (posedge clk) begin cyc <= cyc + 1; diff --git a/test_regress/t/t_timing_always.v b/test_regress/t/t_timing_always.v index e92bc2539..ab6644123 100644 --- a/test_regress/t/t_timing_always.v +++ b/test_regress/t/t_timing_always.v @@ -11,11 +11,11 @@ `endif module t; - logic clk = 0; + bit clk = 0; always #3 clk = ~clk; - logic flag_a; - logic flag_b; + bit flag_a; + bit flag_b; always @(posedge clk) begin `WRITE_VERBOSE(("[%0t] b <= 0\n", $time)); diff --git a/test_regress/t/t_timing_fork_comb.v b/test_regress/t/t_timing_fork_comb.v index 8c7daec56..a3d28b0f8 100644 --- a/test_regress/t/t_timing_fork_comb.v +++ b/test_regress/t/t_timing_fork_comb.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 module t; - logic clk = 0; + bit clk; assign #5 clk = ~clk; @@ -28,7 +28,7 @@ module t; c = a + b; end - logic[5:0] v; + bit [5:0] v; always @a begin v[0] = a[0]; fork diff --git a/test_regress/t/t_timing_nested_assignment_on_lhs.py b/test_regress/t/t_timing_nested_assignment_on_lhs.py index bc41b20a9..2721d0c88 100755 --- a/test_regress/t/t_timing_nested_assignment_on_lhs.py +++ b/test_regress/t/t_timing_nested_assignment_on_lhs.py @@ -14,6 +14,6 @@ test.top_filename = "t/t_net_delay.v" test.compile(timing_loop=True, verilator_flags2=["--timing"]) -test.execute() +test.execute(all_run_flags=["+verilator+rand+reset+0"]) test.passes() diff --git a/test_regress/t/t_timing_sched_if.v b/test_regress/t/t_timing_sched_if.v index 768c7a82c..a3260d9d5 100644 --- a/test_regress/t/t_timing_sched_if.v +++ b/test_regress/t/t_timing_sched_if.v @@ -5,14 +5,14 @@ // SPDX-License-Identifier: CC0-1.0 module t; - logic clk1 = 0; + bit clk1 = 0; assign #3 clk1 = ~clk1; - logic clk2 = 0; + bit clk2 = 0; assign #11 clk2 = ~clk2; - logic flag = 0; + bit flag = 0; int a1 = 0; int b1 = 0; int c1 = 0; diff --git a/test_regress/t/t_timing_sched_nba.v b/test_regress/t/t_timing_sched_nba.v index d09a4778a..f4d622eca 100644 --- a/test_regress/t/t_timing_sched_nba.v +++ b/test_regress/t/t_timing_sched_nba.v @@ -5,11 +5,11 @@ // SPDX-License-Identifier: CC0-1.0 module t; - logic clk1 = 0; + bit clk1 = 0; assign #3 clk1 = ~clk1; - logic clk2 = 0; + bit clk2 = 0; assign #11 clk2 = ~clk2; int a1 = 0; diff --git a/test_regress/t/t_tri_dangle.py b/test_regress/t/t_tri_dangle.py index d4f986441..2b6ee4e44 100755 --- a/test_regress/t/t_tri_dangle.py +++ b/test_regress/t/t_tri_dangle.py @@ -13,6 +13,6 @@ test.scenarios('simulator') test.compile() -test.execute() +test.execute(all_run_flags=['+verilator+rand+reset+0']) test.passes() diff --git a/test_regress/t/t_tri_dangle.v b/test_regress/t/t_tri_dangle.v index 1802063b4..db659de70 100644 --- a/test_regress/t/t_tri_dangle.v +++ b/test_regress/t/t_tri_dangle.v @@ -30,5 +30,6 @@ module sub (/*AUTOARG*/ inout AVDD; inout AVSS; tri NON_IO; + // +verilator+rand+reset+0 so z will read as zero initial if (NON_IO !== 'z) $stop; endmodule diff --git a/test_regress/t/t_unpacked_slice_range.v b/test_regress/t/t_unpacked_slice_range.v index c1e46fa27..1e431425a 100644 --- a/test_regress/t/t_unpacked_slice_range.v +++ b/test_regress/t/t_unpacked_slice_range.v @@ -35,10 +35,10 @@ module t2 #(parameter ORIGIN = 0) (input wire clk, input int c); localparam FULL_HI = ORIGIN + WIDTH - 1; localparam PART_LO = FULL_LO + OFFSET; localparam PART_HI = FULL_HI; - logic unpack_sig0 [FULL_LO:FULL_HI]; - logic unpack_sig1 [PART_LO:PART_HI]; - logic unpack_sig2 [FULL_HI:FULL_LO]; - logic unpack_sig3 [PART_HI:PART_LO]; + bit unpack_sig0 [FULL_LO:FULL_HI]; + bit unpack_sig1 [PART_LO:PART_HI]; + bit unpack_sig2 [FULL_HI:FULL_LO]; + bit unpack_sig3 [PART_HI:PART_LO]; initial $display("%m ORIGIN:%d [%d:%d] [%d:%d]", ORIGIN, FULL_LO, FULL_HI, PART_LO, PART_HI); always @(posedge clk) begin diff --git a/test_regress/t/t_unpacked_struct_eq.py b/test_regress/t/t_unpacked_struct_eq.py index d4f986441..671072f97 100755 --- a/test_regress/t/t_unpacked_struct_eq.py +++ b/test_regress/t/t_unpacked_struct_eq.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=['--binary']) test.execute() diff --git a/test_regress/t/t_unpacked_struct_eq.v b/test_regress/t/t_unpacked_struct_eq.v index 384f10801..b2a064a65 100644 --- a/test_regress/t/t_unpacked_struct_eq.v +++ b/test_regress/t/t_unpacked_struct_eq.v @@ -18,14 +18,14 @@ module t; typedef struct{ - logic [31:0] subarr[4]; + bit [31:0] subarr[4]; } arr_str_t; typedef struct { string txt; struct { - logic m0; - logic [3:0] m1; - logic [7:0] arr[2][3]; + bit m0; + bit [3:0] m1; + bit [7:0] arr[2][3]; arr_str_t str[5]; } sub; } struct_t; @@ -56,6 +56,7 @@ module t; assign s3.sub.arr[1][2] = 8'h06; initial begin + #1; if(s3 == s1) $stop; if(s1 == s2 && s3 != s1) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_verilated_all.v b/test_regress/t/t_verilated_all.v index c34cb4d11..660a709bb 100644 --- a/test_regress/t/t_verilated_all.v +++ b/test_regress/t/t_verilated_all.v @@ -16,7 +16,7 @@ module t (/*AUTOARG*/ input clk; - integer cyc; + int cyc; integer rand_result; integer seed = 123; diff --git a/test_regress/t/t_vpi_escape.cpp b/test_regress/t/t_vpi_escape.cpp index 0ba0c24bc..85acca39b 100644 --- a/test_regress/t/t_vpi_escape.cpp +++ b/test_regress/t/t_vpi_escape.cpp @@ -403,8 +403,11 @@ int main(int argc, char** argv) { tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); #endif - topp->eval(); + topp->a = 0; topp->clk = 0; + topp->b__02ec = 0; + + topp->eval(); main_time += 10; while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) { diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 428e4298f..91b15b185 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -1031,8 +1031,10 @@ int main(int argc, char** argv) { tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); #endif - topp->eval(); topp->clk = 0; + topp->a = 0; + + topp->eval(); main_time += 10; while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) { diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 782143ba1..a651fb413 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -40,7 +40,7 @@ extern "C" int mon_check(); // verilator lint_on ASCRANGE reg [31:0] count /*verilator public_flat */; - reg [31:0] half_count /*verilator public_flat_rd */; + reg [31:0] half_count /*verilator public_flat_rd */ = 0; reg [31:0] delayed /*verilator public_flat_rw */; reg [31:0] delayed_mem [16] /*verilator public_flat_rw */; diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 61df88f63..d5602e0c2 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -53,7 +53,7 @@ extern "C" int mon_check(); /*verilator public_flat_on*/ reg [31:0] count; - reg [31:0] half_count; + reg [31:0] half_count = 0; /*verilator public_off*/ /*verilator public_flat_rw_on*/ reg [31:0] delayed; From 49ca1cfdd79312dcd28195852f7cc89e1b95b754 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 18:32:40 -0400 Subject: [PATCH 176/252] Commentary --- src/V3Const.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 42f5ed1e1..0f009d2b7 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3504,8 +3504,11 @@ class ConstVisitor final : public VNVisitor { } // TODO if there's an ExprStmt underneath just keep lower statements // (No current test case needs this) - // TODO if non-pure, can remove keep removing upper pure-itself - // NodeUniOp/NodeBiOp until get to the non-pure child-node expression + // TODO if non-pure, can remove. First need to clean up that many expressions used + // under this node do not yet properly indicate non-pure. + // Also, under here, a NodeUniOp/NodeBiOp that is pure (but child of such is not + // pure) can be peeled off until get to the non-pure child-node expression, + // because all that is needed is to execute what causes the side effect. } // Simplify From 36c4a24661ab7a9dc8563529d878a40f86b7377e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 21:03:12 -0400 Subject: [PATCH 177/252] Internals: constifyParamsEdit return value was unused. No functional change. --- src/V3Const.cpp | 12 ++++-------- src/V3Const.h | 10 ++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 0f009d2b7..0846a3126 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3973,8 +3973,7 @@ uint32_t ConstVisitor::s_globalPassNum = 0; // Const class functions //! Force this cell node's parameter list to become a constant -//! @return Pointer to the edited node. -AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { +void V3Const::constifyParamsEdit(AstNode* nodep) { // UINFOTREE(1, nodep, "", "forceConPRE"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized yet. @@ -3988,16 +3987,14 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { // init value because we need widthing above to handle the var's type. if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); } else { - nodep = visitor.mainAcceptEdit(nodep); + (void)visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump if have next line // UINFOTREE(1, nodep, "", "forceConDONE"); - return nodep; } //! Constify this cell node's parameter list if possible -//! @return Pointer to the edited node. -AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { +void V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { // UINFOTREE(1, nodep, "", "forceConPRE"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized yet. @@ -4011,11 +4008,10 @@ AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { // init value because we need widthing above to handle the var's type. if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); } else { - nodep = visitor.mainAcceptEdit(nodep); + (void)visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump this. // UINFOTREE(1, nodep, "", "forceConDONE"); - return nodep; } //! Force this cell node's parameter list to become a constant inside generate. diff --git a/src/V3Const.h b/src/V3Const.h index 1491f7714..c8f1c5e27 100644 --- a/src/V3Const.h +++ b/src/V3Const.h @@ -26,14 +26,8 @@ class V3Const final { public: - static AstNode* constifyParamsEdit(AstNode* nodep) VL_MT_DISABLED; - static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) VL_MT_DISABLED { - return VN_AS(constifyParamsEdit(static_cast(exprp)), NodeExpr); - } - static AstNode* constifyParamsNoWarnEdit(AstNode* nodep) VL_MT_DISABLED; - static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) VL_MT_DISABLED { - return VN_AS(constifyParamsNoWarnEdit(static_cast(exprp)), NodeExpr); - } + static void constifyParamsEdit(AstNode* nodep) VL_MT_DISABLED; + static void constifyParamsNoWarnEdit(AstNode* nodep) VL_MT_DISABLED; static AstNode* constifyGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED; // Only do constant pushing, without removing dead logic static void constifyAllLive(AstNetlist* nodep) VL_MT_DISABLED; From 7126293086f8737f73a8570767605b1575f5d03d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Aug 2025 21:06:35 -0400 Subject: [PATCH 178/252] Support enum.next with a parameter --- src/V3Width.cpp | 63 +- test_regress/t/t_enum_type_methods.v | 4 +- test_regress/t/t_enum_type_methods_bad.out | 7 +- test_regress/t/t_json_only_debugcheck.out | 908 ++++++++--------- test_regress/t/t_xml_debugcheck.out | 1016 ++++++++++---------- 5 files changed, 1003 insertions(+), 995 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4a51af033..834e4b165 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3550,38 +3550,43 @@ class WidthVisitor final : public VNVisitor { } if (nodep->name() != "name" && nodep->pinsp()) { - AstNodeExpr* const stepp = methodArg(nodep, 0); + AstNodeExpr* stepp = methodArg(nodep, 0); + VL_DO_DANGLING(V3Const::constifyParamsNoWarnEdit(stepp), stepp); + stepp = methodArg(nodep, 0); nodep->fileline()->modifyWarnOff(V3ErrorCode::WIDTHEXPAND, true); iterateCheckUInt32(nodep, "argument", stepp, BOTH); if (!VN_IS(stepp, Const)) { - stepp->v3fatalSrc("Unsupported: enum next/prev with non-const argument"); - } else { - const uint32_t stepWidth = VN_AS(stepp, Const)->toUInt(); - if (stepWidth == 0) { - // Step of 0 "legalizes" like $cast, use .next.prev - AstMethodCall* const newp = new AstMethodCall{ - nodep->fileline(), - new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - "next", nullptr}, - "prev", nullptr}; - // No dtype assigned, we will recurse the new method and replace - nodep->replaceWith(newp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } else if (stepWidth != 1) { - // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1) - nodep->pinsp()->unlinkFrBack(); - AstMethodCall* const clonep = nodep->cloneTree(false); - VN_AS(stepp, Const)->num().setLong(1); - AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1); - AstArg* const argp = new AstArg{nodep->fileline(), "", constp}; - AstMethodCall* const newp - = new AstMethodCall{nodep->fileline(), clonep, nodep->name(), argp}; - // No dtype assigned, we will recurse the new method and replace - nodep->replaceWith(newp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } + stepp->v3warn(E_UNSUPPORTED, + "Unsupported: enum next/prev with non-constant argument"); + AstNodeExpr* const newp = new AstConst{stepp->fileline(), 1}; + stepp->replaceWith(newp); + stepp = newp; + } + const uint32_t stepWidth = VN_AS(stepp, Const)->toUInt(); + if (stepWidth == 0) { + // Step of 0 "legalizes" like $cast, use .next.prev + AstMethodCall* const newp = new AstMethodCall{ + nodep->fileline(), + new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "next", nullptr}, + "prev", nullptr}; + // No dtype assigned, we will recurse the new method and replace + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; + } else if (stepWidth != 1) { + // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1) + nodep->pinsp()->unlinkFrBack(); + AstMethodCall* const clonep = nodep->cloneTree(false); + VN_AS(stepp, Const)->num().setLong(1); + AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1); + AstArg* const argp = new AstArg{nodep->fileline(), "", constp}; + AstMethodCall* const newp + = new AstMethodCall{nodep->fileline(), clonep, nodep->name(), argp}; + // No dtype assigned, we will recurse the new method and replace + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; } } AstNodeExpr* const newp diff --git a/test_regress/t/t_enum_type_methods.v b/test_regress/t/t_enum_type_methods.v index b391f659b..795aa8b92 100644 --- a/test_regress/t/t_enum_type_methods.v +++ b/test_regress/t/t_enum_type_methods.v @@ -40,7 +40,7 @@ module t (/*AUTOARG*/ `checkh(e.next(2), E01); `checkh(e.next(1).next(1).next(1), E03); `checkh(e.next(1).next(2), E03); - `checkh(e.next(3), E03); + `checkh(e.next(THREE), E03); `checkh(e.prev, E01); `checkh(e.prev(1), E01); `checkh(e.prev(1).prev(1), E04); @@ -57,6 +57,8 @@ module t (/*AUTOARG*/ `checks(all, "E01E03E04"); end + localparam THREE = 3; + // Check runtime always @ (posedge clk) begin cyc <= cyc + 1; diff --git a/test_regress/t/t_enum_type_methods_bad.out b/test_regress/t/t_enum_type_methods_bad.out index b8c07d499..5b492d26c 100644 --- a/test_regress/t/t_enum_type_methods_bad.out +++ b/test_regress/t/t_enum_type_methods_bad.out @@ -1,5 +1,6 @@ -%Error: Internal Error: t/t_enum_type_methods_bad.v:24:14: ../V3Width.cpp:#: Unsupported: enum next/prev with non-const argument - : ... note: In instance 't' +%Error-UNSUPPORTED: t/t_enum_type_methods_bad.v:24:14: Unsupported: enum next/prev with non-constant argument + : ... note: In instance 't' 24 | e.next(increment); | ^~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_json_only_debugcheck.out b/test_regress/t/t_json_only_debugcheck.out index 8c36a21da..9451c4e7f 100644 --- a/test_regress/t/t_json_only_debugcheck.out +++ b/test_regress/t/t_json_only_debugcheck.out @@ -21,12 +21,12 @@ "exprp": [ {"type":"CCALL","name":"","addr":"(BB)","loc":"d,11:8,11:9","dtypep":"(CB)","funcName":"_eval_static__TOP","funcp":"(DB)","argsp": []} ]}, - {"type":"ASSIGN","name":"","addr":"(EB)","loc":"d,61:22,61:25","dtypep":"(FB)", + {"type":"ASSIGN","name":"","addr":"(EB)","loc":"d,63:22,63:25","dtypep":"(FB)", "rhsp": [ - {"type":"VARREF","name":"clk","addr":"(GB)","loc":"d,61:22,61:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(GB)","loc":"d,63:22,63:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(HB)","loc":"d,61:22,61:25","dtypep":"(FB)","access":"WR","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(HB)","loc":"d,63:22,63:25","dtypep":"(FB)","access":"WR","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ],"finalsp": []}, {"type":"CFUNC","name":"_eval_static__TOP","addr":"(DB)","loc":"d,11:8,11:9","slow":true,"isStatic":false,"dpiExportDispatcher":false,"dpiExportImpl":false,"dpiImportPrototype":false,"dpiImportWrapper":false,"dpiContext":false,"isConstructor":false,"isDestructor":false,"isVirtual":false,"isCoroutine":false,"needProcess":false,"scopep":"(Y)","argsp": [],"initsp": [], @@ -464,9 +464,9 @@ ],"elsesp": []}, {"type":"IF","name":"","addr":"(PH)","loc":"d,43:10,43:12", "condp": [ - {"type":"NEQ","name":"","addr":"(QH)","loc":"d,43:26,43:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(QH)","loc":"d,43:30,43:33","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(RH)","loc":"d,43:31,43:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(RH)","loc":"d,43:35,43:38","dtypep":"(AC)"} ], "rhsp": [ {"type":"ARRAYSEL","name":"","addr":"(SH)","loc":"d,43:15,43:16","dtypep":"(AC)", @@ -512,43 +512,43 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(GI)","loc":"d,43:43,43:49", + {"type":"DISPLAY","name":"","addr":"(GI)","loc":"d,43:47,43:53", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:43: got='h%x exp='h3\\n","addr":"(HI)","loc":"d,43:43,43:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:43: got='h%x exp='h3\\n","addr":"(HI)","loc":"d,43:47,43:53","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(II)","loc":"d,43:122,43:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(II)","loc":"d,43:126,43:127","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JI)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KI)","loc":"d,43:122,43:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KI)","loc":"d,43:126,43:127","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LI)","loc":"d,43:122,43:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LI)","loc":"d,43:126,43:127","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(MI)","loc":"d,43:122,43:123","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(MI)","loc":"d,43:126,43:127","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(NI)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(OI)","loc":"d,43:122,43:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(OI)","loc":"d,43:126,43:127","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(PI)","loc":"d,43:122,43:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(PI)","loc":"d,43:126,43:127","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(QI)","loc":"d,43:122,43:123","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(QI)","loc":"d,43:126,43:127","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(RI)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(SI)","loc":"d,43:122,43:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(SI)","loc":"d,43:126,43:127","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(TI)","loc":"d,43:122,43:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(TI)","loc":"d,43:126,43:127","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(UI)","loc":"d,43:122,43:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(UI)","loc":"d,43:126,43:127","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(VI)","loc":"d,43:122,43:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(VI)","loc":"d,43:126,43:127","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -558,7 +558,7 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(WI)","loc":"d,43:142,43:147","isFatal":false} + {"type":"STOP","name":"","addr":"(WI)","loc":"d,43:150,43:155","isFatal":false} ],"elsesp": []}, {"type":"IF","name":"","addr":"(XI)","loc":"d,44:10,44:12", "condp": [ @@ -999,30 +999,30 @@ ], "pinsp": [ {"type":"CONST","name":"32'h0","addr":"(FP)","loc":"d,11:8,11:9","dtypep":"(NC)"}, - {"type":"AND","name":"","addr":"(GP)","loc":"d,61:14,61:21","dtypep":"(FB)", + {"type":"AND","name":"","addr":"(GP)","loc":"d,63:14,63:21","dtypep":"(FB)", "lhsp": [ - {"type":"CCAST","name":"","addr":"(HP)","loc":"d,61:22,61:25","dtypep":"(FB)","size":32, + {"type":"CCAST","name":"","addr":"(HP)","loc":"d,63:22,63:25","dtypep":"(FB)","size":32, "lhsp": [ - {"type":"VARREF","name":"clk","addr":"(IP)","loc":"d,61:22,61:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(IP)","loc":"d,63:22,63:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "rhsp": [ - {"type":"NOT","name":"","addr":"(JP)","loc":"d,61:14,61:21","dtypep":"(FB)", + {"type":"NOT","name":"","addr":"(JP)","loc":"d,63:14,63:21","dtypep":"(FB)", "lhsp": [ - {"type":"CCAST","name":"","addr":"(KP)","loc":"d,61:14,61:21","dtypep":"(FB)","size":32, + {"type":"CCAST","name":"","addr":"(KP)","loc":"d,63:14,63:21","dtypep":"(FB)","size":32, "lhsp": [ - {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(LP)","loc":"d,61:14,61:21","dtypep":"(FB)","access":"RD","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(LP)","loc":"d,63:14,63:21","dtypep":"(FB)","access":"RD","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ]}, - {"type":"ASSIGN","name":"","addr":"(MP)","loc":"d,61:22,61:25","dtypep":"(FB)", + {"type":"ASSIGN","name":"","addr":"(MP)","loc":"d,63:22,63:25","dtypep":"(FB)", "rhsp": [ - {"type":"VARREF","name":"clk","addr":"(NP)","loc":"d,61:22,61:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(NP)","loc":"d,63:22,63:25","dtypep":"(FB)","access":"RD","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(OP)","loc":"d,61:22,61:25","dtypep":"(FB)","access":"WR","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtrigprevexpr___TOP__clk__0","addr":"(OP)","loc":"d,63:22,63:25","dtypep":"(FB)","access":"WR","varp":"(N)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"TEXTBLOCK","name":"","addr":"(PP)","loc":"d,11:8,11:9","shortText":"", "nodesp": [ @@ -1156,9 +1156,9 @@ "varrefp": [ {"type":"VARREF","name":"__Vdly__t.e","addr":"(RR)","loc":"d,24:9,24:10","dtypep":"(M)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]}, - {"type":"VAR","name":"__Vtemp_1","addr":"(SR)","loc":"d,68:123,68:124","dtypep":"(RB)","origName":"__Vtemp_1","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"__Vtemp_2","addr":"(TR)","loc":"d,78:123,78:124","dtypep":"(RB)","origName":"__Vtemp_2","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"__Vtemp_3","addr":"(UR)","loc":"d,88:123,88:124","dtypep":"(RB)","origName":"__Vtemp_3","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} + {"type":"VAR","name":"__Vtemp_1","addr":"(SR)","loc":"d,70:123,70:124","dtypep":"(RB)","origName":"__Vtemp_1","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"__Vtemp_2","addr":"(TR)","loc":"d,80:123,80:124","dtypep":"(RB)","origName":"__Vtemp_2","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"__Vtemp_3","addr":"(UR)","loc":"d,90:123,90:124","dtypep":"(RB)","origName":"__Vtemp_3","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ], "stmtsp": [ {"type":"ASSIGNPRE","name":"","addr":"(VR)","loc":"d,23:17,23:20","dtypep":"(R)", @@ -1175,240 +1175,240 @@ "lhsp": [ {"type":"VARREF","name":"__Vdly__t.e","addr":"(AS)","loc":"d,24:9,24:10","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(BS)","loc":"d,62:11,62:13","dtypep":"(R)", + {"type":"ASSIGNDLY","name":"","addr":"(BS)","loc":"d,64:11,64:13","dtypep":"(R)", "rhsp": [ - {"type":"ADD","name":"","addr":"(CS)","loc":"d,62:18,62:19","dtypep":"(R)", + {"type":"ADD","name":"","addr":"(CS)","loc":"d,64:18,64:19","dtypep":"(R)", "lhsp": [ - {"type":"CCAST","name":"","addr":"(DS)","loc":"d,62:20,62:21","dtypep":"(NC)","size":32, + {"type":"CCAST","name":"","addr":"(DS)","loc":"d,64:20,64:21","dtypep":"(NC)","size":32, "lhsp": [ - {"type":"CONST","name":"32'sh1","addr":"(ES)","loc":"d,62:20,62:21","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh1","addr":"(ES)","loc":"d,64:20,64:21","dtypep":"(KB)"} ]} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(FS)","loc":"d,62:14,62:17","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(FS)","loc":"d,64:14,64:17","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "lhsp": [ - {"type":"VARREF","name":"__Vdly__t.cyc","addr":"(GS)","loc":"d,62:7,62:10","dtypep":"(R)","access":"WR","varp":"(MR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vdly__t.cyc","addr":"(GS)","loc":"d,64:7,64:10","dtypep":"(R)","access":"WR","varp":"(MR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"IF","name":"","addr":"(HS)","loc":"d,63:7,63:9", + {"type":"IF","name":"","addr":"(HS)","loc":"d,65:7,65:9", "condp": [ - {"type":"EQ","name":"","addr":"(IS)","loc":"d,63:14,63:16","dtypep":"(FB)", + {"type":"EQ","name":"","addr":"(IS)","loc":"d,65:14,65:16","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"32'sh0","addr":"(JS)","loc":"d,63:16,63:17","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh0","addr":"(JS)","loc":"d,65:16,65:17","dtypep":"(KB)"} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(KS)","loc":"d,63:11,63:14","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(KS)","loc":"d,65:11,65:14","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "thensp": [ - {"type":"ASSIGNDLY","name":"","addr":"(LS)","loc":"d,65:12,65:14","dtypep":"(AC)", + {"type":"ASSIGNDLY","name":"","addr":"(LS)","loc":"d,67:12,67:14","dtypep":"(AC)", "rhsp": [ - {"type":"CONST","name":"4'h1","addr":"(MS)","loc":"d,65:15,65:18","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(MS)","loc":"d,67:15,67:18","dtypep":"(AC)"} ], "lhsp": [ - {"type":"VARREF","name":"__Vdly__t.e","addr":"(NS)","loc":"d,65:10,65:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vdly__t.e","addr":"(NS)","loc":"d,67:10,67:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ], "elsesp": [ - {"type":"IF","name":"","addr":"(OS)","loc":"d,67:12,67:14", + {"type":"IF","name":"","addr":"(OS)","loc":"d,69:12,69:14", "condp": [ - {"type":"EQ","name":"","addr":"(PS)","loc":"d,67:19,67:21","dtypep":"(FB)", + {"type":"EQ","name":"","addr":"(PS)","loc":"d,69:19,69:21","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"32'sh1","addr":"(QS)","loc":"d,67:21,67:22","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh1","addr":"(QS)","loc":"d,69:21,69:22","dtypep":"(KB)"} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(RS)","loc":"d,67:16,67:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(RS)","loc":"d,69:16,69:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "thensp": [ - {"type":"IF","name":"","addr":"(SS)","loc":"d,68:13,68:15", + {"type":"IF","name":"","addr":"(SS)","loc":"d,70:13,70:15", "condp": [ - {"type":"NEQN","name":"","addr":"(TS)","loc":"d,68:26,68:28","dtypep":"(FB)", + {"type":"NEQN","name":"","addr":"(TS)","loc":"d,70:26,70:28","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"\\\"E01\\\"","addr":"(US)","loc":"d,68:30,68:35","dtypep":"(RB)"} + {"type":"CONST","name":"\\\"E01\\\"","addr":"(US)","loc":"d,70:30,70:35","dtypep":"(RB)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(VS)","loc":"d,68:18,68:19","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(VS)","loc":"d,70:18,70:19","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(WS)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(XS)","loc":"d,68:18,68:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(XS)","loc":"d,70:18,70:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(YS)","loc":"d,68:18,68:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(YS)","loc":"d,70:18,70:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(ZS)","loc":"d,68:18,68:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(ZS)","loc":"d,70:18,70:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(AT)","loc":"d,68:18,68:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(AT)","loc":"d,70:18,70:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"ASSIGN","name":"","addr":"(BT)","loc":"d,68:123,68:124","dtypep":"(RB)", + {"type":"ASSIGN","name":"","addr":"(BT)","loc":"d,70:123,70:124","dtypep":"(RB)", "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(CT)","loc":"d,68:123,68:124","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(CT)","loc":"d,70:123,70:124","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(DT)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(ET)","loc":"d,68:123,68:124","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(ET)","loc":"d,70:123,70:124","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(FT)","loc":"d,68:123,68:124","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(FT)","loc":"d,70:123,70:124","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(GT)","loc":"d,68:123,68:124","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(GT)","loc":"d,70:123,70:124","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(HT)","loc":"d,68:123,68:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(HT)","loc":"d,70:123,70:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ], "lhsp": [ - {"type":"VARREF","name":"__Vtemp_1","addr":"(IT)","loc":"d,68:123,68:124","dtypep":"(RB)","access":"WR","varp":"(SR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_1","addr":"(IT)","loc":"d,70:123,70:124","dtypep":"(RB)","access":"WR","varp":"(SR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"DISPLAY","name":"","addr":"(JT)","loc":"d,68:44,68:50", + {"type":"DISPLAY","name":"","addr":"(JT)","loc":"d,70:44,70:50", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:68: got='%@' exp='E01'\\n","addr":"(KT)","loc":"d,68:44,68:50","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:70: got='%@' exp='E01'\\n","addr":"(KT)","loc":"d,70:44,70:50","dtypep":"(RB)", "exprsp": [ - {"type":"VARREF","name":"__Vtemp_1","addr":"(LT)","loc":"d,68:123,68:124","dtypep":"(RB)","access":"RD","varp":"(SR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_1","addr":"(LT)","loc":"d,70:123,70:124","dtypep":"(RB)","access":"RD","varp":"(SR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(MT)","loc":"d,68:142,68:147","isFatal":false} + {"type":"STOP","name":"","addr":"(MT)","loc":"d,70:142,70:147","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(NT)","loc":"d,69:13,69:15", + {"type":"IF","name":"","addr":"(NT)","loc":"d,71:13,71:15", "condp": [ - {"type":"NEQ","name":"","addr":"(OT)","loc":"d,69:26,69:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(OT)","loc":"d,71:26,71:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(PT)","loc":"d,69:31,69:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(PT)","loc":"d,71:31,71:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(QT)","loc":"d,69:18,69:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(QT)","loc":"d,71:18,71:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(RT)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(ST)","loc":"d,69:18,69:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(ST)","loc":"d,71:18,71:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(TT)","loc":"d,69:18,69:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(TT)","loc":"d,71:18,71:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(UT)","loc":"d,69:18,69:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(UT)","loc":"d,71:18,71:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(VT)","loc":"d,69:18,69:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(VT)","loc":"d,71:18,71:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(WT)","loc":"d,69:43,69:49", + {"type":"DISPLAY","name":"","addr":"(WT)","loc":"d,71:43,71:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:69: got='h%x exp='h3\\n","addr":"(XT)","loc":"d,69:43,69:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:71: got='h%x exp='h3\\n","addr":"(XT)","loc":"d,71:43,71:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(YT)","loc":"d,69:122,69:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(YT)","loc":"d,71:122,71:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(ZT)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(AU)","loc":"d,69:122,69:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(AU)","loc":"d,71:122,71:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(BU)","loc":"d,69:122,69:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(BU)","loc":"d,71:122,71:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(CU)","loc":"d,69:122,69:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(CU)","loc":"d,71:122,71:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(DU)","loc":"d,69:122,69:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(DU)","loc":"d,71:122,71:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(EU)","loc":"d,69:139,69:144","isFatal":false} + {"type":"STOP","name":"","addr":"(EU)","loc":"d,71:139,71:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(FU)","loc":"d,70:13,70:15", + {"type":"IF","name":"","addr":"(FU)","loc":"d,72:13,72:15", "condp": [ - {"type":"NEQ","name":"","addr":"(GU)","loc":"d,70:29,70:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(GU)","loc":"d,72:29,72:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(HU)","loc":"d,70:34,70:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(HU)","loc":"d,72:34,72:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IU)","loc":"d,70:18,70:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(IU)","loc":"d,72:18,72:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JU)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KU)","loc":"d,70:18,70:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KU)","loc":"d,72:18,72:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LU)","loc":"d,70:18,70:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LU)","loc":"d,72:18,72:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(MU)","loc":"d,70:18,70:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(MU)","loc":"d,72:18,72:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(NU)","loc":"d,70:18,70:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(NU)","loc":"d,72:18,72:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(OU)","loc":"d,70:46,70:52", + {"type":"DISPLAY","name":"","addr":"(OU)","loc":"d,72:46,72:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:70: got='h%x exp='h3\\n","addr":"(PU)","loc":"d,70:46,70:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:72: got='h%x exp='h3\\n","addr":"(PU)","loc":"d,72:46,72:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(QU)","loc":"d,70:125,70:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(QU)","loc":"d,72:125,72:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(RU)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(SU)","loc":"d,70:125,70:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(SU)","loc":"d,72:125,72:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(TU)","loc":"d,70:125,70:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(TU)","loc":"d,72:125,72:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(UU)","loc":"d,70:125,70:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(UU)","loc":"d,72:125,72:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(VU)","loc":"d,70:125,70:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(VU)","loc":"d,72:125,72:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(WU)","loc":"d,70:145,70:150","isFatal":false} + {"type":"STOP","name":"","addr":"(WU)","loc":"d,72:145,72:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(XU)","loc":"d,71:13,71:15", + {"type":"IF","name":"","addr":"(XU)","loc":"d,73:13,73:15", "condp": [ - {"type":"NEQ","name":"","addr":"(YU)","loc":"d,71:29,71:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(YU)","loc":"d,73:29,73:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(ZU)","loc":"d,71:34,71:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(ZU)","loc":"d,73:34,73:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AV)","loc":"d,71:18,71:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(AV)","loc":"d,73:18,73:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(BV)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CV)","loc":"d,71:18,71:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CV)","loc":"d,73:18,73:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DV)","loc":"d,71:18,71:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DV)","loc":"d,73:18,73:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(EV)","loc":"d,71:18,71:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(EV)","loc":"d,73:18,73:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(FV)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(GV)","loc":"d,71:18,71:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(GV)","loc":"d,73:18,73:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(HV)","loc":"d,71:18,71:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(HV)","loc":"d,73:18,73:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(IV)","loc":"d,71:18,71:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(IV)","loc":"d,73:18,73:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(JV)","loc":"d,71:18,71:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(JV)","loc":"d,73:18,73:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1417,33 +1417,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(KV)","loc":"d,71:46,71:52", + {"type":"DISPLAY","name":"","addr":"(KV)","loc":"d,73:46,73:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:71: got='h%x exp='h4\\n","addr":"(LV)","loc":"d,71:46,71:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:73: got='h%x exp='h4\\n","addr":"(LV)","loc":"d,73:46,73:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(MV)","loc":"d,71:125,71:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(MV)","loc":"d,73:125,73:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(NV)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(OV)","loc":"d,71:125,71:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(OV)","loc":"d,73:125,73:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(PV)","loc":"d,71:125,71:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(PV)","loc":"d,73:125,73:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(QV)","loc":"d,71:125,71:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(QV)","loc":"d,73:125,73:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(RV)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(SV)","loc":"d,71:125,71:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(SV)","loc":"d,73:125,73:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(TV)","loc":"d,71:125,71:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(TV)","loc":"d,73:125,73:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(UV)","loc":"d,71:125,71:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(UV)","loc":"d,73:125,73:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(VV)","loc":"d,71:125,71:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(VV)","loc":"d,73:125,73:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1451,138 +1451,138 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(WV)","loc":"d,71:145,71:150","isFatal":false} + {"type":"STOP","name":"","addr":"(WV)","loc":"d,73:145,73:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(XV)","loc":"d,72:13,72:15", + {"type":"IF","name":"","addr":"(XV)","loc":"d,74:13,74:15", "condp": [ - {"type":"NEQ","name":"","addr":"(YV)","loc":"d,72:26,72:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(YV)","loc":"d,74:26,74:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(ZV)","loc":"d,72:31,72:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(ZV)","loc":"d,74:31,74:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AW)","loc":"d,72:18,72:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(AW)","loc":"d,74:18,74:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(BW)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CW)","loc":"d,72:18,72:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CW)","loc":"d,74:18,74:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DW)","loc":"d,72:18,72:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DW)","loc":"d,74:18,74:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EW)","loc":"d,72:18,72:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EW)","loc":"d,74:18,74:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FW)","loc":"d,72:18,72:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FW)","loc":"d,74:18,74:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(GW)","loc":"d,72:43,72:49", + {"type":"DISPLAY","name":"","addr":"(GW)","loc":"d,74:43,74:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:72: got='h%x exp='h4\\n","addr":"(HW)","loc":"d,72:43,72:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:74: got='h%x exp='h4\\n","addr":"(HW)","loc":"d,74:43,74:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IW)","loc":"d,72:122,72:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(IW)","loc":"d,74:122,74:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(JW)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KW)","loc":"d,72:122,72:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KW)","loc":"d,74:122,74:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LW)","loc":"d,72:122,72:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LW)","loc":"d,74:122,74:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(MW)","loc":"d,72:122,72:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(MW)","loc":"d,74:122,74:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(NW)","loc":"d,72:122,72:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(NW)","loc":"d,74:122,74:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(OW)","loc":"d,72:139,72:144","isFatal":false} + {"type":"STOP","name":"","addr":"(OW)","loc":"d,74:139,74:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(PW)","loc":"d,73:13,73:15", + {"type":"IF","name":"","addr":"(PW)","loc":"d,75:13,75:15", "condp": [ - {"type":"NEQ","name":"","addr":"(QW)","loc":"d,73:29,73:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(QW)","loc":"d,75:29,75:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(RW)","loc":"d,73:34,73:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(RW)","loc":"d,75:34,75:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SW)","loc":"d,73:18,73:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(SW)","loc":"d,75:18,75:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(TW)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UW)","loc":"d,73:18,73:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UW)","loc":"d,75:18,75:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VW)","loc":"d,73:18,73:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VW)","loc":"d,75:18,75:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(WW)","loc":"d,73:18,73:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(WW)","loc":"d,75:18,75:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(XW)","loc":"d,73:18,73:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(XW)","loc":"d,75:18,75:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(YW)","loc":"d,73:46,73:52", + {"type":"DISPLAY","name":"","addr":"(YW)","loc":"d,75:46,75:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:73: got='h%x exp='h4\\n","addr":"(ZW)","loc":"d,73:46,73:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:75: got='h%x exp='h4\\n","addr":"(ZW)","loc":"d,75:46,75:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AX)","loc":"d,73:125,73:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(AX)","loc":"d,75:125,75:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(BX)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CX)","loc":"d,73:125,73:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CX)","loc":"d,75:125,75:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DX)","loc":"d,73:125,73:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DX)","loc":"d,75:125,75:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EX)","loc":"d,73:125,73:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EX)","loc":"d,75:125,75:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FX)","loc":"d,73:125,73:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FX)","loc":"d,75:125,75:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(GX)","loc":"d,73:145,73:150","isFatal":false} + {"type":"STOP","name":"","addr":"(GX)","loc":"d,75:145,75:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(HX)","loc":"d,74:13,74:15", + {"type":"IF","name":"","addr":"(HX)","loc":"d,76:13,76:15", "condp": [ - {"type":"NEQ","name":"","addr":"(IX)","loc":"d,74:29,74:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(IX)","loc":"d,76:29,76:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(JX)","loc":"d,74:34,74:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(JX)","loc":"d,76:34,76:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(KX)","loc":"d,74:18,74:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(KX)","loc":"d,76:18,76:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(LX)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(MX)","loc":"d,74:18,74:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(MX)","loc":"d,76:18,76:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(NX)","loc":"d,74:18,74:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(NX)","loc":"d,76:18,76:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(OX)","loc":"d,74:18,74:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(OX)","loc":"d,76:18,76:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(PX)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(QX)","loc":"d,74:18,74:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(QX)","loc":"d,76:18,76:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(RX)","loc":"d,74:18,74:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(RX)","loc":"d,76:18,76:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(SX)","loc":"d,74:18,74:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(SX)","loc":"d,76:18,76:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(TX)","loc":"d,74:18,74:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(TX)","loc":"d,76:18,76:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1591,33 +1591,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(UX)","loc":"d,74:46,74:52", + {"type":"DISPLAY","name":"","addr":"(UX)","loc":"d,76:46,76:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:74: got='h%x exp='h3\\n","addr":"(VX)","loc":"d,74:46,74:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:76: got='h%x exp='h3\\n","addr":"(VX)","loc":"d,76:46,76:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WX)","loc":"d,74:125,74:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(WX)","loc":"d,76:125,76:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(XX)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YX)","loc":"d,74:125,74:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YX)","loc":"d,76:125,76:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZX)","loc":"d,74:125,74:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZX)","loc":"d,76:125,76:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AY)","loc":"d,74:125,74:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(AY)","loc":"d,76:125,76:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(BY)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CY)","loc":"d,74:125,74:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CY)","loc":"d,76:125,76:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DY)","loc":"d,74:125,74:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DY)","loc":"d,76:125,76:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EY)","loc":"d,74:125,74:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EY)","loc":"d,76:125,76:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FY)","loc":"d,74:125,74:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FY)","loc":"d,76:125,76:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1625,215 +1625,215 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(GY)","loc":"d,74:145,74:150","isFatal":false} + {"type":"STOP","name":"","addr":"(GY)","loc":"d,76:145,76:150","isFatal":false} ],"elsesp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(HY)","loc":"d,75:12,75:14","dtypep":"(AC)", + {"type":"ASSIGNDLY","name":"","addr":"(HY)","loc":"d,77:12,77:14","dtypep":"(AC)", "rhsp": [ - {"type":"CONST","name":"4'h3","addr":"(IY)","loc":"d,75:15,75:18","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(IY)","loc":"d,77:15,77:18","dtypep":"(AC)"} ], "lhsp": [ - {"type":"VARREF","name":"__Vdly__t.e","addr":"(JY)","loc":"d,75:10,75:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vdly__t.e","addr":"(JY)","loc":"d,77:10,77:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ], "elsesp": [ - {"type":"IF","name":"","addr":"(KY)","loc":"d,77:12,77:14", + {"type":"IF","name":"","addr":"(KY)","loc":"d,79:12,79:14", "condp": [ - {"type":"EQ","name":"","addr":"(LY)","loc":"d,77:19,77:21","dtypep":"(FB)", + {"type":"EQ","name":"","addr":"(LY)","loc":"d,79:19,79:21","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"32'sh2","addr":"(MY)","loc":"d,77:21,77:22","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh2","addr":"(MY)","loc":"d,79:21,79:22","dtypep":"(KB)"} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(NY)","loc":"d,77:16,77:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(NY)","loc":"d,79:16,79:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "thensp": [ - {"type":"IF","name":"","addr":"(OY)","loc":"d,78:13,78:15", + {"type":"IF","name":"","addr":"(OY)","loc":"d,80:13,80:15", "condp": [ - {"type":"NEQN","name":"","addr":"(PY)","loc":"d,78:26,78:28","dtypep":"(FB)", + {"type":"NEQN","name":"","addr":"(PY)","loc":"d,80:26,80:28","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"\\\"E03\\\"","addr":"(QY)","loc":"d,78:30,78:35","dtypep":"(RB)"} + {"type":"CONST","name":"\\\"E03\\\"","addr":"(QY)","loc":"d,80:30,80:35","dtypep":"(RB)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(RY)","loc":"d,78:18,78:19","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(RY)","loc":"d,80:18,80:19","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(SY)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(TY)","loc":"d,78:18,78:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(TY)","loc":"d,80:18,80:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(UY)","loc":"d,78:18,78:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(UY)","loc":"d,80:18,80:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(VY)","loc":"d,78:18,78:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(VY)","loc":"d,80:18,80:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(WY)","loc":"d,78:18,78:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(WY)","loc":"d,80:18,80:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"ASSIGN","name":"","addr":"(XY)","loc":"d,78:123,78:124","dtypep":"(RB)", + {"type":"ASSIGN","name":"","addr":"(XY)","loc":"d,80:123,80:124","dtypep":"(RB)", "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(YY)","loc":"d,78:123,78:124","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(YY)","loc":"d,80:123,80:124","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(ZY)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(AZ)","loc":"d,78:123,78:124","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(AZ)","loc":"d,80:123,80:124","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(BZ)","loc":"d,78:123,78:124","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(BZ)","loc":"d,80:123,80:124","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(CZ)","loc":"d,78:123,78:124","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(CZ)","loc":"d,80:123,80:124","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(DZ)","loc":"d,78:123,78:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(DZ)","loc":"d,80:123,80:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ], "lhsp": [ - {"type":"VARREF","name":"__Vtemp_2","addr":"(EZ)","loc":"d,78:123,78:124","dtypep":"(RB)","access":"WR","varp":"(TR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_2","addr":"(EZ)","loc":"d,80:123,80:124","dtypep":"(RB)","access":"WR","varp":"(TR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"DISPLAY","name":"","addr":"(FZ)","loc":"d,78:44,78:50", + {"type":"DISPLAY","name":"","addr":"(FZ)","loc":"d,80:44,80:50", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:78: got='%@' exp='E03'\\n","addr":"(GZ)","loc":"d,78:44,78:50","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:80: got='%@' exp='E03'\\n","addr":"(GZ)","loc":"d,80:44,80:50","dtypep":"(RB)", "exprsp": [ - {"type":"VARREF","name":"__Vtemp_2","addr":"(HZ)","loc":"d,78:123,78:124","dtypep":"(RB)","access":"RD","varp":"(TR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_2","addr":"(HZ)","loc":"d,80:123,80:124","dtypep":"(RB)","access":"RD","varp":"(TR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(IZ)","loc":"d,78:142,78:147","isFatal":false} + {"type":"STOP","name":"","addr":"(IZ)","loc":"d,80:142,80:147","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(JZ)","loc":"d,79:13,79:15", + {"type":"IF","name":"","addr":"(JZ)","loc":"d,81:13,81:15", "condp": [ - {"type":"NEQ","name":"","addr":"(KZ)","loc":"d,79:26,79:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(KZ)","loc":"d,81:26,81:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(LZ)","loc":"d,79:31,79:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(LZ)","loc":"d,81:31,81:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(MZ)","loc":"d,79:18,79:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(MZ)","loc":"d,81:18,81:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(NZ)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(OZ)","loc":"d,79:18,79:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(OZ)","loc":"d,81:18,81:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(PZ)","loc":"d,79:18,79:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(PZ)","loc":"d,81:18,81:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(QZ)","loc":"d,79:18,79:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(QZ)","loc":"d,81:18,81:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(RZ)","loc":"d,79:18,79:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(RZ)","loc":"d,81:18,81:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(SZ)","loc":"d,79:43,79:49", + {"type":"DISPLAY","name":"","addr":"(SZ)","loc":"d,81:43,81:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:79: got='h%x exp='h4\\n","addr":"(TZ)","loc":"d,79:43,79:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:81: got='h%x exp='h4\\n","addr":"(TZ)","loc":"d,81:43,81:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(UZ)","loc":"d,79:122,79:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(UZ)","loc":"d,81:122,81:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(VZ)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(WZ)","loc":"d,79:122,79:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(WZ)","loc":"d,81:122,81:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(XZ)","loc":"d,79:122,79:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(XZ)","loc":"d,81:122,81:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(YZ)","loc":"d,79:122,79:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(YZ)","loc":"d,81:122,81:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(ZZ)","loc":"d,79:122,79:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(ZZ)","loc":"d,81:122,81:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(AAB)","loc":"d,79:139,79:144","isFatal":false} + {"type":"STOP","name":"","addr":"(AAB)","loc":"d,81:139,81:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(BAB)","loc":"d,80:13,80:15", + {"type":"IF","name":"","addr":"(BAB)","loc":"d,82:13,82:15", "condp": [ - {"type":"NEQ","name":"","addr":"(CAB)","loc":"d,80:29,80:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(CAB)","loc":"d,82:29,82:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(DAB)","loc":"d,80:34,80:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(DAB)","loc":"d,82:34,82:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(EAB)","loc":"d,80:18,80:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(EAB)","loc":"d,82:18,82:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(FAB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(GAB)","loc":"d,80:18,80:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(GAB)","loc":"d,82:18,82:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(HAB)","loc":"d,80:18,80:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(HAB)","loc":"d,82:18,82:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(IAB)","loc":"d,80:18,80:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(IAB)","loc":"d,82:18,82:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(JAB)","loc":"d,80:18,80:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(JAB)","loc":"d,82:18,82:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(KAB)","loc":"d,80:46,80:52", + {"type":"DISPLAY","name":"","addr":"(KAB)","loc":"d,82:46,82:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:80: got='h%x exp='h4\\n","addr":"(LAB)","loc":"d,80:46,80:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:82: got='h%x exp='h4\\n","addr":"(LAB)","loc":"d,82:46,82:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(MAB)","loc":"d,80:125,80:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(MAB)","loc":"d,82:125,82:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(NAB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(OAB)","loc":"d,80:125,80:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(OAB)","loc":"d,82:125,82:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(PAB)","loc":"d,80:125,80:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(PAB)","loc":"d,82:125,82:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(QAB)","loc":"d,80:125,80:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(QAB)","loc":"d,82:125,82:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(RAB)","loc":"d,80:125,80:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(RAB)","loc":"d,82:125,82:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(SAB)","loc":"d,80:145,80:150","isFatal":false} + {"type":"STOP","name":"","addr":"(SAB)","loc":"d,82:145,82:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(TAB)","loc":"d,81:13,81:15", + {"type":"IF","name":"","addr":"(TAB)","loc":"d,83:13,83:15", "condp": [ - {"type":"NEQ","name":"","addr":"(UAB)","loc":"d,81:29,81:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(UAB)","loc":"d,83:29,83:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(VAB)","loc":"d,81:34,81:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(VAB)","loc":"d,83:34,83:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WAB)","loc":"d,81:18,81:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(WAB)","loc":"d,83:18,83:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(XAB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YAB)","loc":"d,81:18,81:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YAB)","loc":"d,83:18,83:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZAB)","loc":"d,81:18,81:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZAB)","loc":"d,83:18,83:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(ABB)","loc":"d,81:18,81:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(ABB)","loc":"d,83:18,83:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(BBB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CBB)","loc":"d,81:18,81:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CBB)","loc":"d,83:18,83:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DBB)","loc":"d,81:18,81:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DBB)","loc":"d,83:18,83:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EBB)","loc":"d,81:18,81:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EBB)","loc":"d,83:18,83:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FBB)","loc":"d,81:18,81:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FBB)","loc":"d,83:18,83:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1842,33 +1842,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(GBB)","loc":"d,81:46,81:52", + {"type":"DISPLAY","name":"","addr":"(GBB)","loc":"d,83:46,83:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:81: got='h%x exp='h1\\n","addr":"(HBB)","loc":"d,81:46,81:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:83: got='h%x exp='h1\\n","addr":"(HBB)","loc":"d,83:46,83:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IBB)","loc":"d,81:125,81:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(IBB)","loc":"d,83:125,83:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JBB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KBB)","loc":"d,81:125,81:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KBB)","loc":"d,83:125,83:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LBB)","loc":"d,81:125,81:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LBB)","loc":"d,83:125,83:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(MBB)","loc":"d,81:125,81:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(MBB)","loc":"d,83:125,83:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(NBB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(OBB)","loc":"d,81:125,81:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(OBB)","loc":"d,83:125,83:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(PBB)","loc":"d,81:125,81:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(PBB)","loc":"d,83:125,83:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(QBB)","loc":"d,81:125,81:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(QBB)","loc":"d,83:125,83:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(RBB)","loc":"d,81:125,81:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(RBB)","loc":"d,83:125,83:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -1876,138 +1876,138 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(SBB)","loc":"d,81:145,81:150","isFatal":false} + {"type":"STOP","name":"","addr":"(SBB)","loc":"d,83:145,83:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(TBB)","loc":"d,82:13,82:15", + {"type":"IF","name":"","addr":"(TBB)","loc":"d,84:13,84:15", "condp": [ - {"type":"NEQ","name":"","addr":"(UBB)","loc":"d,82:26,82:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(UBB)","loc":"d,84:26,84:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(VBB)","loc":"d,82:31,82:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(VBB)","loc":"d,84:31,84:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WBB)","loc":"d,82:18,82:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(WBB)","loc":"d,84:18,84:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(XBB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YBB)","loc":"d,82:18,82:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YBB)","loc":"d,84:18,84:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZBB)","loc":"d,82:18,82:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZBB)","loc":"d,84:18,84:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(ACB)","loc":"d,82:18,82:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(ACB)","loc":"d,84:18,84:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(BCB)","loc":"d,82:18,82:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(BCB)","loc":"d,84:18,84:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(CCB)","loc":"d,82:43,82:49", + {"type":"DISPLAY","name":"","addr":"(CCB)","loc":"d,84:43,84:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:82: got='h%x exp='h1\\n","addr":"(DCB)","loc":"d,82:43,82:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:84: got='h%x exp='h1\\n","addr":"(DCB)","loc":"d,84:43,84:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(ECB)","loc":"d,82:122,82:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(ECB)","loc":"d,84:122,84:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(FCB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(GCB)","loc":"d,82:122,82:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(GCB)","loc":"d,84:122,84:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(HCB)","loc":"d,82:122,82:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(HCB)","loc":"d,84:122,84:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(ICB)","loc":"d,82:122,82:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(ICB)","loc":"d,84:122,84:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(JCB)","loc":"d,82:122,82:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(JCB)","loc":"d,84:122,84:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(KCB)","loc":"d,82:139,82:144","isFatal":false} + {"type":"STOP","name":"","addr":"(KCB)","loc":"d,84:139,84:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(LCB)","loc":"d,83:13,83:15", + {"type":"IF","name":"","addr":"(LCB)","loc":"d,85:13,85:15", "condp": [ - {"type":"NEQ","name":"","addr":"(MCB)","loc":"d,83:29,83:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(MCB)","loc":"d,85:29,85:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(NCB)","loc":"d,83:34,83:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(NCB)","loc":"d,85:34,85:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(OCB)","loc":"d,83:18,83:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(OCB)","loc":"d,85:18,85:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(PCB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(QCB)","loc":"d,83:18,83:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(QCB)","loc":"d,85:18,85:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(RCB)","loc":"d,83:18,83:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(RCB)","loc":"d,85:18,85:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(SCB)","loc":"d,83:18,83:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(SCB)","loc":"d,85:18,85:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(TCB)","loc":"d,83:18,83:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(TCB)","loc":"d,85:18,85:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(UCB)","loc":"d,83:46,83:52", + {"type":"DISPLAY","name":"","addr":"(UCB)","loc":"d,85:46,85:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:83: got='h%x exp='h1\\n","addr":"(VCB)","loc":"d,83:46,83:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:85: got='h%x exp='h1\\n","addr":"(VCB)","loc":"d,85:46,85:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WCB)","loc":"d,83:125,83:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(WCB)","loc":"d,85:125,85:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(XCB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YCB)","loc":"d,83:125,83:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YCB)","loc":"d,85:125,85:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZCB)","loc":"d,83:125,83:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZCB)","loc":"d,85:125,85:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(ADB)","loc":"d,83:125,83:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(ADB)","loc":"d,85:125,85:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(BDB)","loc":"d,83:125,83:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(BDB)","loc":"d,85:125,85:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(CDB)","loc":"d,83:145,83:150","isFatal":false} + {"type":"STOP","name":"","addr":"(CDB)","loc":"d,85:145,85:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(DDB)","loc":"d,84:13,84:15", + {"type":"IF","name":"","addr":"(DDB)","loc":"d,86:13,86:15", "condp": [ - {"type":"NEQ","name":"","addr":"(EDB)","loc":"d,84:29,84:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(EDB)","loc":"d,86:29,86:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h4","addr":"(FDB)","loc":"d,84:34,84:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(FDB)","loc":"d,86:34,86:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(GDB)","loc":"d,84:18,84:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(GDB)","loc":"d,86:18,86:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(HDB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(IDB)","loc":"d,84:18,84:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(IDB)","loc":"d,86:18,86:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(JDB)","loc":"d,84:18,84:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(JDB)","loc":"d,86:18,86:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(KDB)","loc":"d,84:18,84:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(KDB)","loc":"d,86:18,86:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(LDB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(MDB)","loc":"d,84:18,84:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(MDB)","loc":"d,86:18,86:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(NDB)","loc":"d,84:18,84:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(NDB)","loc":"d,86:18,86:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(ODB)","loc":"d,84:18,84:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(ODB)","loc":"d,86:18,86:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(PDB)","loc":"d,84:18,84:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(PDB)","loc":"d,86:18,86:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2016,33 +2016,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(QDB)","loc":"d,84:46,84:52", + {"type":"DISPLAY","name":"","addr":"(QDB)","loc":"d,86:46,86:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:84: got='h%x exp='h4\\n","addr":"(RDB)","loc":"d,84:46,84:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:86: got='h%x exp='h4\\n","addr":"(RDB)","loc":"d,86:46,86:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SDB)","loc":"d,84:125,84:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(SDB)","loc":"d,86:125,86:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(TDB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UDB)","loc":"d,84:125,84:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UDB)","loc":"d,86:125,86:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VDB)","loc":"d,84:125,84:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VDB)","loc":"d,86:125,86:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WDB)","loc":"d,84:125,84:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(WDB)","loc":"d,86:125,86:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(XDB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YDB)","loc":"d,84:125,84:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YDB)","loc":"d,86:125,86:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZDB)","loc":"d,84:125,84:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZDB)","loc":"d,86:125,86:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(AEB)","loc":"d,84:125,84:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(AEB)","loc":"d,86:125,86:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(BEB)","loc":"d,84:125,84:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(BEB)","loc":"d,86:125,86:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2050,215 +2050,215 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(CEB)","loc":"d,84:145,84:150","isFatal":false} + {"type":"STOP","name":"","addr":"(CEB)","loc":"d,86:145,86:150","isFatal":false} ],"elsesp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(DEB)","loc":"d,85:12,85:14","dtypep":"(AC)", + {"type":"ASSIGNDLY","name":"","addr":"(DEB)","loc":"d,87:12,87:14","dtypep":"(AC)", "rhsp": [ - {"type":"CONST","name":"4'h4","addr":"(EEB)","loc":"d,85:15,85:18","dtypep":"(AC)"} + {"type":"CONST","name":"4'h4","addr":"(EEB)","loc":"d,87:15,87:18","dtypep":"(AC)"} ], "lhsp": [ - {"type":"VARREF","name":"__Vdly__t.e","addr":"(FEB)","loc":"d,85:10,85:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vdly__t.e","addr":"(FEB)","loc":"d,87:10,87:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ], "elsesp": [ - {"type":"IF","name":"","addr":"(GEB)","loc":"d,87:12,87:14", + {"type":"IF","name":"","addr":"(GEB)","loc":"d,89:12,89:14", "condp": [ - {"type":"EQ","name":"","addr":"(HEB)","loc":"d,87:19,87:21","dtypep":"(FB)", + {"type":"EQ","name":"","addr":"(HEB)","loc":"d,89:19,89:21","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"32'sh3","addr":"(IEB)","loc":"d,87:21,87:22","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh3","addr":"(IEB)","loc":"d,89:21,89:22","dtypep":"(KB)"} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(JEB)","loc":"d,87:16,87:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(JEB)","loc":"d,89:16,89:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "thensp": [ - {"type":"IF","name":"","addr":"(KEB)","loc":"d,88:13,88:15", + {"type":"IF","name":"","addr":"(KEB)","loc":"d,90:13,90:15", "condp": [ - {"type":"NEQN","name":"","addr":"(LEB)","loc":"d,88:26,88:28","dtypep":"(FB)", + {"type":"NEQN","name":"","addr":"(LEB)","loc":"d,90:26,90:28","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"\\\"E04\\\"","addr":"(MEB)","loc":"d,88:30,88:35","dtypep":"(RB)"} + {"type":"CONST","name":"\\\"E04\\\"","addr":"(MEB)","loc":"d,90:30,90:35","dtypep":"(RB)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(NEB)","loc":"d,88:18,88:19","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(NEB)","loc":"d,90:18,90:19","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(OEB)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(PEB)","loc":"d,88:18,88:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(PEB)","loc":"d,90:18,90:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(QEB)","loc":"d,88:18,88:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(QEB)","loc":"d,90:18,90:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(REB)","loc":"d,88:18,88:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(REB)","loc":"d,90:18,90:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(SEB)","loc":"d,88:18,88:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(SEB)","loc":"d,90:18,90:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"ASSIGN","name":"","addr":"(TEB)","loc":"d,88:123,88:124","dtypep":"(RB)", + {"type":"ASSIGN","name":"","addr":"(TEB)","loc":"d,90:123,90:124","dtypep":"(RB)", "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(UEB)","loc":"d,88:123,88:124","dtypep":"(RB)", + {"type":"ARRAYSEL","name":"","addr":"(UEB)","loc":"d,90:123,90:124","dtypep":"(RB)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_name1","addr":"(VEB)","loc":"d,17:12,17:16","dtypep":"(OM)","access":"RD","varp":"(PM)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(WEB)","loc":"d,88:123,88:124","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(WEB)","loc":"d,90:123,90:124","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(XEB)","loc":"d,88:123,88:124","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(XEB)","loc":"d,90:123,90:124","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(YEB)","loc":"d,88:123,88:124","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(YEB)","loc":"d,90:123,90:124","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(ZEB)","loc":"d,88:123,88:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(ZEB)","loc":"d,90:123,90:124","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ], "lhsp": [ - {"type":"VARREF","name":"__Vtemp_3","addr":"(AFB)","loc":"d,88:123,88:124","dtypep":"(RB)","access":"WR","varp":"(UR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_3","addr":"(AFB)","loc":"d,90:123,90:124","dtypep":"(RB)","access":"WR","varp":"(UR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"DISPLAY","name":"","addr":"(BFB)","loc":"d,88:44,88:50", + {"type":"DISPLAY","name":"","addr":"(BFB)","loc":"d,90:44,90:50", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:88: got='%@' exp='E04'\\n","addr":"(CFB)","loc":"d,88:44,88:50","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:90: got='%@' exp='E04'\\n","addr":"(CFB)","loc":"d,90:44,90:50","dtypep":"(RB)", "exprsp": [ - {"type":"VARREF","name":"__Vtemp_3","addr":"(DFB)","loc":"d,88:123,88:124","dtypep":"(RB)","access":"RD","varp":"(UR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vtemp_3","addr":"(DFB)","loc":"d,90:123,90:124","dtypep":"(RB)","access":"RD","varp":"(UR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(EFB)","loc":"d,88:142,88:147","isFatal":false} + {"type":"STOP","name":"","addr":"(EFB)","loc":"d,90:142,90:147","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(FFB)","loc":"d,89:13,89:15", + {"type":"IF","name":"","addr":"(FFB)","loc":"d,91:13,91:15", "condp": [ - {"type":"NEQ","name":"","addr":"(GFB)","loc":"d,89:26,89:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(GFB)","loc":"d,91:26,91:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(HFB)","loc":"d,89:31,89:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(HFB)","loc":"d,91:31,91:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IFB)","loc":"d,89:18,89:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(IFB)","loc":"d,91:18,91:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JFB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KFB)","loc":"d,89:18,89:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KFB)","loc":"d,91:18,91:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LFB)","loc":"d,89:18,89:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LFB)","loc":"d,91:18,91:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(MFB)","loc":"d,89:18,89:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(MFB)","loc":"d,91:18,91:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(NFB)","loc":"d,89:18,89:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(NFB)","loc":"d,91:18,91:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(OFB)","loc":"d,89:43,89:49", + {"type":"DISPLAY","name":"","addr":"(OFB)","loc":"d,91:43,91:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:89: got='h%x exp='h1\\n","addr":"(PFB)","loc":"d,89:43,89:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:91: got='h%x exp='h1\\n","addr":"(PFB)","loc":"d,91:43,91:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(QFB)","loc":"d,89:122,89:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(QFB)","loc":"d,91:122,91:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(RFB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(SFB)","loc":"d,89:122,89:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(SFB)","loc":"d,91:122,91:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(TFB)","loc":"d,89:122,89:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(TFB)","loc":"d,91:122,91:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(UFB)","loc":"d,89:122,89:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(UFB)","loc":"d,91:122,91:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(VFB)","loc":"d,89:122,89:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(VFB)","loc":"d,91:122,91:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(WFB)","loc":"d,89:139,89:144","isFatal":false} + {"type":"STOP","name":"","addr":"(WFB)","loc":"d,91:139,91:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(XFB)","loc":"d,90:13,90:15", + {"type":"IF","name":"","addr":"(XFB)","loc":"d,92:13,92:15", "condp": [ - {"type":"NEQ","name":"","addr":"(YFB)","loc":"d,90:29,90:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(YFB)","loc":"d,92:29,92:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(ZFB)","loc":"d,90:34,90:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(ZFB)","loc":"d,92:34,92:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AGB)","loc":"d,90:18,90:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(AGB)","loc":"d,92:18,92:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(BGB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CGB)","loc":"d,90:18,90:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CGB)","loc":"d,92:18,92:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DGB)","loc":"d,90:18,90:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DGB)","loc":"d,92:18,92:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EGB)","loc":"d,90:18,90:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EGB)","loc":"d,92:18,92:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FGB)","loc":"d,90:18,90:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FGB)","loc":"d,92:18,92:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(GGB)","loc":"d,90:46,90:52", + {"type":"DISPLAY","name":"","addr":"(GGB)","loc":"d,92:46,92:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:90: got='h%x exp='h1\\n","addr":"(HGB)","loc":"d,90:46,90:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:92: got='h%x exp='h1\\n","addr":"(HGB)","loc":"d,92:46,92:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IGB)","loc":"d,90:125,90:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(IGB)","loc":"d,92:125,92:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JGB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KGB)","loc":"d,90:125,90:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KGB)","loc":"d,92:125,92:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LGB)","loc":"d,90:125,90:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LGB)","loc":"d,92:125,92:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(MGB)","loc":"d,90:125,90:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(MGB)","loc":"d,92:125,92:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(NGB)","loc":"d,90:125,90:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(NGB)","loc":"d,92:125,92:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(OGB)","loc":"d,90:145,90:150","isFatal":false} + {"type":"STOP","name":"","addr":"(OGB)","loc":"d,92:145,92:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(PGB)","loc":"d,91:13,91:15", + {"type":"IF","name":"","addr":"(PGB)","loc":"d,93:13,93:15", "condp": [ - {"type":"NEQ","name":"","addr":"(QGB)","loc":"d,91:29,91:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(QGB)","loc":"d,93:29,93:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(RGB)","loc":"d,91:34,91:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(RGB)","loc":"d,93:34,93:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SGB)","loc":"d,91:18,91:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(SGB)","loc":"d,93:18,93:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(TGB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UGB)","loc":"d,91:18,91:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UGB)","loc":"d,93:18,93:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VGB)","loc":"d,91:18,91:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VGB)","loc":"d,93:18,93:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(WGB)","loc":"d,91:18,91:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(WGB)","loc":"d,93:18,93:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(XGB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(YGB)","loc":"d,91:18,91:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(YGB)","loc":"d,93:18,93:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(ZGB)","loc":"d,91:18,91:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(ZGB)","loc":"d,93:18,93:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(AHB)","loc":"d,91:18,91:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(AHB)","loc":"d,93:18,93:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(BHB)","loc":"d,91:18,91:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(BHB)","loc":"d,93:18,93:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2267,33 +2267,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(CHB)","loc":"d,91:46,91:52", + {"type":"DISPLAY","name":"","addr":"(CHB)","loc":"d,93:46,93:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:91: got='h%x exp='h3\\n","addr":"(DHB)","loc":"d,91:46,91:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:93: got='h%x exp='h3\\n","addr":"(DHB)","loc":"d,93:46,93:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(EHB)","loc":"d,91:125,91:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(EHB)","loc":"d,93:125,93:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(FHB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(GHB)","loc":"d,91:125,91:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(GHB)","loc":"d,93:125,93:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(HHB)","loc":"d,91:125,91:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(HHB)","loc":"d,93:125,93:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(IHB)","loc":"d,91:125,91:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(IHB)","loc":"d,93:125,93:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_next1","addr":"(JHB)","loc":"d,17:12,17:16","dtypep":"(IC)","access":"RD","varp":"(JC)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(KHB)","loc":"d,91:125,91:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(KHB)","loc":"d,93:125,93:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(LHB)","loc":"d,91:125,91:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(LHB)","loc":"d,93:125,93:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(MHB)","loc":"d,91:125,91:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(MHB)","loc":"d,93:125,93:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(NHB)","loc":"d,91:125,91:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(NHB)","loc":"d,93:125,93:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2301,138 +2301,138 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(OHB)","loc":"d,91:145,91:150","isFatal":false} + {"type":"STOP","name":"","addr":"(OHB)","loc":"d,93:145,93:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(PHB)","loc":"d,92:13,92:15", + {"type":"IF","name":"","addr":"(PHB)","loc":"d,94:13,94:15", "condp": [ - {"type":"NEQ","name":"","addr":"(QHB)","loc":"d,92:26,92:29","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(QHB)","loc":"d,94:26,94:29","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(RHB)","loc":"d,92:31,92:34","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(RHB)","loc":"d,94:31,94:34","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SHB)","loc":"d,92:18,92:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(SHB)","loc":"d,94:18,94:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(THB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UHB)","loc":"d,92:18,92:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UHB)","loc":"d,94:18,94:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VHB)","loc":"d,92:18,92:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VHB)","loc":"d,94:18,94:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(WHB)","loc":"d,92:18,92:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(WHB)","loc":"d,94:18,94:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(XHB)","loc":"d,92:18,92:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(XHB)","loc":"d,94:18,94:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(YHB)","loc":"d,92:43,92:49", + {"type":"DISPLAY","name":"","addr":"(YHB)","loc":"d,94:43,94:49", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:92: got='h%x exp='h3\\n","addr":"(ZHB)","loc":"d,92:43,92:49","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:94: got='h%x exp='h3\\n","addr":"(ZHB)","loc":"d,94:43,94:49","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(AIB)","loc":"d,92:122,92:123","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(AIB)","loc":"d,94:122,94:123","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(BIB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(CIB)","loc":"d,92:122,92:123","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(CIB)","loc":"d,94:122,94:123","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(DIB)","loc":"d,92:122,92:123","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(DIB)","loc":"d,94:122,94:123","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(EIB)","loc":"d,92:122,92:123","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(EIB)","loc":"d,94:122,94:123","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(FIB)","loc":"d,92:122,92:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(FIB)","loc":"d,94:122,94:123","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(GIB)","loc":"d,92:139,92:144","isFatal":false} + {"type":"STOP","name":"","addr":"(GIB)","loc":"d,94:139,94:144","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(HIB)","loc":"d,93:13,93:15", + {"type":"IF","name":"","addr":"(HIB)","loc":"d,95:13,95:15", "condp": [ - {"type":"NEQ","name":"","addr":"(IIB)","loc":"d,93:29,93:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(IIB)","loc":"d,95:29,95:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h3","addr":"(JIB)","loc":"d,93:34,93:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h3","addr":"(JIB)","loc":"d,95:34,95:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(KIB)","loc":"d,93:18,93:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(KIB)","loc":"d,95:18,95:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(LIB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(MIB)","loc":"d,93:18,93:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(MIB)","loc":"d,95:18,95:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(NIB)","loc":"d,93:18,93:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(NIB)","loc":"d,95:18,95:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(OIB)","loc":"d,93:18,93:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(OIB)","loc":"d,95:18,95:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(PIB)","loc":"d,93:18,93:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(PIB)","loc":"d,95:18,95:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(QIB)","loc":"d,93:46,93:52", + {"type":"DISPLAY","name":"","addr":"(QIB)","loc":"d,95:46,95:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:93: got='h%x exp='h3\\n","addr":"(RIB)","loc":"d,93:46,93:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:95: got='h%x exp='h3\\n","addr":"(RIB)","loc":"d,95:46,95:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SIB)","loc":"d,93:125,93:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(SIB)","loc":"d,95:125,95:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(TIB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UIB)","loc":"d,93:125,93:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UIB)","loc":"d,95:125,95:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VIB)","loc":"d,93:125,93:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VIB)","loc":"d,95:125,95:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(WIB)","loc":"d,93:125,93:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(WIB)","loc":"d,95:125,95:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(XIB)","loc":"d,93:125,93:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(XIB)","loc":"d,95:125,95:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(YIB)","loc":"d,93:145,93:150","isFatal":false} + {"type":"STOP","name":"","addr":"(YIB)","loc":"d,95:145,95:150","isFatal":false} ],"elsesp": []}, - {"type":"IF","name":"","addr":"(ZIB)","loc":"d,94:13,94:15", + {"type":"IF","name":"","addr":"(ZIB)","loc":"d,96:13,96:15", "condp": [ - {"type":"NEQ","name":"","addr":"(AJB)","loc":"d,94:29,94:32","dtypep":"(FB)", + {"type":"NEQ","name":"","addr":"(AJB)","loc":"d,96:29,96:32","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"4'h1","addr":"(BJB)","loc":"d,94:34,94:37","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(BJB)","loc":"d,96:34,96:37","dtypep":"(AC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(CJB)","loc":"d,94:18,94:19","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(CJB)","loc":"d,96:18,96:19","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(DJB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(EJB)","loc":"d,94:18,94:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(EJB)","loc":"d,96:18,96:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(FJB)","loc":"d,94:18,94:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(FJB)","loc":"d,96:18,96:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(GJB)","loc":"d,94:18,94:19","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(GJB)","loc":"d,96:18,96:19","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(HJB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(IJB)","loc":"d,94:18,94:19","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(IJB)","loc":"d,96:18,96:19","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(JJB)","loc":"d,94:18,94:19","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(JJB)","loc":"d,96:18,96:19","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(KJB)","loc":"d,94:18,94:19","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(KJB)","loc":"d,96:18,96:19","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(LJB)","loc":"d,94:18,94:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(LJB)","loc":"d,96:18,96:19","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2441,33 +2441,33 @@ ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(MJB)","loc":"d,94:46,94:52", + {"type":"DISPLAY","name":"","addr":"(MJB)","loc":"d,96:46,96:52", "fmtp": [ - {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:94: got='h%x exp='h1\\n","addr":"(NJB)","loc":"d,94:46,94:52","dtypep":"(RB)", + {"type":"SFORMATF","name":"%%Error: t/t_enum_type_methods.v:96: got='h%x exp='h1\\n","addr":"(NJB)","loc":"d,96:46,96:52","dtypep":"(RB)", "exprsp": [ - {"type":"ARRAYSEL","name":"","addr":"(OJB)","loc":"d,94:125,94:126","dtypep":"(AC)", + {"type":"ARRAYSEL","name":"","addr":"(OJB)","loc":"d,96:125,96:126","dtypep":"(AC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(PJB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(QJB)","loc":"d,94:125,94:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(QJB)","loc":"d,96:125,96:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(RJB)","loc":"d,94:125,94:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(RJB)","loc":"d,96:125,96:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"ARRAYSEL","name":"","addr":"(SJB)","loc":"d,94:125,94:126","dtypep":"(LC)", + {"type":"ARRAYSEL","name":"","addr":"(SJB)","loc":"d,96:125,96:126","dtypep":"(LC)", "fromp": [ {"type":"VARREF","name":"__Venumtab_enum_prev1","addr":"(TJB)","loc":"d,17:12,17:16","dtypep":"(CJ)","access":"RD","varp":"(DJ)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "bitp": [ - {"type":"AND","name":"","addr":"(UJB)","loc":"d,94:125,94:126","dtypep":"(LC)", + {"type":"AND","name":"","addr":"(UJB)","loc":"d,96:125,96:126","dtypep":"(LC)", "lhsp": [ - {"type":"CONST","name":"32'h7","addr":"(VJB)","loc":"d,94:125,94:126","dtypep":"(NC)"} + {"type":"CONST","name":"32'h7","addr":"(VJB)","loc":"d,96:125,96:126","dtypep":"(NC)"} ], "rhsp": [ - {"type":"CCAST","name":"","addr":"(WJB)","loc":"d,94:125,94:126","dtypep":"(LC)","size":32, + {"type":"CCAST","name":"","addr":"(WJB)","loc":"d,96:125,96:126","dtypep":"(LC)","size":32, "lhsp": [ - {"type":"VARREF","name":"t.e","addr":"(XJB)","loc":"d,94:125,94:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.e","addr":"(XJB)","loc":"d,96:125,96:126","dtypep":"(LC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ]} ]} @@ -2475,33 +2475,33 @@ ]} ],"scopeNamep": []} ],"filep": []}, - {"type":"STOP","name":"","addr":"(YJB)","loc":"d,94:145,94:150","isFatal":false} + {"type":"STOP","name":"","addr":"(YJB)","loc":"d,96:145,96:150","isFatal":false} ],"elsesp": []}, - {"type":"ASSIGNDLY","name":"","addr":"(ZJB)","loc":"d,95:12,95:14","dtypep":"(AC)", + {"type":"ASSIGNDLY","name":"","addr":"(ZJB)","loc":"d,97:12,97:14","dtypep":"(AC)", "rhsp": [ - {"type":"CONST","name":"4'h1","addr":"(AKB)","loc":"d,95:15,95:18","dtypep":"(AC)"} + {"type":"CONST","name":"4'h1","addr":"(AKB)","loc":"d,97:15,97:18","dtypep":"(AC)"} ], "lhsp": [ - {"type":"VARREF","name":"__Vdly__t.e","addr":"(BKB)","loc":"d,95:10,95:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"__Vdly__t.e","addr":"(BKB)","loc":"d,97:10,97:11","dtypep":"(AC)","access":"WR","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ], "elsesp": [ - {"type":"IF","name":"","addr":"(CKB)","loc":"d,97:12,97:14", + {"type":"IF","name":"","addr":"(CKB)","loc":"d,99:12,99:14", "condp": [ - {"type":"EQ","name":"","addr":"(DKB)","loc":"d,97:19,97:21","dtypep":"(FB)", + {"type":"EQ","name":"","addr":"(DKB)","loc":"d,99:19,99:21","dtypep":"(FB)", "lhsp": [ - {"type":"CONST","name":"32'sh63","addr":"(EKB)","loc":"d,97:21,97:23","dtypep":"(KB)"} + {"type":"CONST","name":"32'sh63","addr":"(EKB)","loc":"d,99:21,99:23","dtypep":"(KB)"} ], "rhsp": [ - {"type":"VARREF","name":"t.cyc","addr":"(FKB)","loc":"d,97:16,97:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cyc","addr":"(FKB)","loc":"d,99:16,99:19","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ]} ], "thensp": [ - {"type":"DISPLAY","name":"","addr":"(GKB)","loc":"d,98:10,98:16", + {"type":"DISPLAY","name":"","addr":"(GKB)","loc":"d,100:10,100:16", "fmtp": [ - {"type":"SFORMATF","name":"*-* All Finished *-*\\n","addr":"(HKB)","loc":"d,98:10,98:16","dtypep":"(RB)","exprsp": [],"scopeNamep": []} + {"type":"SFORMATF","name":"*-* All Finished *-*\\n","addr":"(HKB)","loc":"d,100:10,100:16","dtypep":"(RB)","exprsp": [],"scopeNamep": []} ],"filep": []}, - {"type":"FINISH","name":"","addr":"(IKB)","loc":"d,99:10,99:17"} + {"type":"FINISH","name":"","addr":"(IKB)","loc":"d,101:10,101:17"} ],"elsesp": []} ]} ]} @@ -2980,7 +2980,7 @@ {"type":"BASICDTYPE","name":"logic","addr":"(GQ)","loc":"d,11:8,11:9","dtypep":"(GQ)","keyword":"logic","range":"63:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"bit","addr":"(P)","loc":"d,11:8,11:9","dtypep":"(P)","keyword":"bit","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"bit","addr":"(T)","loc":"d,11:8,11:9","dtypep":"(T)","keyword":"bit","range":"31:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(FB)","loc":"d,61:22,61:25","dtypep":"(FB)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(FB)","loc":"d,63:22,63:25","dtypep":"(FB)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(AC)","loc":"d,32:11,32:14","dtypep":"(AC)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(LC)","loc":"d,38:15,38:16","dtypep":"(LC)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(XOB)","loc":"d,15:10,15:13","dtypep":"(XOB)","keyword":"logic","range":"7:0","generic":true,"rangep": []} diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index 85640c38d..ca34646a2 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -36,9 +36,9 @@ - - - + + + @@ -289,8 +289,8 @@ - - + + @@ -314,22 +314,22 @@ - - - + + + - - - + + + - - - + + + - - - - + + + + @@ -339,7 +339,7 @@ - + @@ -602,21 +602,21 @@ - - - + + + - - - + + + - - - + + + @@ -708,9 +708,9 @@ - - - + + + @@ -719,102 +719,38 @@ - - - - + + + + - + - + - - - - + + + + - - - + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -822,71 +758,63 @@ - + - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - - - - - - - - + + + - - - + + + - - - - - - - - - - - + + + + + - + - - + + - + @@ -896,32 +824,38 @@ - - - - - - - - + + + + + + + + - + - + - - - + + + + + + + + + @@ -929,12 +863,18 @@ - + - - - + + + + + + + + + @@ -943,18 +883,78 @@ - - + + - + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -962,18 +962,18 @@ - - - + + + - - - + + + - - - - + + + + @@ -981,90 +981,26 @@ - + - - - + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -1072,71 +1008,63 @@ - + - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - - - - - - - - + + + - - - + + + - - - - - - - - - - - + + + + + - + - - + + - + @@ -1146,32 +1074,38 @@ - - - - - - - - + + + + + + + + - + - + - - - + + + + + + + + + @@ -1179,12 +1113,18 @@ - + - - - + + + + + + + + + @@ -1193,18 +1133,78 @@ - - + + - + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1212,18 +1212,18 @@ - - - + + + - - - + + + - - - - + + + + @@ -1231,90 +1231,26 @@ - + - - - + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -1322,71 +1258,63 @@ - + - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - - - - - - - - + + + - - - + + + - - - - - - - - - - - + + + + + - + - - + + - + @@ -1396,32 +1324,38 @@ - - - - - - - - + + + + + + + + - + - + - - - + + + + + + + + + @@ -1429,12 +1363,18 @@ - + - - - + + + + + + + + + @@ -1443,18 +1383,78 @@ - - + + - + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1462,18 +1462,18 @@ - - - + + + - - - + + + - - - - + + + + @@ -1481,25 +1481,25 @@ - + - - - + + + - - - - + + + + - - + + - + @@ -1832,7 +1832,7 @@ - + From b14539569ffff705e4f1bd783fe060ebef8e0b24 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Aug 2025 13:20:52 -0400 Subject: [PATCH 179/252] Internals: Check and enforce member brace initialization. No functional change intended --- include/verilatedos.h | 2 +- src/V3AstNodeExpr.h | 28 ++++++++++++++-------------- src/V3OptionParser.cpp | 4 ++-- src/V3Param.cpp | 4 ++-- src/V3Sched.h | 4 ++-- test_regress/t/TestVpi.h | 8 ++++---- test_regress/t/t_dist_cppstyle.py | 18 +++++++++++++++--- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/include/verilatedos.h b/include/verilatedos.h index 7894a4510..b24b7a58c 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -693,7 +693,7 @@ struct reverse_wrapper final { const T& m_v; explicit reverse_wrapper(const T& a_v) - : m_v(a_v) {} + : m_v(a_v) {} // Need () constructor auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); } auto end() -> decltype(m_v.rend()) { return m_v.rend(); } }; diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 0a1d5cac1..ea74e05f6 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -940,26 +940,26 @@ class AstConst final : public AstNodeExpr { public: AstConst(FileLine* fl, const V3Number& num) : ASTGEN_SUPER_Const(fl) - , m_num(num) { + , m_num{num} { initWithNumber(); } class WidthedValue {}; // for creator type-overload selection AstConst(FileLine* fl, WidthedValue, int width, uint32_t value) : ASTGEN_SUPER_Const(fl) - , m_num(this, width, value) { + , m_num(this, width, value) { // Need () constructor initWithNumber(); } class DTyped {}; // for creator type-overload selection // Zero/empty constant with a type matching nodetypep AstConst(FileLine* fl, DTyped, const AstNodeDType* nodedtypep) : ASTGEN_SUPER_Const(fl) - , m_num(this, nodedtypep) { + , m_num{this, nodedtypep} { initWithNumber(); } class StringToParse {}; // for creator type-overload selection AstConst(FileLine* fl, StringToParse, const char* sourcep) : ASTGEN_SUPER_Const(fl) - , m_num(this, sourcep) { + , m_num{this, sourcep} { initWithNumber(); } class VerilogStringLiteral {}; // for creator type-overload selection @@ -970,41 +970,41 @@ public: } AstConst(FileLine* fl, uint32_t num) : ASTGEN_SUPER_Const(fl) - , m_num(this, 32, num) { + , m_num(this, 32, num) { // Need () constructor dtypeSetLogicUnsized(m_num.width(), 0, VSigning::UNSIGNED); } class Unsized32 {}; // for creator type-overload selection AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value : ASTGEN_SUPER_Const(fl) - , m_num(this, 32, num) { + , m_num(this, 32, num) { // Need () constructor m_num.width(32, false); 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(this, 32, num) { // Need () constructor m_num.width(32, true); dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::SIGNED); } class Unsized64 {}; // for creator type-overload selection AstConst(FileLine* fl, Unsized64, uint64_t num) : ASTGEN_SUPER_Const(fl) - , m_num(this, 64, 0) { + , m_num(this, 64, 0) { // Need () constructor m_num.setQuad(num); dtypeSetLogicSized(64, VSigning::UNSIGNED); } class SizedEData {}; // for creator type-overload selection AstConst(FileLine* fl, SizedEData, uint64_t num) : ASTGEN_SUPER_Const(fl) - , m_num(this, VL_EDATASIZE, 0) { + , m_num(this, VL_EDATASIZE, 0) { // Need () constructor m_num.setQuad(num); dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED); } class RealDouble {}; // for creator type-overload selection AstConst(FileLine* fl, RealDouble, double num) : ASTGEN_SUPER_Const(fl) - , m_num(this, 64) { + , m_num(this, 64) { // Need () constructor m_num.setDouble(num); dtypeSetDouble(); } @@ -1017,27 +1017,27 @@ public: class BitFalse {}; AstConst(FileLine* fl, BitFalse) // Shorthand const 0, dtype should be a logic of size 1 : ASTGEN_SUPER_Const(fl) - , m_num(this, 1, 0) { + , m_num(this, 1, 0) { // Need () constructor dtypeSetBit(); } // Shorthand const 1 (or with argument 0/1), dtype should be a logic of size 1 class BitTrue {}; AstConst(FileLine* fl, BitTrue, bool on = true) : ASTGEN_SUPER_Const(fl) - , m_num(this, 1, on) { + , m_num(this, 1, on) { // Need () constructor dtypeSetBit(); } class All0 {}; AstConst(FileLine* fl, All0) : ASTGEN_SUPER_Const(fl) - , m_num(this, "'0") { + , m_num(this, "'0") { // Need () constructor initWithNumber(); fl->warnOff(V3ErrorCode::NEWERSTD, true); } class All1 {}; AstConst(FileLine* fl, All1) : ASTGEN_SUPER_Const(fl) - , m_num(this, "'1") { + , m_num(this, "'1") { // Need () constructor initWithNumber(); fl->warnOff(V3ErrorCode::NEWERSTD, true); } diff --git a/src/V3OptionParser.cpp b/src/V3OptionParser.cpp index 701c0219e..c9cc59736 100644 --- a/src/V3OptionParser.cpp +++ b/src/V3OptionParser.cpp @@ -126,7 +126,7 @@ class V3OptionParser::Impl::ActionCbVal final : public ActionBase; explicit ActionCbVal(CbType cb) - : m_cb(std::move(cb)) {} + : m_cb{std::move(cb)} {} void exec(const char* optp, const char* argp) override { m_cb(std::atoi(argp)); } }; @@ -137,7 +137,7 @@ class V3OptionParser::Impl::ActionCbVal final : public ActionBase; explicit ActionCbVal(CbType cb) - : m_cb(std::move(cb)) {} + : m_cb{std::move(cb)} {} void exec(const char* optp, const char* argp) override { m_cb(argp); } }; diff --git a/src/V3Param.cpp b/src/V3Param.cpp index fa43d5ab4..34412bad3 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -92,9 +92,9 @@ class ParameterizedHierBlocks final { public: ParameterizedHierBlocks(const V3HierBlockOptSet& hierOpts, AstNetlist* nodep) - : m_hierSubRun((!v3Global.opt.hierBlocks().empty() || v3Global.opt.hierChild()) + : m_hierSubRun{(!v3Global.opt.hierBlocks().empty() || v3Global.opt.hierChild()) // Exclude consolidation - && !v3Global.opt.hierParamFile().empty()) { + && !v3Global.opt.hierParamFile().empty()} { for (const auto& hierOpt : hierOpts) { m_hierBlockOptsByOrigName.emplace(hierOpt.second.origName(), &hierOpt.second); const V3HierarchicalBlockOption::ParamStrMap& params = hierOpt.second.params(); diff --git a/src/V3Sched.h b/src/V3Sched.h index 03ff99a18..c1f8e58df 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -159,8 +159,8 @@ class VirtIfaceTriggers final { const AstVar* m_memberVarp; // pointer to member field IfaceMember(const AstIface* ifacep, const AstVar* memberVarp) - : m_ifacep(ifacep) - , m_memberVarp(memberVarp) {} + : m_ifacep{ifacep} + , m_memberVarp{memberVarp} {} bool operator<(const IfaceMember& other) const { if (m_ifacep != other.m_ifacep) return m_ifacep < other.m_ifacep; diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index d2428ba4a..98a4c548a 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -25,11 +25,11 @@ class TestVpiHandle { public: TestVpiHandle() - : m_handle(NULL) - , m_freeit(true) {} + : m_handle(NULL) // Need (), not C++11 + , m_freeit(true) {} // Need (), not C++11 TestVpiHandle(vpiHandle h) - : m_handle(h) - , m_freeit(true) {} + : m_handle(h) // Need (), not C++11 + , m_freeit(true) {} // Need (), not C++11 ~TestVpiHandle() { release(); } operator vpiHandle() const { return m_handle; } TestVpiHandle& operator=(vpiHandle h) { diff --git a/test_regress/t/t_dist_cppstyle.py b/test_regress/t/t_dist_cppstyle.py index a21c25eb6..f818ac904 100755 --- a/test_regress/t/t_dist_cppstyle.py +++ b/test_regress/t/t_dist_cppstyle.py @@ -29,9 +29,12 @@ def get_source_files(root): def check_pattern(filename, contents, pattern, not_pattern, message): # Pattern uses match, so must include skipping leading whitespace if necessary lineno = 0 + buffer_lineno = 0 buffer = "\n" for line in contents.splitlines(): lineno += 1 + if buffer == "\n": + buffer_lineno = lineno if line != "": # Don't do whole file at once - see issue #4085 # Build a buffer until a newline so we check a block at a time. @@ -40,7 +43,8 @@ def check_pattern(filename, contents, pattern, not_pattern, message): m = re.search(r"\n" + pattern, buffer) if m: if not not_pattern or not re.search(not_pattern, buffer): - test.error_keep_going(filename + ":" + str(lineno) + ": " + message + m.group(0)) + test.error_keep_going(filename + ":" + str(buffer_lineno) + ": " + message + + m.group(0)) buffer = "\n" @@ -77,8 +81,16 @@ for filename in sorted(files.keys()): contents, r'.*[( ]new [a-zA-Z0-9]+\([^\n]*', # Ignore common ok narrowing conversions, on int vs uint32_t arguments - r'(Need \(\) constructor|new AstArraySel|new AstConst|new AstRange)', - "Use brace instead of parenthesis-style constructors e.g. 'new ...{...}'") + r'(Need \(\)|new AstArraySel|new AstConst|new AstRange)', + "Use brace instead of parenthesis-style new constructors e.g. 'new ...{...}'") + + check_pattern( + filename, + contents, + r'.*(\n *:|,\n) +m_[a-zA-Z0-9]+\(', + # Ignore common m_e enum constructors + r'.*(Need \(\)|: m_e\()|V3OPTION_PARSER_DEF', + "Use brace instead of parenthesis-style constructors e.g. ': m_...{...}'") if re.search(r'\.(c|cpp)', filename): check_pattern(filename, contents, r'(\w+\s+)*(inline)[^\n]*', None, From f6edf26eb2c2cd46fe74fb841c0e5ac004876681 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 17 Aug 2025 19:35:40 +0100 Subject: [PATCH 180/252] Fix hierarchical NBAs (#6286) (#6300) NBAs targeting a variable in a different scope are now allocated temporary variables for captured values in the scope of the NBA, not the scope of the target variable. Fixes #6286 --- src/V3Delayed.cpp | 46 +++++++++++---- test_regress/t/t_nba_hier.py | 24 ++++++++ test_regress/t/t_nba_hier.v | 107 +++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 10 deletions(-) create mode 100755 test_regress/t/t_nba_hier.py create mode 100644 test_regress/t/t_nba_hier.v diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 0ba1758d9..1e7a07c53 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -245,7 +245,9 @@ class DelayedVisitor final : public VNVisitor { // AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning // AstVarRef::user1() -> bool. Set true if target of NBA // AstAssignDly::user1() -> bool. Set true if already visited + // AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under // AstNodeModule::user1p() -> std::unorded_map temp map via m_varMap + // AstScope::user1() -> int: Temporary counter for this scope // AstVarScope::user1p() -> VarScopeInfo via m_vscpInfo // AstVarScope::user2p() -> AstVarRef*: First write reference to the Variable // AstVarScope::user3p() -> std::vector via m_writeRefs; @@ -263,6 +265,7 @@ class DelayedVisitor final : public VNVisitor { AstActive* m_activep = nullptr; // Current activate const AstCFunc* m_cfuncp = nullptr; // Current public C Function AstNodeProcedure* m_procp = nullptr; // Current process + AstScope* m_scopep = nullptr; // Current scope bool m_inLoop = false; // True in for loops bool m_inSuspendableOrFork = false; // True in suspendable processes and forks bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK @@ -466,6 +469,7 @@ class DelayedVisitor final : public VNVisitor { varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep}; modp->addStmtsp(varp); } + UASSERT_OBJ(varp->dtypep()->isSame(dtypep), flp, "Invalid type for temporary AstVar"); // Create the AstVarScope AstVarScope* const varscp = new AstVarScope{flp, scopep, varp}; scopep->addVarsp(varscp); @@ -531,6 +535,21 @@ class DelayedVisitor final : public VNVisitor { return lhsp; } + // Create a unique temporary variable name + std::string uniqueTmpName(AstScope* scopep, AstVarScope* vscp, VarScopeInfo& vscpInfo) { + std::stringstream ss; + ss << "__" << vscp->varp()->shortName() + "__v"; + // If the assignment is in the same scope as the variable, just + // use the temporary counter of the variable. + if (scopep == vscp->scopep()) { + ss << vscpInfo.m_nTmp++; + } else { + // Otherwise use the temporary counter of the scope of the assignment. + ss << scopep->user1Inc() << "_hierarchical"; + } + return ss.str(); + } + // Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep' // type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero. // Insert new statements before 'insertp'. @@ -671,11 +690,10 @@ class DelayedVisitor final : public VNVisitor { void convertSchemeFlagShared(AstAssignDly* nodep, AstVarScope* vscp, VarScopeInfo& vscpInfo) { UASSERT_OBJ(vscpInfo.m_scheme == Scheme::FlagShared, vscp, "Inconsistent NBA scheme"); FileLine* const flp = vscp->fileline(); - AstScope* const scopep = vscp->scopep(); + AstScope* const scopep = VN_AS(nodep->user2p(), Scope); // Base name suffix for signals constructed below - const std::string baseName{"__" + vscp->varp()->shortName() + "__v" - + std::to_string(vscpInfo.m_nTmp++)}; + const std::string baseName = uniqueTmpName(scopep, vscp, vscpInfo); // Unlink and capture the RHS value AstNodeExpr* const capturedRhsp @@ -697,6 +715,8 @@ class DelayedVisitor final : public VNVisitor { consecutive // ... that use the same scheme && prevVscpInfop->m_scheme == Scheme::FlagShared + // ... and are in the same scope as the target variable + && scopep == vscp->scopep() // ... and share the same overall update domain && prevVscpInfop->senTreep()->sameTree(vscpInfo.senTreep()); @@ -763,11 +783,10 @@ class DelayedVisitor final : public VNVisitor { void convertSchemeFlagUnique(AstAssignDly* nodep, AstVarScope* vscp, VarScopeInfo& vscpInfo) { UASSERT_OBJ(vscpInfo.m_scheme == Scheme::FlagUnique, vscp, "Inconsistent NBA scheme"); FileLine* const flp = vscp->fileline(); - AstScope* const scopep = vscp->scopep(); + AstScope* const scopep = VN_AS(nodep->user2p(), Scope); // Base name suffix for signals constructed below - const std::string baseName{"__" + vscp->varp()->shortName() + "__v" - + std::to_string(vscpInfo.m_nTmp++)}; + const std::string baseName = uniqueTmpName(scopep, vscp, vscpInfo); // Unlink and capture the RHS value AstNodeExpr* const capturedRhsp @@ -837,11 +856,10 @@ class DelayedVisitor final : public VNVisitor { : vscpInfo.m_scheme == Scheme::ValueQueueWhole, vscp, "Inconsistent NBA scheme"); FileLine* const flp = vscp->fileline(); - AstScope* const scopep = vscp->scopep(); + AstScope* const scopep = VN_AS(nodep->user2p(), Scope); // Base name suffix for signals constructed below - const std::string baseName{"__" + vscp->varp()->shortName() + "__v" - + std::to_string(vscpInfo.m_nTmp++)}; + const std::string baseName = uniqueTmpName(scopep, vscp, vscpInfo); // Unlink and capture the RHS value AstNodeExpr* const capturedRhsp @@ -1051,7 +1069,11 @@ class DelayedVisitor final : public VNVisitor { } } } - void visit(AstScope* nodep) override { iterateChildren(nodep); } + void visit(AstScope* nodep) override { + VL_RESTORER(m_scopep); + m_scopep = nodep; + iterateChildren(nodep); + } void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); m_cfuncp = nodep; @@ -1169,9 +1191,13 @@ class DelayedVisitor final : public VNVisitor { } UASSERT_OBJ(m_procp, nodep, "Delayed assignment not under process"); UASSERT_OBJ(m_activep, nodep, "<= not under sensitivity block"); + UASSERT_OBJ(m_scopep, nodep, "<= not under scope"); UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep, "<= assignment in non-clocked block, should have been converted in V3Active"); + // Record scope of this NBA + nodep->user2p(m_scopep); + // Grab the reference to the target of the NBA, also lift ExprStmt statements on the LHS VL_RESTORER(m_currNbaLhsRefp); UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "NBAs should not nest"); diff --git a/test_regress/t/t_nba_hier.py b/test_regress/t/t_nba_hier.py new file mode 100755 index 000000000..5714e793e --- /dev/null +++ b/test_regress/t/t_nba_hier.py @@ -0,0 +1,24 @@ +#!/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', '--stats', '-fno-inline', '--unroll-count', '0']) + +test.file_grep(test.stats, r'NBA, variables using ShadowVar scheme\s+(\d+)', 2) +test.file_grep(test.stats, r'NBA, variables using ShadowVarMasked scheme\s+(\d+)', 2) +test.file_grep(test.stats, r'NBA, variables using FlagShared scheme\s+(\d+)', 2) +test.file_grep(test.stats, r'NBA, variables using FlagUnique scheme\s+(\d+)', 2) +test.file_grep(test.stats, r'NBA, variables using ValueQueuePartial scheme\s+(\d+)', 2) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_nba_hier.v b/test_regress/t/t_nba_hier.v new file mode 100644 index 000000000..40db25713 --- /dev/null +++ b/test_regress/t/t_nba_hier.v @@ -0,0 +1,107 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); + +module t; + + logic clk = 1'b0; + always #5 clk = ~clk; + + logic [7:0] x; + + sub a_0(); + sub a_1(); + always @(posedge clk) begin + a_0.x[3:0] <= ~x[3:0]; + a_1.x[7:0] <= ~x; + end + + sub b_0(); + sub b_1(); + always begin + // Having this @posedge here makes this a 'suspendable' process, causing + // the use of the FlagUnique scheme + @(posedge clk); + b_0.x[3:0] <= ~x[3:0]; + b_1.x[7:0] <= ~x; + end + + sub c_0(); + sub c_1(); + always @(posedge clk) begin + c_0.x[3:0] <= ~x[3:0]; + c_1.x[7:0] <= ~x; + end + assign c_0.x[9:8] = 2'd1; + assign c_1.x[9:8] = 2'd2; + + sub d_0(); + sub d_1(); + always @(posedge clk) begin + d_0.y[0][3:0] <= ~x[3:0]; + d_1.y[0][7:0] <= ~x; + end + + sub e_0(); + sub e_1(); + always @(posedge clk) begin + for (int i = 0; i < 2; ++i) begin + e_0.y[i][3:0] <= ~x[3:0]; + e_1.y[i][7:0] <= ~x; + end + end + + initial begin + #1; + x = 8'hcc; + @(posedge clk); + @(negedge clk); + `checkh(a_0.x[3:0], 4'h3); + `checkh(a_1.x[7:0], 8'h33); + `checkh(b_0.x[3:0], 4'h3); + `checkh(b_1.x[7:0], 8'h33); + `checkh(c_0.x[3:0], 4'h3); + `checkh(c_0.x[9:8], 2'h1); + `checkh(c_1.x[7:0], 8'h33); + `checkh(c_1.x[9:8], 2'h2); + `checkh(d_0.y[0][3:0], 4'h3); + `checkh(d_1.y[0][7:0], 8'h33); + for (int i = 0; i < 2; ++i) begin + `checkh(e_0.y[i][3:0], 4'h3); + `checkh(e_1.y[i][7:0], 8'h33); + end + + #1; + x = 8'h55; + @(posedge clk); + @(negedge clk); + `checkh(a_0.x[3:0], 4'ha); + `checkh(a_1.x[7:0], 8'haa); + `checkh(b_0.x[3:0], 4'ha); + `checkh(b_1.x[7:0], 8'haa); + `checkh(c_0.x[3:0], 4'ha); + `checkh(c_0.x[9:8], 2'h1); + `checkh(c_1.x[7:0], 8'haa); + `checkh(c_1.x[9:8], 2'h2); + `checkh(d_0.y[0][3:0], 4'ha); + `checkh(d_1.y[0][7:0], 8'haa); + for (int i = 0; i < 2; ++i) begin + `checkh(e_0.y[i][3:0], 4'ha); + `checkh(e_1.y[i][7:0], 8'haa); + end + + #1; + $finish; + end + +endmodule + +module sub; + logic [9:0] x; + logic [9:0] y [99]; +endmodule From eaecdf7477482691b201c7b08b4e3373a9a81ef3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Aug 2025 14:08:10 -0400 Subject: [PATCH 181/252] Tests: Improve check for member brace initialization. --- test_regress/t/t_dist_cppstyle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_regress/t/t_dist_cppstyle.py b/test_regress/t/t_dist_cppstyle.py index f818ac904..596bd8dd3 100755 --- a/test_regress/t/t_dist_cppstyle.py +++ b/test_regress/t/t_dist_cppstyle.py @@ -87,7 +87,7 @@ for filename in sorted(files.keys()): check_pattern( filename, contents, - r'.*(\n *:|,\n) +m_[a-zA-Z0-9]+\(', + r'.*(\n *[:,]|,\n) +m_[a-zA-Z0-9]+\(', # Ignore common m_e enum constructors r'.*(Need \(\)|: m_e\()|V3OPTION_PARSER_DEF', "Use brace instead of parenthesis-style constructors e.g. ': m_...{...}'") From dbdf23511578e4eca63604771d383a3f308ea465 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Aug 2025 14:53:59 -0400 Subject: [PATCH 182/252] Commentary: Changes update --- Changes | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes b/Changes index bbb198a4b..21e601503 100644 --- a/Changes +++ b/Changes @@ -74,6 +74,7 @@ Verilator 5.039 devel * Fix same variable on the RHS forced to two different LHSs. (#6269). [Artur Bieniek, Antmicro Ltd.] * Fix spurious VPI value change callbacks (#6274). [Todd Strader] * Fix stray ']' in Verilog code output for non-constant select (#6277). [Geza Lore] +* Fix hierarchical NBAs (#6286) (#6300). [Geza Lore] * Fix variables hiding package imports (#6289). [Johan Wouters] * Fix DFG circular driver tracing. [Geza Lore] * Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290). [Mateusz Gancarz] From 88046c806398ac89ded6c8a42e90f2bd66abf84f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Aug 2025 19:14:34 -0400 Subject: [PATCH 183/252] Internals: Rename AstSenTree pointers to sentreep. No functional change intended except JSON. --- src/V3Active.cpp | 54 ++++++++--------- src/V3ActiveTop.cpp | 26 ++++----- src/V3AssertPre.cpp | 4 +- src/V3AstInlines.h | 4 +- src/V3AstNodeExpr.h | 10 ++-- src/V3AstNodeOther.h | 32 +++++----- src/V3AstNodeStmt.h | 12 ++-- src/V3AstNodes.cpp | 8 +-- src/V3Clock.cpp | 12 ++-- src/V3Delayed.cpp | 20 +++---- src/V3DfgAstToDfg.cpp | 2 +- src/V3DfgDfgToAst.cpp | 2 +- src/V3DfgOptimizer.cpp | 2 +- src/V3EmitV.cpp | 18 +++--- src/V3Force.cpp | 4 +- src/V3Hasher.cpp | 4 +- src/V3LinkParse.cpp | 8 +-- src/V3OrderCFuncEmitter.h | 4 +- src/V3OrderGraphBuilder.cpp | 11 ++-- src/V3Sched.cpp | 8 +-- src/V3Sched.h | 2 +- src/V3SchedPartition.cpp | 8 +-- src/V3SchedReplicate.cpp | 2 +- src/V3SchedTiming.cpp | 28 ++++----- src/V3SplitVar.cpp | 8 +-- src/V3Timing.cpp | 61 ++++++++++---------- test_regress/t/t_dump_json.out | 8 +-- test_regress/t/t_json_only_first.out | 2 +- test_regress/t/t_json_only_flat.out | 2 +- test_regress/t/t_json_only_flat_vlvbound.out | 4 +- 30 files changed, 186 insertions(+), 184 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 34b87d2fc..dc2367144 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -240,7 +240,7 @@ public: // Make a new AstActive sensitive to the given sentree and return it AstActive* makeActive(FileLine* const fl, AstSenTree* const senTreep) { AstActive* const activep = new AstActive{fl, "", senTreep}; - activep->sensesStorep(activep->sensesp()); + activep->senTreeStorep(activep->sentreep()); addActive(activep); return activep; } @@ -261,17 +261,17 @@ public: } // Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep. - AstActive* getActive(FileLine* fl, AstSenTree* sensesp) { - UASSERT(sensesp, "Must be non-null"); + AstActive* getActive(FileLine* fl, AstSenTree* sentreep) { + UASSERT(sentreep, "Must be non-null"); - auto it = m_activeMap.find(*sensesp); + auto it = m_activeMap.find(*sentreep); // If found matching AstActive, return it if (it != m_activeMap.end()) return it->second; // No such AstActive yet, creat it, and add to map. - AstSenTree* const newsenp = sensesp->cloneTree(false); + AstSenTree* const newsenp = sentreep->cloneTree(false); AstActive* const activep = new AstActive{fl, "sequent", newsenp}; - activep->sensesStorep(activep->sensesp()); + activep->senTreeStorep(activep->sentreep()); addActive(activep); m_activeMap.emplace(*newsenp, activep); return activep; @@ -441,11 +441,11 @@ class ActiveVisitor final : public VNVisitor { wantactivep->addStmtsp(nodep); } - void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) { + void visitAlways(AstNode* nodep, AstSenTree* oldsentreep, VAlwaysKwd kwd) { // Move always to appropriate ACTIVE based on its sense list - if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) { + if (oldsentreep && oldsentreep->sensesp() && oldsentreep->sensesp()->isNever()) { // Never executing. Kill it. - UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep, + UASSERT_OBJ(!oldsentreep->sensesp()->nextp(), nodep, "Never senitem should be alone, else the never should be eliminated."); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; @@ -457,9 +457,9 @@ class ActiveVisitor final : public VNVisitor { // Walk sensitivity list m_clockedProcess = false; m_allChanged = true; - if (oldsensesp) { - oldsensesp->unlinkFrBack(); - iterateChildrenConst(oldsensesp); + if (oldsentreep) { + oldsentreep->unlinkFrBack(); + iterateChildrenConst(oldsentreep); } // If all SenItems are ET_CHANGE, then walk the body to determine if this process @@ -476,9 +476,9 @@ class ActiveVisitor final : public VNVisitor { AstActive* const wantactivep = !m_clockedProcess ? m_namer.getSpecialActive(nodep->fileline()) - : oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp) - // Clocked, no sensitivity lists, it's a suspendable, put it in initial - : m_namer.getSpecialActive(nodep->fileline()); + : oldsentreep ? m_namer.getActive(nodep->fileline(), oldsentreep) + // Clocked, no sensitivity lists, it's a suspendable, put it in initial + : m_namer.getSpecialActive(nodep->fileline()); // Move node to new active nodep->unlinkFrBack(); @@ -487,12 +487,12 @@ class ActiveVisitor final : public VNVisitor { // Warn and convert any delayed assignments { ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB - : oldsensesp ? ActiveDlyVisitor::CT_SEQ + : oldsentreep ? ActiveDlyVisitor::CT_SEQ : ActiveDlyVisitor::CT_SUSPENDABLE}; } // Delete sensitivity list - if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp); + if (oldsentreep) VL_DO_DANGLING(oldsentreep->deleteTree(), oldsentreep); // check combinational processes for latches if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) { @@ -534,7 +534,7 @@ class ActiveVisitor final : public VNVisitor { return; } visitSenItems(nodep); - visitAlways(nodep, nodep->sensesp(), nodep->keyword()); + visitAlways(nodep, nodep->sentreep(), nodep->keyword()); } void visit(AstAlwaysPostponed* nodep) override { // Might be empty with later optimizations, so this assertion can be removed, @@ -545,23 +545,23 @@ class ActiveVisitor final : public VNVisitor { activep->addStmtsp(nodep->unlinkFrBack()); } void visit(AstAlwaysObserved* nodep) override { - UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree"); - AstSenTree* const sensesp = nodep->sensesp(); - sensesp->unlinkFrBack(); + UASSERT_OBJ(nodep->sentreep(), nodep, "Should have a sentree"); + AstSenTree* const sentreep = nodep->sentreep(); + sentreep->unlinkFrBack(); // Make a new active for it, needs to be the only item under the active for V3Sched - AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp); + AstActive* const activep = m_namer.makeActive(nodep->fileline(), sentreep); activep->addStmtsp(nodep->unlinkFrBack()); } void visit(AstAlwaysReactive* nodep) override { - UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree"); - AstSenTree* const sensesp = nodep->sensesp(); - sensesp->unlinkFrBack(); + UASSERT_OBJ(nodep->sentreep(), nodep, "Should have a sentree"); + AstSenTree* const sentreep = nodep->sentreep(); + sentreep->unlinkFrBack(); // Make a new active for it, needs to be the only item under the active for V3Sched - AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp); + AstActive* const activep = m_namer.makeActive(nodep->fileline(), sentreep); activep->addStmtsp(nodep->unlinkFrBack()); } void visit(AstAlwaysPublic* nodep) override { - visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS); + visitAlways(nodep, nodep->sentreep(), VAlwaysKwd::ALWAYS); } void visit(AstCFunc* nodep) override { visitSenItems(nodep); } void visit(AstSenItem* nodep) override { diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index b432f0506..276f3c120 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -68,11 +68,11 @@ class ActiveTopVisitor final : public VNVisitor { UINFO(4, " ACTIVE " << nodep); // Remove duplicate clocks and such; sensesp() may change! V3Const::constifyExpensiveEdit(nodep); - AstSenTree* sensesp = nodep->sensesp(); - UASSERT_OBJ(sensesp, nodep, "nullptr"); - if (sensesp->sensesp() && sensesp->sensesp()->isNever()) { + AstSenTree* sentreep = nodep->sentreep(); + UASSERT_OBJ(sentreep, nodep, "nullptr"); + if (sentreep->sensesp() && sentreep->sensesp()->isNever()) { // Never executing. Kill it. - UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep, + UASSERT_OBJ(!sentreep->sensesp()->nextp(), nodep, "Never senitem should be alone, else the never should be eliminated."); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; @@ -94,21 +94,21 @@ class ActiveTopVisitor final : public VNVisitor { // Move the SENTREE for each active up to the global level. // This way we'll easily see what clock domains are identical - AstSenTree* const wantp = m_finder.getSenTree(sensesp); + AstSenTree* const wantp = m_finder.getSenTree(sentreep); UINFO(4, " lookdone"); - if (wantp != sensesp) { + if (wantp != sentreep) { // Move the active's contents to the other active - UINFO(4, " merge active " << sensesp << " into " << wantp); - if (nodep->sensesStorep()) { - UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep, + UINFO(4, " merge active " << sentreep << " into " << wantp); + if (nodep->senTreeStorep()) { + UASSERT_OBJ(sentreep == nodep->senTreeStorep(), nodep, "sensesStore should have been deleted earlier if different"); - sensesp->unlinkFrBack(); + sentreep->unlinkFrBack(); // There may be other references to same sense tree, // we'll be removing all references when we get to them, // but don't dangle our pointer yet! - VL_DO_DANGLING(pushDeletep(sensesp), sensesp); + VL_DO_DANGLING(pushDeletep(sentreep), sentreep); } - nodep->sensesp(wantp); + nodep->sentreep(wantp); } // If this is combinational logic that does not read any variables, then it really is an @@ -116,7 +116,7 @@ class ActiveTopVisitor final : public VNVisitor { // prune these otherwise. // TODO: we should warn for these if they were 'always @*' as some (including strictly // compliant) simulators will never execute these. - if (nodep->sensesp()->hasCombo()) { + if (nodep->sentreep()->hasCombo()) { FileLine* const flp = nodep->fileline(); AstActive* initialp = nullptr; for (AstNode *logicp = nodep->stmtsp(), *nextp; logicp; logicp = nextp) { diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 1a680d828..032c2855b 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -438,8 +438,8 @@ private: } } void visit(AstAlways* nodep) override { - iterateAndNextNull(nodep->sensesp()); - if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp(); + iterateAndNextNull(nodep->sentreep()); + if (nodep->sentreep()) m_seniAlwaysp = nodep->sentreep()->sensesp(); iterateAndNextNull(nodep->stmtsp()); m_seniAlwaysp = nullptr; } diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 3542839b1..3ec6c1917 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -148,8 +148,8 @@ bool AstBasicDType::ascending() const { return (rangep() ? rangep()->ascending() : m.m_nrange.ascending()); } -bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); } -bool AstActive::hasCombo() const { return m_sensesp->hasCombo(); } +bool AstActive::hasClocked() const { return m_sentreep->hasClocked(); } +bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); } AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* exprsp) : ASTGEN_SUPER_ElabDisplay(fl) { diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ea74e05f6..71a97d67a 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4945,17 +4945,17 @@ class AstCAwait final : public AstNodeUniop { // Emit C++'s co_await expression // @astgen alias op1 := exprp // - // @astgen ptr := m_sensesp : Optional[AstSenTree] // Sentree related to this await + // @astgen ptr := m_sentreep : Optional[AstSenTree] // Sentree related to this await public: - AstCAwait(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sensesp = nullptr) + AstCAwait(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep = nullptr) : ASTGEN_SUPER_CAwait(fl, exprp) - , m_sensesp{sensesp} {} + , m_sentreep{sentreep} {} ASTGEN_MEMBERS_AstCAwait; bool isTimingControl() const override { return true; } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - AstSenTree* sensesp() const { return m_sensesp; } - void clearSensesp() { m_sensesp = nullptr; } + AstSenTree* sentreep() const { return m_sentreep; } + void clearSentreep() { m_sentreep = nullptr; } void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 65f11ca93..05468ab53 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -386,26 +386,26 @@ public: class AstActive final : public AstNode { // Block of code with sensitivity activation // Parents: MODULE | CFUNC - // @astgen op1 := sensesStorep : Optional[AstSenTree] // Moved into m_sensesp in V3Active + // @astgen op1 := senTreeStorep : Optional[AstSenTree] // Moved into m_sensesp in V3Active // @astgen op2 := stmtsp : List[AstNode] // Logic // - // @astgen ptr := m_sensesp : Optional[AstSenTree] // Sensitivity list for this process + // @astgen ptr := m_sentreep : Optional[AstSenTree] // Sensitivity list for this process string m_name; public: - AstActive(FileLine* fl, const string& name, AstSenTree* sensesp) + AstActive(FileLine* fl, const string& name, AstSenTree* sentreep) : ASTGEN_SUPER_Active(fl) , m_name{name} - , m_sensesp{sensesp} { - UASSERT(sensesp, "Sensesp required arg"); + , m_sentreep{sentreep} { + UASSERT(sentreep, "Sentreep required arg"); } ASTGEN_MEMBERS_AstActive; void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; string name() const override VL_MT_STABLE { return m_name; } // Statements are broken into pieces, as some must come before others. - void sensesp(AstSenTree* nodep) { m_sensesp = nodep; } - AstSenTree* sensesp() const { return m_sensesp; } + void sentreep(AstSenTree* nodep) { m_sentreep = nodep; } + AstSenTree* sentreep() const { return m_sentreep; } // METHODS inline bool hasClocked() const; inline bool hasCombo() const; @@ -2498,14 +2498,14 @@ public: // === AstNodeProcedure === class AstAlways final : public AstNodeProcedure { - // @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked + // @astgen op1 := sentreep : Optional[AstSenTree] // Sensitivity list iff clocked const VAlwaysKwd m_keyword; public: - AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sensesp, AstNode* stmtsp) + AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sentreep, AstNode* stmtsp) : ASTGEN_SUPER_Always(fl, stmtsp) , m_keyword{keyword} { - this->sensesp(sensesp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstAlways; // @@ -2515,12 +2515,12 @@ public: }; class AstAlwaysObserved final : public AstNodeProcedure { // Like always but Observed scheduling region - // @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list, removed in V3Active + // @astgen op1 := sentreep : Optional[AstSenTree] // Sensitivity list, removed in V3Active public: - AstAlwaysObserved(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) + AstAlwaysObserved(FileLine* fl, AstSenTree* sentreep, AstNode* bodysp) : ASTGEN_SUPER_AlwaysObserved(fl, bodysp) { - this->sensesp(sensesp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstAlwaysObserved; }; @@ -2542,12 +2542,12 @@ public: }; class AstAlwaysReactive final : public AstNodeProcedure { // Like always but Reactive scheduling region - // @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list, removed in V3Active + // @astgen op1 := sentreep : Optional[AstSenTree] // Sensitivity list, removed in V3Active public: - AstAlwaysReactive(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) + AstAlwaysReactive(FileLine* fl, AstSenTree* sentreep, AstNode* bodysp) : ASTGEN_SUPER_AlwaysReactive(fl, bodysp) { - this->sensesp(sensesp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstAlwaysReactive; }; diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 7ba9e187d..4a3783276 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -268,12 +268,12 @@ public: class AstAlwaysPublic final : public AstNodeStmt { // "Fake" sensitivity created by /*verilator public_flat_rw @(edgelist)*/ // Body statements are just AstVarRefs to the public signals - // @astgen op1 := sensesp : List[AstSenTree] + // @astgen op1 := sentreep : Optional[AstSenTree] // @astgen op2 := stmtsp : List[AstNode] public: - AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) + AstAlwaysPublic(FileLine* fl, AstSenTree* sentreep, AstNode* stmtsp) : ASTGEN_SUPER_AlwaysPublic(fl) { - addSensesp(sensesp); + this->sentreep(sentreep); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstAlwaysPublic; @@ -605,12 +605,12 @@ public: }; class AstEventControl final : public AstNodeStmt { // Parents: stmtlist - // @astgen op1 := sensesp : Optional[AstSenTree] + // @astgen op1 := sentreep : Optional[AstSenTree] // @astgen op2 := stmtsp : List[AstNode] public: - AstEventControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp) + AstEventControl(FileLine* fl, AstSenTree* sentreep, AstNode* stmtsp) : ASTGEN_SUPER_EventControl(fl) { - this->sensesp(sensesp); + this->sentreep(sentreep); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstEventControl; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 9757c04eb..821fd09a1 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2814,8 +2814,8 @@ void AstDot::dumpJson(std::ostream& str) const { void AstActive::dump(std::ostream& str) const { this->AstNode::dump(str); str << " => "; - if (sensesp()) { - sensesp()->dump(str); + if (sentreep()) { + sentreep()->dump(str); } else { str << "UNLINKED"; } @@ -3069,9 +3069,9 @@ void AstCFunc::dumpJson(std::ostream& str) const { } void AstCAwait::dump(std::ostream& str) const { this->AstNodeUniop::dump(str); - if (sensesp()) { + if (sentreep()) { str << " => "; - sensesp()->dump(str); + sentreep()->dump(str); } } void AstCAwait::dumpJson(std::ostream& str) const { dumpJsonGen(str); } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index ac7fa5ff8..3627187b8 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -85,10 +85,10 @@ class ClockVisitor final : public VNVisitor { } return senEqnp; } - AstIf* makeActiveIf(AstSenTree* sensesp) { - AstNodeExpr* const senEqnp = createSenseEquation(sensesp->sensesp()); - UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation."); - AstIf* const newifp = new AstIf{sensesp->fileline(), senEqnp}; + AstIf* makeActiveIf(AstSenTree* sentreep) { + AstNodeExpr* const senEqnp = createSenseEquation(sentreep->sensesp()); + UASSERT_OBJ(senEqnp, sentreep, "No sense equation, shouldn't be in sequent activation."); + AstIf* const newifp = new AstIf{sentreep->fileline(), senEqnp}; return newifp; } void clearLastSen() { @@ -133,11 +133,11 @@ class ClockVisitor final : public VNVisitor { AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext(); // Create 'if' statement, if needed - if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) { + if (!m_lastSenp || !nodep->sentreep()->sameTree(m_lastSenp)) { VNRelinker relinker; nodep->unlinkFrBack(&relinker); clearLastSen(); - m_lastSenp = nodep->sensesp(); + m_lastSenp = nodep->sentreep(); // Make a new if statement m_lastIfp = makeActiveIf(m_lastSenp); relinker.relink(m_lastIfp); diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 1e7a07c53..25a6f234d 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -583,7 +583,7 @@ class DelayedVisitor final : public VNVisitor { vscpInfo.shadowVariableKit().vscp = shadowVscp; // Create the AstActive for the Pre/Post logic AstActive* const activep = new AstActive{flp, "nba-shadow-variable", vscpInfo.senTreep()}; - activep->sensesStorep(vscpInfo.senTreep()); + activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); // Add 'Pre' scheduled 'shadowVariable = originalVariable' assignment activep->addStmtsp(new AstAssignPre{flp, new AstVarRef{flp, shadowVscp, VAccess::WRITE}, @@ -622,7 +622,7 @@ class DelayedVisitor final : public VNVisitor { // Create the AstActive for the Post logic AstActive* const activep = new AstActive{flp, "nba-shadow-var-masked", vscpInfo.senTreep()}; - activep->sensesStorep(vscpInfo.senTreep()); + activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); // Add 'Post' scheduled process for the commit and mask clear AstAlwaysPost* const postp = new AstAlwaysPost{flp}; @@ -676,7 +676,7 @@ class DelayedVisitor final : public VNVisitor { AstScope* const scopep = vscp->scopep(); // Create the AstActive for the Pre/Post logic AstActive* const activep = new AstActive{flp, "nba-flag-shared", vscpInfo.senTreep()}; - activep->sensesStorep(vscpInfo.senTreep()); + activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); vscpInfo.flagSharedKit().activep = activep; // Add 'Post' scheduled process to be populated later @@ -773,7 +773,7 @@ class DelayedVisitor final : public VNVisitor { AstScope* const scopep = vscp->scopep(); // Create the AstActive for the Pre/Post logic AstActive* const activep = new AstActive{flp, "nba-flag-unique", vscpInfo.senTreep()}; - activep->sensesStorep(vscpInfo.senTreep()); + activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); // Add 'Post' scheduled process to be populated later AstAlwaysPost* const postp = new AstAlwaysPost{flp}; @@ -837,7 +837,7 @@ class DelayedVisitor final : public VNVisitor { // Create the AstActive for the Post logic AstActive* const activep = new AstActive{flp, "nba-value-queue-whole", vscpInfo.senTreep()}; - activep->sensesStorep(vscpInfo.senTreep()); + activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); // Add 'Post' scheduled process for the commit AstAlwaysPost* const postp = new AstAlwaysPost{flp}; @@ -1085,7 +1085,7 @@ class DelayedVisitor final : public VNVisitor { VL_RESTORER(m_ignoreBlkAndNBlk); VL_RESTORER(m_inNonCombLogic); m_activep = nodep; - AstSenTree* const senTreep = nodep->sensesp(); + AstSenTree* const senTreep = nodep->sentreep(); m_ignoreBlkAndNBlk = senTreep->hasStatic() || senTreep->hasInitial(); m_inNonCombLogic = senTreep->hasClocked(); iterateChildren(nodep); @@ -1131,7 +1131,7 @@ class DelayedVisitor final : public VNVisitor { iterateChildren(nodep); } void visit(AstCAwait* nodep) override { - if (nodep->sensesp()) m_timingDomains.insert(nodep->sensesp()); + if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep()); } void visit(AstFireEvent* nodep) override { UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent"); @@ -1166,7 +1166,7 @@ class DelayedVisitor final : public VNVisitor { } UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent"); - AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sensesp()}; + AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()}; m_activep->addNextHere(activep); activep->addStmtsp(prep); activep->addStmtsp(postp); @@ -1238,7 +1238,7 @@ class DelayedVisitor final : public VNVisitor { vscpInfo.m_inLoop |= m_inLoop; vscpInfo.m_inSuspOrFork |= m_inSuspendableOrFork; // Sensitivity might be non-clocked, in a suspendable process, which are handled elsewhere - if (m_activep->sensesp()->hasClocked()) { + if (m_activep->sentreep()->hasClocked()) { if (vscpInfo.m_fistActivep != m_activep) { AstVar* const varp = vscp->varp(); if (!varp->user1SetOnce() @@ -1255,7 +1255,7 @@ class DelayedVisitor final : public VNVisitor { } } // Add this sensitivity to the variable - vscpInfo.addSensitivity(m_activep->sensesp()); + vscpInfo.addSensitivity(m_activep->sentreep()); } // Record the NBA for later processing diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index e9379dca0..147bd2b1a 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -492,7 +492,7 @@ public: bool convert(AstAlways* nodep) { // Ignore sequential logic const VAlwaysKwd kwd = nodep->keyword(); - if (nodep->sensesp() || (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) { + if (nodep->sentreep() || (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) { return false; } diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index a96af5864..de5f5c125 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -127,7 +127,7 @@ class DfgToAstVisitor final : DfgVisitor { AstSenTree* const senTreep = new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}; AstActive* const activep = new AstActive{flp, "", senTreep}; - activep->sensesStorep(senTreep); + activep->senTreeStorep(senTreep); scopep->addBlocksp(activep); scopep->user2p(activep); } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index de7695bfd..5f7e34ced 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -153,7 +153,7 @@ class DataflowExtractVisitor final : public VNVisitor { void visit(AstAlways* nodep) override { VL_RESTORER(m_candidatesp); // Only extract from combinational logic under proper modules - const bool isComb = !nodep->sensesp() + const bool isComb = !nodep->sentreep() && (nodep->keyword() == VAlwaysKwd::ALWAYS || nodep->keyword() == VAlwaysKwd::ALWAYS_COMB || nodep->keyword() == VAlwaysKwd::ALWAYS_LATCH); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 46f979200..1990c54c9 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -34,7 +34,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { const bool m_suppressUnknown; // Do not error on unknown node // STATE - for current visit position (use VL_RESTORER) - AstSenTree* m_sensesp = nullptr; // Domain for printing one a ALWAYS under a ACTIVE + AstSenTree* m_sentreep = nullptr; // Domain for printing one a ALWAYS under a ACTIVE bool m_suppressSemi = false; // Non-statement, don't print ; bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_arrayPost = false; // Print array information that goes after identifier (vs after) @@ -122,11 +122,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { void visit(AstInitialStatic* nodep) override { iterateChildrenConst(nodep); } void visit(AstAlways* nodep) override { putfs(nodep, "always "); - if (m_sensesp) { - iterateAndNextConstNull(m_sensesp); + if (m_sentreep) { + iterateAndNextConstNull(m_sentreep); } // In active else { - iterateAndNextConstNull(nodep->sensesp()); + iterateAndNextConstNull(nodep->sentreep()); } putbs(" begin\n"); iterateAndNextConstNull(nodep->stmtsp()); @@ -134,11 +134,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } void visit(AstAlwaysPublic* nodep) override { putfs(nodep, "/*verilator public_flat_rw "); - if (m_sensesp) { - iterateAndNextConstNull(m_sensesp); + if (m_sentreep) { + iterateAndNextConstNull(m_sentreep); } // In active else { - iterateAndNextConstNull(nodep->sensesp()); + iterateAndNextConstNull(nodep->sentreep()); } putqs(nodep, " "); iterateAndNextConstNull(nodep->stmtsp()); @@ -923,8 +923,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { puts(m_suppressVarSemi ? "\n" : ";\n"); } void visit(AstActive* nodep) override { - VL_RESTORER(m_sensesp); - m_sensesp = nodep->sensesp(); + VL_RESTORER(m_sentreep); + m_sentreep = nodep->sentreep(); iterateAndNextConstNull(nodep->stmtsp()); } void visit(AstDelay* nodep) override { diff --git a/src/V3Force.cpp b/src/V3Force.cpp index 52c16e211..d2e5955a4 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -97,7 +97,7 @@ public: AstActive* const activep = new AstActive{ flp, "force-init", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}}}; - activep->sensesStorep(activep->sensesp()); + activep->senTreeStorep(activep->sentreep()); activep->addStmtsp(new AstInitial{flp, assignp}); vscp->scopep()->addBlocksp(activep); @@ -120,7 +120,7 @@ public: new AstSenItem{flp, VEdgeType::ET_CHANGED, origp->cloneTree(false)}); AstActive* const activep = new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}}; - activep->sensesStorep(activep->sensesp()); + activep->senTreeStorep(activep->sentreep()); activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, new AstAssign{flp, lhsp, rhsp}}); vscp->scopep()->addBlocksp(activep); diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 7608bbaed..a8e835d3c 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -298,7 +298,7 @@ class HasherVisitor final : public VNVisitorConst { } void visit(AstCAwait* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { // - iterateConstNull(nodep->sensesp()); + iterateConstNull(nodep->sentreep()); }); } void visit(AstCLocalScope* nodep) override { @@ -480,7 +480,7 @@ class HasherVisitor final : public VNVisitorConst { } void visit(AstActive* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { // - iterateConstNull(nodep->sensesp()); + iterateConstNull(nodep->sentreep()); }); } void visit(AstCell* nodep) override { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 5db065de1..f98396b24 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -837,11 +837,11 @@ class LinkParseVisitor final : public VNVisitor { "(IEEE 1800-2023 9.2.2.2.2)\n" << nodep->warnMore() << "... Suggest use a normal 'always'"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - } else if (alwaysp && !alwaysp->sensesp()) { + } else if (alwaysp && !alwaysp->sentreep()) { // If the event control is at the top, move the sentree to the always - if (AstSenTree* const sensesp = nodep->sensesp()) { - sensesp->unlinkFrBackWithNext(); - alwaysp->sensesp(sensesp); + if (AstSenTree* const sentreep = nodep->sentreep()) { + sentreep->unlinkFrBackWithNext(); + alwaysp->sentreep(sentreep); } if (nodep->stmtsp()) alwaysp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext()); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); diff --git a/src/V3OrderCFuncEmitter.h b/src/V3OrderCFuncEmitter.h index 03752a509..896b7b58b 100644 --- a/src/V3OrderCFuncEmitter.h +++ b/src/V3OrderCFuncEmitter.h @@ -109,7 +109,7 @@ public: // When profCFuncs, create a new function for each logic vertex if (v3Global.opt.profCFuncs()) forceNewFunction(); // If the new domain is different, force a new function as it needs to be called separately - if (!m_activeps.empty() && m_activeps.back()->sensesp() != domainp) forceNewFunction(); + if (!m_activeps.empty() && m_activeps.back()->sentreep() != domainp) forceNewFunction(); // Process procedures per statement, so we can split CFuncs within procedures. // Everything else is handled as a unit. @@ -146,7 +146,7 @@ public: AstCCall* const callp = new AstCCall{flp, m_funcp}; callp->dtypeSetVoid(); // Call it under an AstActive with the same sensitivity - if (m_activeps.empty() || m_activeps.back()->sensesp() != domainp) { + if (m_activeps.empty() || m_activeps.back()->sentreep() != domainp) { m_activeps.emplace_back(new AstActive{flp, name, domainp}); } m_activeps.back()->addStmtsp(callp->makeStmt()); diff --git a/src/V3OrderGraphBuilder.cpp b/src/V3OrderGraphBuilder.cpp index 96b346c05..58711906c 100644 --- a/src/V3OrderGraphBuilder.cpp +++ b/src/V3OrderGraphBuilder.cpp @@ -126,7 +126,7 @@ class OrderGraphBuilder final : public VNVisitor { // VISITORS void visit(AstActive* nodep) override { - UASSERT_OBJ(!nodep->sensesStorep(), nodep, + UASSERT_OBJ(!nodep->senTreeStorep(), nodep, "AstSenTrees should have been made global in V3ActiveTop"); UASSERT_OBJ(m_scopep, nodep, "AstActive not under AstScope"); UASSERT_OBJ(!m_logicVxp, nodep, "AstActive under logic"); @@ -138,8 +138,9 @@ class OrderGraphBuilder final : public VNVisitor { // This is the original sensitivity of the block (i.e.: not the ref into the TRIGGERVEC) - const AstSenTree* const senTreep - = nodep->sensesp()->hasCombo() ? nodep->sensesp() : m_trigToSen.at(nodep->sensesp()); + const AstSenTree* const senTreep = nodep->sentreep()->hasCombo() + ? nodep->sentreep() + : m_trigToSen.at(nodep->sentreep()); m_inClocked = senTreep->hasClocked(); @@ -149,11 +150,11 @@ class OrderGraphBuilder final : public VNVisitor { // Combinational and hybrid logic will have it's domain assigned based on the driver // domains. For clocked logic, we already know its domain. - if (!senTreep->hasCombo() && !senTreep->hasHybrid()) m_domainp = nodep->sensesp(); + if (!senTreep->hasCombo() && !senTreep->hasHybrid()) m_domainp = nodep->sentreep(); // Hybrid logic also includes additional sensitivities if (senTreep->hasHybrid()) { - m_hybridp = nodep->sensesp(); + m_hybridp = nodep->sentreep(); // Mark AstVarScopes that are explicit sensitivities AstNode::user3ClearTree(); senTreep->foreach([](const AstVarRef* refp) { // diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 8e16c3479..d19bebba0 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -80,7 +80,7 @@ std::vector getSenTreesUsedBy(const std::vectorsensesp(); + AstSenTree* const senTreep = activep->sentreep(); if (senTreep->user1SetOnce()) continue; if (senTreep->hasClocked() || senTreep->hasHybrid()) result.push_back(senTreep); } @@ -92,9 +92,9 @@ void remapSensitivities(const LogicByScope& lbs, std::unordered_map senTreeMap) { for (const auto& pair : lbs) { AstActive* const activep = pair.second; - AstSenTree* const senTreep = activep->sensesp(); + AstSenTree* const senTreep = activep->sentreep(); if (senTreep->hasCombo()) continue; - activep->sensesp(senTreeMap.at(senTreep)); + activep->sentreep(senTreeMap.at(senTreep)); } } @@ -368,7 +368,7 @@ LogicClasses gatherLogicClasses(AstNetlist* netlistp) { netlistp->foreach([&](AstScope* scopep) { scopep->foreach([&](AstActive* activep) { - AstSenTree* const senTreep = activep->sensesp(); + AstSenTree* const senTreep = activep->sentreep(); if (senTreep->hasStatic()) { UASSERT_OBJ(!senTreep->sensesp()->nextp(), activep, "static initializer with additional sensitivities"); diff --git a/src/V3Sched.h b/src/V3Sched.h index c1f8e58df..1f0e0ffb1 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -39,7 +39,7 @@ struct LogicByScope final : public std::vector> // Add logic void add(AstScope* scopep, AstSenTree* senTreep, AstNode* logicp) { UASSERT_OBJ(!logicp->backp(), logicp, "Already linked"); - if (empty() || back().first != scopep || back().second->sensesp() != senTreep) { + if (empty() || back().first != scopep || back().second->sentreep() != senTreep) { emplace_back(scopep, new AstActive{logicp->fileline(), "", senTreep}); } back().second->addStmtsp(logicp); diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index dc695adc8..eeeee8cb4 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -215,7 +215,7 @@ class SchedGraphBuilder final : public VNVisitor { // VISIT methods void visit(AstActive* nodep) override { - AstSenTree* const senTreep = nodep->sensesp(); + AstSenTree* const senTreep = nodep->sentreep(); UASSERT_OBJ(senTreep->hasClocked() || senTreep->hasCombo() || senTreep->hasHybrid(), nodep, "Unhandled"); UASSERT_OBJ(!m_senTreep, nodep, "Should not nest"); @@ -369,7 +369,7 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo for (const auto& pair : result.m_act) { AstActive* const activep = pair.second; - markVars(activep->sensesp()); + markVars(activep->sentreep()); markVars(activep); } @@ -394,12 +394,12 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo }); LogicByScope& lbs = toActiveRegion ? result.m_pre : result.m_nba; logicp->unlinkFrBack(); - lbs.add(scopep, activep->sensesp(), logicp); + lbs.add(scopep, activep->sentreep(), logicp); } else { UASSERT_OBJ(VN_IS(nodep, AssignPost) || VN_IS(nodep, AlwaysPost), nodep, "Unexpected node type " << nodep->typeName()); nodep->unlinkFrBack(); - result.m_nba.add(scopep, activep->sensesp(), nodep); + result.m_nba.add(scopep, activep->sentreep(), nodep); } } } diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 6f2b519ef..c7d89b336 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -188,7 +188,7 @@ std::unique_ptr buildGraph(const LogicRegions& logicRegions) { }; const auto addLogic = [&](RegionFlags region, AstScope* scopep, AstActive* activep) { - AstSenTree* const senTreep = activep->sensesp(); + AstSenTree* const senTreep = activep->sentreep(); // Predicate for whether a read of the given variable triggers this block std::function readTriggersThisLogic; diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index b0dbda05f..962048244 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -115,15 +115,15 @@ AstCCall* TimingKit::createCommit(AstNetlist* const netlistp) { m_commitFuncp->declPrivate(true); scopeTopp->addBlocksp(m_commitFuncp); } - AstSenTree* const sensesp = activep->sensesp(); - FileLine* const flp = sensesp->fileline(); + AstSenTree* const sentreep = activep->sentreep(); + FileLine* const flp = sentreep->fileline(); // Negate the sensitivity. We will commit only if the event wasn't triggered on the // current iteration - auto* const negSensesp = sensesp->cloneTree(false); - negSensesp->sensesp()->sensp( - new AstLogNot{flp, negSensesp->sensesp()->sensp()->unlinkFrBack()}); - sensesp->addNextHere(negSensesp); - auto* const newactp = new AstActive{flp, "", negSensesp}; + auto* const negSentreep = sentreep->cloneTree(false); + negSentreep->sensesp()->sensp( + new AstLogNot{flp, negSentreep->sensesp()->sensp()->unlinkFrBack()}); + sentreep->addNextHere(negSentreep); + auto* const newactp = new AstActive{flp, "", negSentreep}; // Create the commit call and put it in the commit function auto* const commitp = new AstCMethodHard{ flp, new AstVarRef{flp, schedulerp, VAccess::READWRITE}, "commit"}; @@ -180,8 +180,8 @@ TimingKit prepareTiming(AstNetlist* const netlistp) { void createResumeActive(AstCAwait* const awaitp) { auto* const methodp = VN_AS(awaitp->exprp(), CMethodHard); AstVarScope* const schedulerp = VN_AS(methodp->fromp(), VarRef)->varScopep(); - AstSenTree* const sensesp = awaitp->sensesp(); - FileLine* const flp = sensesp->fileline(); + AstSenTree* const sentreep = awaitp->sentreep(); + FileLine* const flp = sentreep->fileline(); // Create a resume() call on the timing scheduler auto* const resumep = new AstCMethodHard{ flp, new AstVarRef{flp, schedulerp, VAccess::READWRITE}, "resume"}; @@ -201,7 +201,7 @@ TimingKit prepareTiming(AstNetlist* const netlistp) { m_postUpdatesr = AstNode::addNext(m_postUpdatesr, postp->makeStmt()); } // Put it in an active and put that in the global resume function - auto* const activep = new AstActive{flp, "_timing", sensesp}; + auto* const activep = new AstActive{flp, "_timing", sentreep}; activep->addStmtsp(resumep->makeStmt()); m_lbs.emplace_back(m_scopeTopp, activep); } @@ -232,10 +232,10 @@ TimingKit prepareTiming(AstNetlist* const netlistp) { iterateChildren(nodep); } void visit(AstCAwait* nodep) override { - if (AstSenTree* const sensesp = nodep->sensesp()) { - if (!sensesp->user1SetOnce()) createResumeActive(nodep); - nodep->clearSensesp(); // Clear as these sentrees will get deleted later - if (m_inProcess) m_processDomains.insert(sensesp); + if (AstSenTree* const sentreep = nodep->sentreep()) { + if (!sentreep->user1SetOnce()) createResumeActive(nodep); + nodep->clearSentreep(); // Clear as these sentrees will get deleted later + if (m_inProcess) m_processDomains.insert(sentreep); } } void visit(AstNodeVarRef* nodep) override { diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 7bbfe1ed5..1b0ca79fc 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -473,16 +473,16 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl { void visit(AstNodeStmt* nodep) override { setContextAndIterateChildren(nodep); } void visit(AstCell* nodep) override { setContextAndIterateChildren(nodep); } void visit(AstAlways* nodep) override { - if (nodep->sensesp()) { // When visiting sensitivity list, always is the context - setContextAndIterate(nodep, nodep->sensesp()); + if (nodep->sentreep()) { // When visiting sensitivity list, always is the context + setContextAndIterate(nodep, nodep->sentreep()); } for (AstNode* bodysp = nodep->stmtsp(); bodysp; bodysp = bodysp->nextp()) { iterate(bodysp); } }; void visit(AstAlwaysPublic* nodep) override { - if (nodep->sensesp()) { // When visiting sensitivity list, always is the context - setContextAndIterate(nodep, nodep->sensesp()); + if (nodep->sentreep()) { // When visiting sensitivity list, always is the context + setContextAndIterate(nodep, nodep->sentreep()); } for (AstNode* bodysp = nodep->stmtsp(); bodysp; bodysp = bodysp->nextp()) { iterate(bodysp); diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index c23799239..48dd9e3f5 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -506,9 +506,9 @@ class TimingControlVisitor final : public VNVisitor { stmtp->replaceWith(delayp->unlinkFrBack()); delayp->addStmtsp(stmtp); stmtp = delayp; - } else if (AstSenTree* const sensesp = VN_CAST(controlp, SenTree)) { + } else if (AstSenTree* const sentreep = VN_CAST(controlp, SenTree)) { AstEventControl* const eventControlp - = new AstEventControl{sensesp->fileline(), sensesp->unlinkFrBack(), nullptr}; + = new AstEventControl{sentreep->fileline(), sentreep->unlinkFrBack(), nullptr}; stmtp->replaceWith(eventControlp); eventControlp->addStmtsp(stmtp); stmtp = eventControlp; @@ -619,8 +619,8 @@ class TimingControlVisitor final : public VNVisitor { }); } // Creates a trigger scheduler variable - AstVarScope* getCreateTriggerSchedulerp(AstSenTree* const sensesp) { - if (!sensesp->user1p()) { + AstVarScope* getCreateTriggerSchedulerp(AstSenTree* const sentreep) { + if (!sentreep->user1p()) { if (!m_trigSchedDtp) { m_trigSchedDtp = new AstBasicDType{m_scopeTopp->fileline(), VBasicDTypeKwd::TRIGGER_SCHEDULER, @@ -628,27 +628,27 @@ class TimingControlVisitor final : public VNVisitor { m_netlistp->typeTablep()->addTypesp(m_trigSchedDtp); } AstVarScope* const trigSchedp - = m_scopeTopp->createTemp(m_trigSchedNames.get(sensesp), m_trigSchedDtp); - sensesp->user1p(trigSchedp); + = m_scopeTopp->createTemp(m_trigSchedNames.get(sentreep), m_trigSchedDtp); + sentreep->user1p(trigSchedp); } - return VN_AS(sensesp->user1p(), VarScope); + return VN_AS(sentreep->user1p(), VarScope); } // Creates a string describing the sentree - AstCExpr* createEventDescription(AstSenTree* const sensesp) const { - if (!sensesp->user2p()) { + AstCExpr* createEventDescription(AstSenTree* const sentreep) const { + if (!sentreep->user2p()) { std::stringstream ss; ss << '"'; - V3EmitV::verilogForTree(sensesp, ss); + V3EmitV::verilogForTree(sentreep, ss); ss << '"'; // possibly a multiline string std::string comment = ss.str(); std::replace(comment.begin(), comment.end(), '\n', ' '); - AstCExpr* const commentp = new AstCExpr{sensesp->fileline(), comment, 0}; + AstCExpr* const commentp = new AstCExpr{sentreep->fileline(), comment, 0}; commentp->dtypeSetString(); - sensesp->user2p(commentp); + sentreep->user2p(commentp); return commentp; } - return VN_AS(sensesp->user2p(), CExpr)->cloneTree(false); + return VN_AS(sentreep->user2p(), CExpr)->cloneTree(false); } // Adds debug info to a hardcoded method call void addDebugInfo(AstCMethodHard* const methodp) const { @@ -662,9 +662,9 @@ class TimingControlVisitor final : public VNVisitor { methodp->addPinsp(bp); } // Adds debug info to a trigSched.trigger() call - void addEventDebugInfo(AstCMethodHard* const methodp, AstSenTree* const sensesp) const { + void addEventDebugInfo(AstCMethodHard* const methodp, AstSenTree* const sentreep) const { if (v3Global.opt.protectIds()) return; - methodp->addPinsp(createEventDescription(sensesp)); + methodp->addPinsp(createEventDescription(sentreep)); addDebugInfo(methodp); } // Adds process pointer to a hardcoded method call @@ -812,17 +812,17 @@ class TimingControlVisitor final : public VNVisitor { if (!hasFlags(nodep, T_SUSPENDEE)) return; nodep->setSuspendable(); FileLine* const flp = nodep->fileline(); - AstSenTree* const sensesp = m_activep->sensesp(); - if (sensesp->hasClocked()) { + AstSenTree* const sentreep = m_activep->sentreep(); + if (sentreep->hasClocked()) { AstNode* const bodysp = nodep->stmtsp()->unlinkFrBackWithNext(); - auto* const controlp = new AstEventControl{flp, sensesp->cloneTree(false), bodysp}; + auto* const controlp = new AstEventControl{flp, sentreep->cloneTree(false), bodysp}; nodep->addStmtsp(controlp); iterate(controlp); } // Note: The 'while (true)' outer loop will be added in V3Sched auto* const activep = new AstActive{ flp, "", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}}; - activep->sensesStorep(activep->sensesp()); + activep->senTreeStorep(activep->sentreep()); activep->addStmtsp(nodep->unlinkFrBack()); m_activep->addNextHere(activep); } @@ -905,11 +905,12 @@ class TimingControlVisitor final : public VNVisitor { void visit(AstEventControl* nodep) override { // Do not allow waiting on local named events, as they get enqueued for clearing, but can // go out of scope before that happens - if (!nodep->sensesp()) nodep->v3warn(E_UNSUPPORTED, "Unsupported: no sense equation (@*)"); + if (!nodep->sentreep()) + nodep->v3warn(E_UNSUPPORTED, "Unsupported: no sense equation (@*)"); FileLine* const flp = nodep->fileline(); // Relink child statements after the event control if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext()); - if (needDynamicTrigger(nodep->sensesp())) { + if (needDynamicTrigger(nodep->sentreep())) { // Create the trigger variable and init it with 0 AstVarScope* const trigvscp = createTemp(flp, m_dynTrigNames.get(nodep), nodep->findBitDType(), nodep); @@ -923,8 +924,8 @@ class TimingControlVisitor final : public VNVisitor { "evaluation"}; evalMethodp->dtypeSetVoid(); addProcessInfo(evalMethodp); - auto* const sensesp = nodep->sensesp(); - addEventDebugInfo(evalMethodp, sensesp); + auto* const sentreep = nodep->sentreep(); + addEventDebugInfo(evalMethodp, sentreep); // Create the co_await AstCAwait* const awaitEvalp = new AstCAwait{flp, evalMethodp, getCreateDynamicTriggerSenTree()}; @@ -932,7 +933,7 @@ class TimingControlVisitor final : public VNVisitor { // Construct the sen expression for this sentree UASSERT_OBJ(m_senExprBuilderp, nodep, "No SenExprBuilder for this scope"); auto* const assignp = new AstAssign{flp, new AstVarRef{flp, trigvscp, VAccess::WRITE}, - m_senExprBuilderp->build(sensesp).first}; + m_senExprBuilderp->build(sentreep).first}; // Get the SenExprBuilder results const SenExprBuilder::Results senResults = m_senExprBuilderp->getAndClearResults(); // Put all and inits before the trigger eval loop @@ -957,7 +958,7 @@ class TimingControlVisitor final : public VNVisitor { loopp->addStmtsp(anyTriggeredMethodp->makeStmt()); // If the post update is destructive (e.g. event vars are cleared), create an await for // the post update step - if (destructivePostUpdate(sensesp)) { + if (destructivePostUpdate(sentreep)) { AstCAwait* const awaitPostUpdatep = awaitEvalp->cloneTree(false); VN_AS(awaitPostUpdatep->exprp(), CMethodHard)->name("postUpdate"); loopp->addStmtsp(awaitPostUpdatep->makeStmt()); @@ -971,21 +972,21 @@ class TimingControlVisitor final : public VNVisitor { // Replace the event control with the loop nodep->replaceWith(loopp); } else { - auto* const sensesp = m_finder.getSenTree(nodep->sensesp()); - nodep->sensesp()->unlinkFrBack()->deleteTree(); + auto* const sentreep = m_finder.getSenTree(nodep->sentreep()); + nodep->sentreep()->unlinkFrBack()->deleteTree(); // Get this sentree's trigger scheduler // Replace self with a 'co_await trigSched.trigger()' auto* const triggerMethodp = new AstCMethodHard{ - flp, new AstVarRef{flp, getCreateTriggerSchedulerp(sensesp), VAccess::WRITE}, + flp, new AstVarRef{flp, getCreateTriggerSchedulerp(sentreep), VAccess::WRITE}, "trigger"}; triggerMethodp->dtypeSetVoid(); // If it should be committed immediately, pass true, otherwise false triggerMethodp->addPinsp(nodep->user2() ? new AstConst{flp, AstConst::BitTrue{}} : new AstConst{flp, AstConst::BitFalse{}}); addProcessInfo(triggerMethodp); - addEventDebugInfo(triggerMethodp, sensesp); + addEventDebugInfo(triggerMethodp, sentreep); // Create the co_await - AstCAwait* const awaitp = new AstCAwait{flp, triggerMethodp, sensesp}; + AstCAwait* const awaitp = new AstCAwait{flp, triggerMethodp, sentreep}; awaitp->dtypeSetVoid(); nodep->replaceWith(awaitp->makeStmt()); } diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 3d03126d8..63a123363 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -147,10 +147,10 @@ "lhsp": [ {"type":"PARSEREF","name":"result","addr":"(RC)","loc":"e,33:16,33:22","dtypep":"UNLINKED","expect":"TEXT","lhsp": [],"ftaskrefp": []} ],"timingControlp": [],"strengthSpecp": []}, - {"type":"ALWAYS","name":"","addr":"(SC)","loc":"e,36:4,36:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(SC)","loc":"e,36:4,36:10","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ {"type":"EVENTCONTROL","name":"","addr":"(TC)","loc":"e,36:11,36:12", - "sensesp": [ + "sentreep": [ {"type":"SENTREE","name":"","addr":"(UC)","loc":"e,36:11,36:12","isMulti":false, "sensesp": [ {"type":"SENITEM","name":"","addr":"(VC)","loc":"e,36:14,36:21","edgeType":"POS", @@ -466,10 +466,10 @@ ]} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"ALWAYS","name":"","addr":"(PH)","loc":"e,82:4,82:10","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(PH)","loc":"e,82:4,82:10","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ {"type":"EVENTCONTROL","name":"","addr":"(QH)","loc":"e,82:11,82:12", - "sensesp": [ + "sentreep": [ {"type":"SENTREE","name":"","addr":"(RH)","loc":"e,82:11,82:12","isMulti":false, "sensesp": [ {"type":"SENITEM","name":"","addr":"(SH)","loc":"e,82:13,82:20","edgeType":"POS", diff --git a/test_regress/t/t_json_only_first.out b/test_regress/t/t_json_only_first.out index 2689c200c..d0ea76704 100644 --- a/test_regress/t/t_json_only_first.out +++ b/test_regress/t/t_json_only_first.out @@ -64,7 +64,7 @@ {"type":"CONST","name":"32'sh1","addr":"(OB)","loc":"d,39:25,39:26","dtypep":"(LB)"} ],"attrsp": []}, {"type":"ALWAYS","name":"","addr":"(PB)","loc":"d,41:4,41:10","keyword":"always","isSuspendable":false,"needProcess":false, - "sensesp": [ + "sentreep": [ {"type":"SENTREE","name":"","addr":"(QB)","loc":"d,41:11,41:12","isMulti":false, "sensesp": [ {"type":"SENITEM","name":"","addr":"(RB)","loc":"d,41:13,41:20","edgeType":"POS", diff --git a/test_regress/t/t_json_only_flat.out b/test_regress/t/t_json_only_flat.out index 7b72c6a5c..7cdddf973 100644 --- a/test_regress/t/t_json_only_flat.out +++ b/test_regress/t/t_json_only_flat.out @@ -87,7 +87,7 @@ {"type":"VARREF","name":"t.cell1.q","addr":"(HC)","loc":"d,36:30,36:31","dtypep":"(H)","access":"WR","varp":"(U)","varScopep":"(LB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"ALWAYS","name":"","addr":"(IC)","loc":"d,41:4,41:10","keyword":"always","isSuspendable":false,"needProcess":false, - "sensesp": [ + "sentreep": [ {"type":"SENTREE","name":"","addr":"(JC)","loc":"d,41:11,41:12","isMulti":false, "sensesp": [ {"type":"SENITEM","name":"","addr":"(KC)","loc":"d,41:13,41:20","edgeType":"POS", diff --git a/test_regress/t/t_json_only_flat_vlvbound.out b/test_regress/t/t_json_only_flat_vlvbound.out index 9d1451e4e..8d7116053 100644 --- a/test_regress/t/t_json_only_flat_vlvbound.out +++ b/test_regress/t/t_json_only_flat_vlvbound.out @@ -60,7 +60,7 @@ "lhsp": [ {"type":"VARREF","name":"vlvbound_test.o_b","addr":"(BC)","loc":"d,12:25,12:28","dtypep":"(K)","access":"WR","varp":"(P)","varScopep":"(Y)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ALWAYS","name":"","addr":"(CC)","loc":"d,24:14,24:15","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(CC)","loc":"d,24:14,24:15","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ {"type":"COMMENT","name":"Function: foo","addr":"(DC)","loc":"d,24:16,24:19"}, {"type":"ASSIGN","name":"","addr":"(EC)","loc":"d,24:20,24:23","dtypep":"(H)", @@ -170,7 +170,7 @@ {"type":"VARREF","name":"o_a","addr":"(XD)","loc":"d,24:10,24:13","dtypep":"(K)","access":"WR","varp":"(J)","varScopep":"(T)","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ]}, - {"type":"ALWAYS","name":"","addr":"(YD)","loc":"d,25:14,25:15","keyword":"always","isSuspendable":false,"needProcess":false,"sensesp": [], + {"type":"ALWAYS","name":"","addr":"(YD)","loc":"d,25:14,25:15","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ {"type":"COMMENT","name":"Function: foo","addr":"(ZD)","loc":"d,25:16,25:19"}, {"type":"ASSIGN","name":"","addr":"(AE)","loc":"d,25:20,25:23","dtypep":"(H)", From 25d71a43a5f686ff12850cfd81b5e562969a6a4b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Aug 2025 20:14:19 -0400 Subject: [PATCH 184/252] For hier-generated Verilog, use always_comb --- src/V3ProtectLib.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index f11cdb9a2..ebec94043 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -267,9 +267,9 @@ class ProtectVisitor final : public VNVisitor { txtp->addText(fl, "end\n\n"); // Combinatorial process - addComment(txtp, fl, "Combinatorialy evaluate changes to inputs"); + addComment(txtp, fl, "Combinatorially evaluate changes to inputs"); m_comboParamsp = new AstTextBlock{fl, - "always @* begin\n" + "always_comb begin\n" "last_combo_seqnum__V = " + m_libName + "_protectlib_combo_update(\n", false, true}; @@ -302,7 +302,7 @@ class ProtectVisitor final : public VNVisitor { // Select between combinatorial and sequential results addComment(txtp, fl, "Select between combinatorial and sequential results"); - txtp->addText(fl, "always @* begin\n"); + txtp->addText(fl, "always_comb begin\n"); if (m_hasClk) { m_seqAssignsp = new AstTextBlock{fl, "if (last_seq_seqnum__V > " "last_combo_seqnum__V) begin\n"}; From c172cc2ab05fbd38365cb47005afd9a6e35ee899 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 18 Aug 2025 09:55:54 +0100 Subject: [PATCH 185/252] Internals: Simplify port connect after instance dearray (#6302) Pre pull for #6298 --- src/V3Inst.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 08c2af8c6..828d4973f 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -644,7 +644,10 @@ public: // Make a new temp wire // UINFOTREE(9, pinp, "", "in_pin"); V3Inst::checkOutputShort(pinp); - AstNodeExpr* const pinexprp = VN_AS(pinp->exprp(), NodeExpr)->unlinkFrBack(); + // Simplify, so stuff like '{a[0], b[0]}[1]' produced during + // instance array expansion are brought to normal 'a[0]' + AstNodeExpr* const pinexprp + = V3Const::constifyEdit(VN_AS(pinp->exprp(), NodeExpr)->unlinkFrBack()); const string newvarname = (string{pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp"} // Prevent name conflict if both tri & non-tri add signals From 9dc31e4556c2de931dfe9445dfedc147c5efe398 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 18 Aug 2025 13:46:35 +0100 Subject: [PATCH 186/252] Fix unsound assertion in V3Delayed (#6305) --- src/V3Delayed.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 25a6f234d..c812f83c4 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -469,7 +469,11 @@ class DelayedVisitor final : public VNVisitor { varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep}; modp->addStmtsp(varp); } - UASSERT_OBJ(varp->dtypep()->isSame(dtypep), flp, "Invalid type for temporary AstVar"); + + // We should be able to assert this here, but unfortuantely + // 'isAssignmentCompatible' does not exist as of right now. + // UASSERT_OBJ(isAssignmentCompatible(varp->dtypep(), dtypep), flp, "Invalid temporary"); + // Create the AstVarScope AstVarScope* const varscp = new AstVarScope{flp, scopep, varp}; scopep->addVarsp(varscp); From 53c59e7ac78a2b5f925cbf2b550ab28711c8c954 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Mon, 18 Aug 2025 14:51:25 +0200 Subject: [PATCH 187/252] Fix referencing module variables above classes (#6304) Signed-off-by: Artur Bieniek --- src/V3Scope.cpp | 15 ++++++++++--- test_regress/t/t_class_modscope.py | 18 ++++++++++++++++ test_regress/t/t_class_modscope.v | 34 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_class_modscope.py create mode 100644 test_regress/t/t_class_modscope.v diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index cc89adb07..ea29b1fa2 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -68,9 +68,18 @@ class ScopeVisitor final : public VNVisitor { UASSERT_OBJ(it2 != m_packageScopes.end(), nodep, "Can't locate package scope"); scopep = it2->second; } - const auto it3 = m_varScopes.find(std::make_pair(nodep->varp(), scopep)); - UASSERT_OBJ(it3 != m_varScopes.end(), nodep, "Can't locate varref scope"); - AstVarScope* const varscp = it3->second; + // Search up the scope hierarchy for the variable + AstVarScope* varscp = nullptr; + AstScope* searchScopep = scopep; + while (searchScopep) { + const auto it3 = m_varScopes.find(std::make_pair(nodep->varp(), searchScopep)); + if (it3 != m_varScopes.end()) { + varscp = it3->second; + break; + } + searchScopep = searchScopep->aboveScopep(); + } + UASSERT_OBJ(varscp, nodep, "Can't locate varref scope"); nodep->varScopep(varscp); } } diff --git a/test_regress/t/t_class_modscope.py b/test_regress/t/t_class_modscope.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_modscope.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_modscope.v b/test_regress/t/t_class_modscope.v new file mode 100644 index 000000000..a2cfb24cd --- /dev/null +++ b/test_regress/t/t_class_modscope.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define 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 m(); + class c; + static function void fstatic(); + `checkh(v, 42); + v++; + endfunction + function void fnonstatic(); + `checkh(v, 43); + v++; + endfunction + endclass + + c classinst; + int v; + + initial begin + v=42; + `checkh(v, 42); + c::fstatic(); + classinst = new(); + classinst.fnonstatic(); + `checkh(v, 44); + $finish; + end +endmodule From c90f9e53b7779625e8095bd0645796b2832ae4bd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 18 Aug 2025 12:00:53 -0400 Subject: [PATCH 188/252] Add ALWNEVER warning, for `always @*` that never execute (#6291) (#6303) --- Changes | 1 + docs/gen/ex_ALWNEVER_faulty.rst | 4 +++ docs/gen/ex_ALWNEVER_msg.rst | 4 +++ docs/guide/warnings.rst | 21 +++++++++++++ src/V3Ast.h | 8 +++-- src/V3AstNodeOther.h | 9 +++++- src/V3AstNodes.cpp | 2 +- src/V3Clock.cpp | 6 ++-- src/V3Delayed.cpp | 3 +- src/V3Error.h | 25 ++++++++-------- src/V3LinkParse.cpp | 2 +- src/V3SchedPartition.cpp | 8 +++-- src/V3Timing.cpp | 5 +++- src/V3Width.cpp | 1 + src/V3WidthCommit.cpp | 30 +++++++++++++++++++ src/verilog.y | 24 ++++++++------- test_regress/t/t_event_control_star.out | 7 ++--- test_regress/t/t_event_control_star_never.py | 18 +++++++++++ test_regress/t/t_event_control_star_never.v | 14 +++++++++ .../t/t_event_control_star_never_bad.out | 8 +++++ .../t/t_event_control_star_never_bad.py | 30 +++++++++++++++++++ test_regress/t/t_lint_blksync_bad.v | 2 +- test_regress/t/t_math_vgen.v | 2 +- 23 files changed, 192 insertions(+), 42 deletions(-) create mode 100644 docs/gen/ex_ALWNEVER_faulty.rst create mode 100644 docs/gen/ex_ALWNEVER_msg.rst create mode 100755 test_regress/t/t_event_control_star_never.py create mode 100644 test_regress/t/t_event_control_star_never.v create mode 100644 test_regress/t/t_event_control_star_never_bad.out create mode 100755 test_regress/t/t_event_control_star_never_bad.py diff --git a/Changes b/Changes index 21e601503..157f59d8c 100644 --- a/Changes +++ b/Changes @@ -20,6 +20,7 @@ Verilator 5.039 devel * Add enum base data type, wire data type, and I/O versus data declaration checking per IEEE. * Add PROTOTYPEMIS error on missing and mismatching prototypes (#6206) (#6207). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] +* Add ALWNEVER warning, for `always @*` that never execute (#6291). * Add error on class 'function static'. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] diff --git a/docs/gen/ex_ALWNEVER_faulty.rst b/docs/gen/ex_ALWNEVER_faulty.rst new file mode 100644 index 000000000..6080a1065 --- /dev/null +++ b/docs/gen/ex_ALWNEVER_faulty.rst @@ -0,0 +1,4 @@ +.. comment: generated by t_event_control_star_never_bad +.. code-block:: sv + + always @* a = 100; diff --git a/docs/gen/ex_ALWNEVER_msg.rst b/docs/gen/ex_ALWNEVER_msg.rst new file mode 100644 index 000000000..f1c8470f8 --- /dev/null +++ b/docs/gen/ex_ALWNEVER_msg.rst @@ -0,0 +1,4 @@ +.. comment: generated by t_event_control_star_never_bad +.. code-block:: + + %Warning-ALWNEVER: example.v:1:3 'always @*' will never execute as expression list is empty (no variables read) diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 24bfd9a56..ee50f6894 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -111,6 +111,27 @@ List Of Warnings simulate correctly. +.. option:: ALWNEVER + + Warning that an `always @*` statement has no variables being read, + therefore the event list is empty, and as there are no events to wake + the process up, the always will never execute. + + Faulty example: + + .. include:: ../../docs/gen/ex_ALWNEVER_faulty.rst + + Results in: + + .. include:: ../../docs/gen/ex_ALWNEVER_msg.rst + + To repair, assuming the intent was to execute the statements at e.g. + time zero, instead use an `always_comb` statement. + + Ignoring this warning will only suppress the lint check; it will + simulate correctly. + + .. option:: ASCRANGE .. TODO better example diff --git a/src/V3Ast.h b/src/V3Ast.h index 217ff2fa7..32b1ceb99 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -406,6 +406,7 @@ public: ET_TRUE, // ET_COMBO, // Sensitive to all combo inputs to this block + ET_COMBO_STAR, // Sensitive to all combo inputs to this block (from .*) ET_HYBRID, // This is like ET_COMB, but with explicit sensitivity to an expression ET_STATIC, // static variable initializers (runs before 'initial') ET_INITIAL, // 'initial' statements @@ -423,6 +424,7 @@ public: true, // ET_TRUE false, // ET_COMBO + false, // ET_COMBO_STAR false, // ET_HYBRID false, // ET_STATIC false, // ET_INITIAL @@ -443,13 +445,13 @@ public: } const char* ascii() const { static const char* const names[] - = {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", - "COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"}; + = {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "COMBO", + "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"}; return names[m_e]; } const char* verilogKwd() const { static const char* const names[] - = {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", + = {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"}; return names[m_e]; } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 05468ab53..84aef1b07 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1533,7 +1533,10 @@ public: AstNodeVarRef* varrefp() const { return VN_CAST(sensp(), NodeVarRef); } // bool isClocked() const { return edgeType().clockedStmt(); } - bool isCombo() const { return edgeType() == VEdgeType::ET_COMBO; } + bool isComboOrStar() const { + return edgeType() == VEdgeType::ET_COMBO || edgeType() == VEdgeType::ET_COMBO_STAR; + } + bool isComboStar() const { return edgeType() == VEdgeType::ET_COMBO_STAR; } bool isHybrid() const { return edgeType() == VEdgeType::ET_HYBRID; } bool isStatic() const { return edgeType() == VEdgeType::ET_STATIC; } bool isInitial() const { return edgeType() == VEdgeType::ET_INITIAL; } @@ -1550,6 +1553,10 @@ public: addSensesp(sensesp); } ASTGEN_MEMBERS_AstSenTree; + bool sameNode(const AstNode* samep) const override { + const AstSenTree* const asamep = VN_DBG_AS(samep, SenTree); + return m_multi == asamep->m_multi; + } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool maybePointedTo() const override VL_MT_SAFE { return true; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 821fd09a1..a4f223e32 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1254,7 +1254,7 @@ bool AstSenTree::hasFinal() const { bool AstSenTree::hasCombo() const { UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it"); for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) { - if (senp->isCombo()) return true; + if (senp->isComboOrStar()) return true; } return false; } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 3627187b8..10c10d057 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -80,8 +80,10 @@ class ClockVisitor final : public VNVisitor { AstNodeExpr* senEqnp = nullptr; for (AstSenItem* senp = nodesp; senp; senp = VN_AS(senp->nextp(), SenItem)) { UASSERT_OBJ(senp->edgeType() == VEdgeType::ET_TRUE, senp, "Should have been lowered"); - AstNodeExpr* const senOnep = senp->sensp()->cloneTree(false); - senEqnp = senEqnp ? new AstOr{senp->fileline(), senEqnp, senOnep} : senOnep; + if (senp->sensp()) { + AstNodeExpr* const senOnep = senp->sensp()->cloneTree(false); + senEqnp = senEqnp ? new AstOr{senp->fileline(), senEqnp, senOnep} : senOnep; + } } return senEqnp; } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index c812f83c4..a1c4f565a 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -1119,7 +1119,8 @@ class DelayedVisitor final : public VNVisitor { // First gather all senItems AstSenItem* senItemp = nullptr; for (AstSenTree* const domainp : m_timingDomains) { - senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true)); + if (domainp->sensesp()) + senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true)); } m_timingDomains.clear(); // Add them to all nba targets we gathered in this process diff --git a/src/V3Error.h b/src/V3Error.h index 224f6f987..aed9db65c 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -71,6 +71,7 @@ public: EC_FIRST_WARN, // Just a code so the program knows where to start warnings // ALWCOMBORDER, // Always_comb with unordered statements + ALWNEVER, // always will never execute ASCRANGE, // Ascending bit range vector ASSIGNDLY, // Assignment delays ASSIGNIN, // Assigning to input @@ -203,18 +204,18 @@ public: // Errors "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED", // Warnings - " EC_FIRST_WARN", "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", - "BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", - "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", - "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME", - "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", - "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "IFDEPTH", - "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", - "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", - "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", - "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PARAMNODEFAULT", - "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", - "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", + " EC_FIRST_WARN", "ALWCOMBORDER", "ALWNEVER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", + "BADSTDPRAGMA", "BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", + "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", + "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", + "COVERIGN", "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", + "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", + "HIERBLOCK", "IFDEPTH", "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", + "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", + "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", + "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", + "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", + "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index f98396b24..0e3901af1 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -835,7 +835,7 @@ class LinkParseVisitor final : public VNVisitor { if (alwaysp && alwaysp->keyword() == VAlwaysKwd::ALWAYS_COMB) { alwaysp->v3error("Event control statements not legal under always_comb " "(IEEE 1800-2023 9.2.2.2.2)\n" - << nodep->warnMore() << "... Suggest use a normal 'always'"); + << alwaysp->warnMore() << "... Suggest use a normal 'always'"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } else if (alwaysp && !alwaysp->sentreep()) { // If the event control is at the top, move the sentree to the always diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index eeeee8cb4..28048f098 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -162,9 +162,11 @@ class SchedGraphBuilder final : public VNVisitor { SchedSenVertex* const vtxp = new SchedSenVertex{m_graphp, senItemp}; // Connect up the variable references - senItemp->sensp()->foreach([&](AstVarRef* refp) { - new V3GraphEdge{m_graphp, getVarVertex(refp->varScopep()), vtxp, 1}; - }); + if (senItemp->sensp()) { + senItemp->sensp()->foreach([&](AstVarRef* refp) { + new V3GraphEdge{m_graphp, getVarVertex(refp->varScopep()), vtxp, 1}; + }); + } // Store back to hash map so we can find it next time pair.first->second = vtxp; diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 48dd9e3f5..d70f3ed21 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -905,8 +905,11 @@ class TimingControlVisitor final : public VNVisitor { void visit(AstEventControl* nodep) override { // Do not allow waiting on local named events, as they get enqueued for clearing, but can // go out of scope before that happens - if (!nodep->sentreep()) + if (!nodep->sentreep()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: no sense equation (@*)"); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } FileLine* const flp = nodep->fileline(); // Relink child statements after the event control if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext()); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 834e4b165..6a7fd368c 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6556,6 +6556,7 @@ class WidthVisitor final : public VNVisitor { userIterateChildren(nodep, nullptr); } void visit(AstSenItem* nodep) override { + if (nodep->isComboStar()) return; UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge"); // Optimize concat/replicate senitems; this has to be done here at the latest, otherwise we // emit WIDTHCONCAT if there are unsized constants diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 64e35b3ea..20cc4677d 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -168,6 +168,36 @@ private: } } } + void visit(AstAlways* nodep) override { + // As have not optimized SenTrees yet, an 'always .*' will be on first and only SenItem + if (nodep->sentreep() && nodep->sentreep()->sensesp() + && nodep->sentreep()->sensesp()->isComboStar()) { + const bool noReads = nodep->forall( + [&](const AstNodeVarRef* refp) { return !refp->access().isReadOrRW(); }); + if (noReads) { + nodep->v3warn(ALWNEVER, "'always @*' will never execute as expression list is " + "empty (no variables read)\n" + << nodep->warnMore() + << "... Suggest use 'always_comb'"); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + } + // Iterate will delete ComboStar sentrees, so after above + iterateChildren(nodep); + editDType(nodep); + } + void visit(AstSenTree* nodep) override { + if (nodep->sensesp() && nodep->sensesp()->isComboStar()) { + UASSERT_OBJ(!nodep->sensesp()->nextp(), nodep, "Shouldn't be senitems after .*"); + // Make look like standalone always + // (Rest of code assumed this before .* existed) + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + iterateChildren(nodep); + editDType(nodep); + } void visit(AstAttrOf* nodep) override { switch (nodep->attrType()) { case VAttrType::FUNC_ARG_PROTO: // FALLTHRU diff --git a/src/verilog.y b/src/verilog.y index 8f9051fdf..d2fd145c0 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -160,14 +160,17 @@ public: return new AstGatePin{rangep->fileline(), exprp, rangep->cloneTree(true)}; } } - AstSenTree* createClockSenTree(FileLine* fl, AstNodeExpr* exprp) { + AstSenTree* createSenTreeChanged(FileLine* fl, AstNodeExpr* exprp) { return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_CHANGED, exprp}}; } + AstSenTree* createSenTreeDotStar(FileLine* fl) { + return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_COMBO_STAR, nullptr}}; + } AstNodeExpr* createGlobalClockParseRef(FileLine* fl) { return new AstParseRef{fl, VParseRefExp::PX_TEXT, "__024global_clock", nullptr, nullptr}; } AstSenTree* createGlobalClockSenTree(FileLine* fl) { - return createClockSenTree(fl, createGlobalClockParseRef(fl)); + return createSenTreeChanged(fl, createGlobalClockParseRef(fl)); } AstNode* createNettype(FileLine* fl, const string& name) { // As nettypes are unsupported, we just alias to logic @@ -2817,7 +2820,6 @@ module_common_item: // ==IEEE: module_common_item ; always_construct: // IEEE: == always_construct - // // Verilator only - event_control attached to always yALWAYS stmtBlock { $$ = new AstAlways{$1, VAlwaysKwd::ALWAYS, nullptr, $2}; } | yALWAYS_FF stmtBlock { $$ = new AstAlways{$1, VAlwaysKwd::ALWAYS_FF, nullptr, $2}; } | yALWAYS_LATCH stmtBlock { $$ = new AstAlways{$1, VAlwaysKwd::ALWAYS_LATCH, nullptr, $2}; } @@ -3492,14 +3494,14 @@ attr_event_controlE: attr_event_control: // ==IEEE: event_control '@' '(' event_expression ')' { $$ = new AstSenTree{$1, $3}; } - | '@' '(' '*' ')' { $$ = nullptr; } - | '@' '*' { $$ = nullptr; } + | '@' '(' '*' ')' { $$ = GRAMMARP->createSenTreeDotStar($1); } + | '@' '*' { $$ = GRAMMARP->createSenTreeDotStar($1); } ; event_control: // ==IEEE: event_control // UNSUP: Needs alignment with IEEE event_control and clocking_event - '@' '(' '*' ')' { $$ = nullptr; } - | '@' '*' { $$ = nullptr; } + '@' '(' '*' ')' { $$ = GRAMMARP->createSenTreeDotStar($1); } + | '@' '*' { $$ = GRAMMARP->createSenTreeDotStar($1); } // // IEEE: clocking_event | '@' '(' event_expression ')' { $$ = new AstSenTree{$1, $3}; } // // IEEE: hierarchical_event_identifier @@ -4464,7 +4466,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_CEIL '(' expr ')' { $$ = new AstCeilD{$1, $3}; } | yD_CHANGED '(' expr ')' { $$ = new AstLogNot{$1, new AstStable{$1, $3, nullptr}}; } | yD_CHANGED '(' expr ',' expr ')' - { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createClockSenTree($1, $5)}}; } + { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}}; } | yD_CHANGED_GCLK '(' expr ')' { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}}; } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2{$1, $3}; } @@ -4487,7 +4489,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_DIST_UNIFORM '(' expr ',' expr ',' expr ')' { $$ = new AstDistUniform{$1, $3, $5, $7}; } | yD_EXP '(' expr ')' { $$ = new AstExpD{$1, $3}; } | yD_FELL '(' expr ')' { $$ = new AstFell{$1, $3, nullptr}; } - | yD_FELL '(' expr ',' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_FELL '(' expr ',' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_FELL_GCLK '(' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_FEOF '(' expr ')' { $$ = new AstFEof{$1, $3}; } | yD_FERROR '(' expr ',' idClassSel ')' { $$ = new AstFError{$1, $3, $5}; } @@ -4539,7 +4541,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, nullptr}; } | yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, $5}; } | yD_ROSE '(' expr ')' { $$ = new AstRose{$1, $3, nullptr}; } - | yD_ROSE '(' expr ',' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_ROSE '(' expr ',' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_ROSE_GCLK '(' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_RTOI '(' expr ')' { $$ = new AstRToIS{$1, $3}; } | yD_SAMPLED '(' expr ')' { $$ = new AstSampled{$1, $3}; } @@ -4555,7 +4557,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_STIME parenE { $$ = new AstSel{$1, new AstTime{$1, VTimescale{VTimescale::NONE}}, 0, 32}; } | yD_STABLE '(' expr ')' { $$ = new AstStable{$1, $3, nullptr}; } - | yD_STABLE '(' expr ',' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_STABLE '(' expr ',' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_STABLE_GCLK '(' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_TAN '(' expr ')' { $$ = new AstTanD{$1, $3}; } | yD_TANH '(' expr ')' { $$ = new AstTanhD{$1, $3}; } diff --git a/test_regress/t/t_event_control_star.out b/test_regress/t/t_event_control_star.out index c8682b945..d33d0e424 100644 --- a/test_regress/t/t_event_control_star.out +++ b/test_regress/t/t_event_control_star.out @@ -1,6 +1,5 @@ -%Error-UNSUPPORTED: t/t_event_control_star.v:19:14: Unsupported: no sense equation (@*) +%Error-UNSUPPORTED: t/t_event_control_star.v:19:6: Unsupported: no sense equation (@*) 19 | @* a = c; - | ^ + | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Verilator internal fault, sorry. Suggest trying --debug --gdbbt -%Error: Command Failed +%Error: Exiting due to diff --git a/test_regress/t/t_event_control_star_never.py b/test_regress/t/t_event_control_star_never.py new file mode 100755 index 000000000..3c530ac0d --- /dev/null +++ b/test_regress/t/t_event_control_star_never.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', '-Wno-ALWNEVER']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_event_control_star_never.v b/test_regress/t/t_event_control_star_never.v new file mode 100644 index 000000000..e0e62bf94 --- /dev/null +++ b/test_regress/t/t_event_control_star_never.v @@ -0,0 +1,14 @@ +// 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; + int a; + always @* a = 100; + initial begin + #1; + if (a != 0) $stop; + end +endmodule diff --git a/test_regress/t/t_event_control_star_never_bad.out b/test_regress/t/t_event_control_star_never_bad.out new file mode 100644 index 000000000..4abe8252e --- /dev/null +++ b/test_regress/t/t_event_control_star_never_bad.out @@ -0,0 +1,8 @@ +%Warning-ALWNEVER: t/t_event_control_star_never.v:9:3: 'always @*' will never execute as expression list is empty (no variables read) + : ... note: In instance 't' + : ... Suggest use 'always_comb' + 9 | always @* a = 100; + | ^~~~~~ + ... For warning description see https://verilator.org/warn/ALWNEVER?v=latest + ... Use "/* verilator lint_off ALWNEVER */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_event_control_star_never_bad.py b/test_regress/t/t_event_control_star_never_bad.py new file mode 100755 index 000000000..ec7fab69d --- /dev/null +++ b/test_regress/t/t_event_control_star_never_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('linter') +test.top_filename = 't/t_event_control_star_never.v' + +root = ".." + +if not os.path.exists(root + "/.git"): + test.skip("Not in a git repository") + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.extract(in_filename=test.top_filename, + out_filename=root + "/docs/gen/ex_ALWNEVER_faulty.rst", + lines="9-9") + +test.extract(in_filename=test.golden_filename, + out_filename=root + "/docs/gen/ex_ALWNEVER_msg.rst", + lines="1-1") + +test.passes() diff --git a/test_regress/t/t_lint_blksync_bad.v b/test_regress/t/t_lint_blksync_bad.v index f7bc51bf7..839b0db6b 100644 --- a/test_regress/t/t_lint_blksync_bad.v +++ b/test_regress/t/t_lint_blksync_bad.v @@ -26,7 +26,7 @@ module t (/*AUTOARG*/ sync_nblk <= 1'b1; end - always @* begin + always_comb begin combo_blk = 1'b1; combo_nblk <= 1'b1; end diff --git a/test_regress/t/t_math_vgen.v b/test_regress/t/t_math_vgen.v index 0111649c0..c2b0f7720 100644 --- a/test_regress/t/t_math_vgen.v +++ b/test_regress/t/t_math_vgen.v @@ -214,7 +214,7 @@ module t (/*AUTOARG*/ reg signed [ 82:0] W0226 ; //=47A4301EE3FB4133EE3DA - always @* begin : Block144 + always_comb begin : Block144 W0226 = 83'sh47A4301EE3FB4133EE3DA; if ((W0226 >>> 8'sh1a) != 83'sh7ffffff1e90c07b8fed04) if (check) $stop; end From c9a6d06544bf777eb6e84474827e3652f7dcd295 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 00:11:14 +0100 Subject: [PATCH 189/252] Improve V3LifePost to not require AstAssignPre/AssignPost (#6306) Rewrote V3LifePost to not depend on having AstAssignPre and AstAssignPost types, but work with generic AstNodeAssign. There is an extra flag in AstVarScope to denote it's part of an NBA and should be considered. Step towards #6280. --- src/V3AstNodeOther.h | 4 + src/V3Delayed.cpp | 3 + src/V3LifePost.cpp | 460 ++++++++++++++++++--------------------- src/V3SchedVirtIface.cpp | 3 + 4 files changed, 217 insertions(+), 253 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 84aef1b07..821e2bf0c 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2170,6 +2170,7 @@ class AstVarScope final : public AstNode { // @astgen ptr := m_scopep : Optional[AstScope] // Scope variable is underneath // @astgen ptr := m_varp : Optional[AstVar] // [AfterLink] Pointer to variable itself bool m_trace : 1; // Tracing is turned on for this scope + bool m_optimizeLifePost : 1; // One half of an NBA pair using ShadowVariable scheme. Optimize. public: AstVarScope(FileLine* fl, AstScope* scopep, AstVar* varp) : ASTGEN_SUPER_VarScope(fl) @@ -2178,6 +2179,7 @@ public: UASSERT_OBJ(scopep, fl, "Scope must be non-null"); UASSERT_OBJ(varp, fl, "Var must be non-null"); m_trace = true; + m_optimizeLifePost = false; dtypeFrom(varp); } ASTGEN_MEMBERS_AstVarScope; @@ -2198,6 +2200,8 @@ public: void scopep(AstScope* nodep) { m_scopep = nodep; } bool isTrace() const { return m_trace; } void trace(bool flag) { m_trace = flag; } + bool optimizeLifePost() const { return m_optimizeLifePost; } + void optimizeLifePost(bool flag) { m_optimizeLifePost = flag; } }; // === AstNodeBlock === diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index a1c4f565a..463d9fe8a 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -585,6 +585,9 @@ class DelayedVisitor final : public VNVisitor { const std::string name = "__Vdly__" + vscp->varp()->shortName(); AstVarScope* const shadowVscp = createTemp(flp, scopep, name, vscp->dtypep()); vscpInfo.shadowVariableKit().vscp = shadowVscp; + // Mark both for V3LifePsot + vscp->optimizeLifePost(true); + shadowVscp->optimizeLifePost(true); // Create the AstActive for the Pre/Post logic AstActive* const activep = new AstActive{flp, "nba-shadow-variable", vscpInfo.senTreep()}; activep->senTreeStorep(vscpInfo.senTreep()); diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 7a6e5b889..d8b191900 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -1,6 +1,6 @@ // -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* -// DESCRIPTION: Verilator: AssignPost Variable assignment elimination +// DESCRIPTION: Verilator: NBA shadow variable assignment elimination // // Code available from: https://verilator.org // @@ -13,14 +13,48 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* -// LIFE TRANSFORMATIONS: -// Build control-flow graph with assignments and var usages -// All modules: -// Delete these -// ASSIGN(Vdly, a) -// ... {no reads or writes of a after the first write to Vdly} -// ... {no reads of a after the first write to Vdly} -// ASSIGNPOST(Vdly, tmp) +// +// Given a pair of variables 'd' and 'q', where 'd' is the shadow variable +// created by V3Delayed using the ShadowVar scheme. Attemp to turn this code: +// +// ... reads of 'q' ok here +// X1: d = q; // First and complete write of 'd' (the pre-scheduled NBA initial assignment) +// ... reads of 'q' ok here +// d = foo; // Second assignment to 'd' +// ... no reads of 'q' here +// X2: q = d; // Only and complete write of 'q', only read of 'd' (the post-scheduled NBA commit) +// ... reads of 'q' ok here +// +// into: +// +// X1: q = q; +// ... +// q = foo; +// ... +// X2: q = q; +// +// by replacing 'd' with 'q'. This then allows deletion of 'd' and the two assignments. +// +// More formally, with the non-sequential mtasks graph, we must prove all of these: +// 1) No reads of 'd' anywhere except for the ASSIGNPOST itself +// 2) No write of 'q' anywhere except for the ASSIGNPOST itself +// 3) The first write of 'd' is complete (writes all bits) +// 4) Every read of 'q' either falls before the second write of 'd', or after only read of 'd' +// +// Notes: +// +// While these rules could be applied to any variables, not just the NBA +// shadow variables. **Proving** that no reads of 'q' happen after the second +// assignment of 'd' is difficult due to the presence of loops (the whole +// eval_nba is inside a loop), virtual methods and other dynamic executions. +// For the NBA shadow variables, we can compute this safely as their use +// is understood as we schedule their first and last assignments specially. +// +// Constraint 2 could be relaxed to "no write of 'q' before the only read of +// 'd', however we only have one write of 'q', created in V3Delayed, so +// trying harder would just be a code coverage hole today. +// +// Constraint 3 should always hold with V3Delayed, will check assert it. // //************************************************************************* @@ -32,236 +66,149 @@ #include "V3GraphPathChecker.h" #include "V3Stats.h" -#include // for std::unique_ptr -> auto_ptr or unique_ptr +#include +#include #include VL_DEFINE_DEBUG_FUNCTIONS; -//###################################################################### -// LifePost class functions - -class LifePostElimVisitor final : public VNVisitor { - bool m_tracingCall = false; // Iterating into a CCall to a CFunc - - // NODE STATE - // INPUT: - // AstVarScope::user4p() -> AstVarScope*, If set, replace this - // varscope with specified new one - - // STATE - - // VISITORS - void visit(AstVarRef* nodep) override { - const AstVarScope* const vscp = nodep->varScopep(); - UASSERT_OBJ(vscp, nodep, "Scope not assigned"); - if (AstVarScope* const newvscp = reinterpret_cast(vscp->user4p())) { - UINFO(9, " Replace " << nodep << " to " << newvscp); - AstVarRef* const newrefp = new AstVarRef{nodep->fileline(), newvscp, nodep->access()}; - nodep->replaceWith(newrefp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - } - } - void visit(AstNodeModule* nodep) override { - // Only track the top scopes, not lower level functions - if (nodep->isTop()) iterateChildren(nodep); - } - void visit(AstNodeCCall* nodep) override { - iterateChildren(nodep); - if (!nodep->funcp()->entryPoint()) { - // Enter the function and trace it - m_tracingCall = true; - iterate(nodep->funcp()); - } - } - void visit(AstExecGraph* nodep) override { - // Can just iterate across the MTask bodies in any order. Order - // isn't important for LifePostElimVisitor's simple substitution. - iterateChildren(nodep); - } - void visit(AstCFunc* nodep) override { - if (!m_tracingCall && !nodep->entryPoint()) return; - m_tracingCall = false; - iterateChildren(nodep); - } - void visit(AstVar*) override {} // Don't want varrefs under it - void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit LifePostElimVisitor(AstTopScope* nodep) { iterate(nodep); } - ~LifePostElimVisitor() override = default; -}; - -//###################################################################### -// Location within the execution graph, identified by an mtask -// and a sequence number within the mtask: - -struct LifeLocation final { - const ExecMTask* mtaskp = nullptr; - uint32_t sequence = 0; - -public: - LifeLocation() = default; - LifeLocation(const ExecMTask* mtaskp_, uint32_t sequence_) - : mtaskp{mtaskp_} - , sequence{sequence_} {} - bool operator<(const LifeLocation& b) const { - const unsigned a_id = mtaskp ? mtaskp->id() : 0; - const unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0; - if (a_id < b_id) return true; - if (b_id < a_id) return false; - return sequence < b.sequence; - } -}; - -struct LifePostLocation final { - LifeLocation loc; - AstAssignPost* nodep = nullptr; - LifePostLocation() = default; - LifePostLocation(LifeLocation loc_, AstAssignPost* nodep_) - : loc{loc_} - , nodep{nodep_} {} -}; - //###################################################################### // LifePost delay elimination -class LifePostDlyVisitor final : public VNVisitor { +class LifePostDlyVisitor final : public VNVisitorConst { + // TYPES + + // Location of AstNode within the program + template + class Location final { + template + friend class Location; + + T_Node* m_nodep; // The AstNode being recorded + const AstExecGraph* m_egraphp; // AstExecTraph location is under, if any + const ExecMTask* m_mtaskp; // The ExecMTask location is under, if any + uint32_t m_seqNum; // Location counter + + public: + Location(T_Node* nodep, const AstExecGraph* egp, const ExecMTask* mtaskp, uint32_t seqNum) + : m_nodep{nodep} + , m_egraphp{egp} + , m_mtaskp{mtaskp} + , m_seqNum{seqNum} {} + Location() = delete; + + T_Node* const& nodep() const { return m_nodep; } + + // "is before" - Note: Equality (concurrency) is possible iff they are independent mtasks! + template + bool operator<(const Location& that) const { + // If they are in different mtasks under the same graph, check for a path in the graph + if (m_egraphp && m_egraphp == that.m_egraphp && m_mtaskp != that.m_mtaskp) { + GraphPathChecker* const checkerp = m_egraphp->user1u().to(); + return checkerp->pathExistsFrom(m_mtaskp, that.m_mtaskp); + } + // Otherwise the sequence numbers work (one/both outside graph, or both in same mtask) + return m_seqNum < that.m_seqNum; + } + }; + // NODE STATE // AstVarScope::user1() -> bool: referenced outside _eval__nba - // AstVarScope::user4() -> AstVarScope*: Passed to LifePostElim to substitute this var + // AstVarScope::user4() -> AstVarScope*: Replacement variable + // AstExecGraph::user1p() -> GraphPathChecker*: path checker for this AstExecGraph const VNUser1InUse m_inuser1; const VNUser4InUse m_inuser4; // STATE uint32_t m_sequence = 0; // Sequence number of assigns/varrefs, - // // local to the current MTask. - const ExecMTask* m_execMTaskp = nullptr; // Current ExecMTask being processed, - // // or nullptr for serial code. + const AstExecGraph* m_execGraphp = nullptr; // Current AstExecGraph being processed (or null) + const ExecMTask* m_execMTaskp = nullptr; // Current ExecMTask being processed (or null) VDouble0 m_statAssnDel; // Statistic tracking - bool m_tracingCall = false; // Currently tracing a CCall to a CFunc - - // Map each varscope to one or more locations where it's accessed. - // These maps will not include any ASSIGNPOST accesses: - using LocMap = std::unordered_map>; + // Maps from Varscope to all their reads and writes + using LocMap = std::unordered_map>>; LocMap m_reads; // VarScope read locations LocMap m_writes; // VarScope write locations - - // Map each dly var to its AstAssignPost* node and the location thereof - std::unordered_map m_assignposts; - - V3Graph* m_mtasksGraphp = nullptr; // Mtask tracking graph - std::unique_ptr m_checker; - + std::vector> m_assigns; // Assignments considered for removal + std::vector> m_checkers; // Storage for exec graph checkers const AstCFunc* const m_evalNbap; // The _eval__nba function bool m_inEvalNba = false; // Traversing under _eval__nba // METHODS - bool before(const LifeLocation& a, const LifeLocation& b) { - if (a.mtaskp == b.mtaskp) return a.sequence < b.sequence; - return m_checker->pathExistsFrom(a.mtaskp, b.mtaskp); - } - bool outsideCriticalArea(LifeLocation loc, const std::set& dlyVarAssigns, - LifeLocation assignPostLoc) { - // If loc is before all of dlyVarAssigns, return true. - // ("Before" means certain to be ordered before them at execution time.) - // If assignPostLoc is before loc, return true. - // - // Otherwise, loc could fall in the "critical" area where the - // substitution affects the result of the operation at loc, so - // return false. - if (!loc.mtaskp && assignPostLoc.mtaskp) { - // This is threaded mode; 'loc' is something that happens at - // initial/settle time, or perhaps in _eval() but outside of - // the mtask graph. - // In either case, it's not in the critical area. - return true; - } - if (before(assignPostLoc, loc)) return true; - for (const auto& i : dlyVarAssigns) { - if (!before(loc, i)) return false; - } - return true; - } void squashAssignposts() { - for (auto& pair : m_assignposts) { - // If referenced external to _eval__nba, don't optimize - if (pair.first->user1()) continue; + for (const Location& assign : m_assigns) { + AstVarScope* const dVscp = VN_AS(assign.nodep()->rhsp(), VarRef)->varScopep(); + AstVarScope* const qVscp = VN_AS(assign.nodep()->lhsp(), VarRef)->varScopep(); - const LifePostLocation* const app = &pair.second; - const AstVarRef* const lhsp = VN_AS(app->nodep->lhsp(), VarRef); // original var - const AstVarRef* const rhsp = VN_AS(app->nodep->rhsp(), VarRef); // dly var - AstVarScope* const dlyVarp = rhsp->varScopep(); - AstVarScope* const origVarp = lhsp->varScopep(); + // We are considering deleting 'y', don't do it if referenced external to _eval__nba + if (dVscp->user1()) continue; - // Scrunch these: - // X1: __Vdly__q = __PVT__clk_clocks; - // ... {no reads or writes of __PVT__q after the first write to __Vdly__q} - // ... {no reads of __Vdly__q after the first write to __Vdly__q} - // X2: __PVT__q = __Vdly__q; - // - // Into just this: - // X1: __PVT__q = __PVT__clk_clocks; - // X2: (nothing) + const std::vector>& dWrites = m_writes[dVscp]; + UASSERT_OBJ(!dWrites.empty(), dVscp, "NBA shadow variable read but never written"); - // More formally, with the non-sequential mtasks graph, we must - // prove all of these before doing the scrunch: - // 1) No reads of the dly var anywhere except for the ASSIGNPOST - // 2) Every read of the original var either falls before each of - // the dly var's assignments, or after the ASSIGNPOST. - // 3) Every write of the original var either falls before each of - // the dly var's assignments, or after the ASSIGNPOST. + // *** See file header for requirements *** - const std::set& dlyVarAssigns = m_writes[dlyVarp]; - // Proof (1) - const std::set& dlyVarReads = m_reads[dlyVarp]; - if (!dlyVarReads.empty()) { - continue; // do not scrunch, go to next LifePostLocation + // Proof (1) - Only read is on the RHS of this assignment + if (m_reads[dVscp].size() > 1) continue; + + // Proof (2) - Only write is on the LHS of this assignment + if (m_writes[qVscp].size() > 1) continue; + + // Proof (3) - Should always hold + UASSERT_OBJ(VN_IS(dWrites.at(0).nodep()->backp(), NodeAssign), dVscp, + "Partial first write to NBA shadow variable"); + + // Proof (4) + if (dWrites.size() > 1) { + // V3Order always serializes writes so they cannot be concurrent + UASSERT_OBJ(dWrites[0] < dWrites[1], dVscp, "Concurrent writes"); + const bool qRdOK = [&]() { + for (const Location& qRead : m_reads[qVscp]) { + if (assign < qRead) continue; + // Check from 2nd write of 'd' + for (size_t i = 1; i < dWrites.size(); ++i) { + if (qRead < dWrites[i]) continue; + return false; + } + } + return true; + }(); + if (!qRdOK) continue; } - // Proof (2) - bool canScrunch = true; - const std::set& origVarReads = m_reads[origVarp]; - for (std::set::iterator rdLocIt = origVarReads.begin(); - rdLocIt != origVarReads.end(); ++rdLocIt) { - if (!outsideCriticalArea(*rdLocIt, dlyVarAssigns, app->loc)) { - canScrunch = false; - break; - } - } - if (!canScrunch) continue; - - // Proof (3) - const std::set& origVarWrites = m_writes[origVarp]; - for (std::set::iterator wrLocIt = origVarWrites.begin(); - wrLocIt != origVarWrites.end(); ++wrLocIt) { - if (!outsideCriticalArea(*wrLocIt, dlyVarAssigns, app->loc)) { - canScrunch = false; - break; - } - } - if (!canScrunch) continue; - - // Delete and mark so LifePostElimVisitor will get it - UINFO(4, " DELETE " << app->nodep); - dlyVarp->user4p(origVarp); - VL_DO_DANGLING(app->nodep->unlinkFrBack()->deleteTree(), app->nodep); + // Mark variable for replacement + dVscp->user4p(qVscp); + // Delete assignment + UINFO(4, " DELETE " << assign.nodep()); + VL_DO_DANGLING(assign.nodep()->unlinkFrBack()->deleteTree(), assign.nodep()); ++m_statAssnDel; } } + // Trace code in the given function + void trace(AstCFunc* nodep) { + VL_RESTORER(m_inEvalNba); + if (nodep == m_evalNbap) m_inEvalNba = true; + iterateChildrenConst(nodep); + } + // VISITORS - void visit(AstTopScope* nodep) override { + void visit(AstNetlist* nodep) override { // First, build maps of every location (mtask and sequence // within the mtask) where each varscope is read, and written. - iterateChildren(nodep); + iterateChildrenConst(nodep); - if (v3Global.opt.mtasks()) { - UASSERT_OBJ(m_mtasksGraphp, nodep, "Should have initted m_mtasksGraphp by now"); - m_checker.reset(new GraphPathChecker{m_mtasksGraphp}); - } else { - UASSERT_OBJ(!m_mtasksGraphp, nodep, - "Did not expect any m_mtasksGraphp in serial mode"); + // We need to be able to pick up the first write of each variable. + // V3Order serializes all writes, and we trace AstExecGraph in + // dependency order, so the first one we encouner during tracing should + // always be the one. It's somewhat expensive to assert so only with debugCheck(). + if (v3Global.opt.debugCheck()) { + for (auto& pair : m_writes) { + const std::vector>& writes = pair.second; + const Location& first = writes[0]; + for (size_t i = 1; i < writes.size(); ++i) { + UASSERT_OBJ(first < writes[i], pair.first, "First write is not the first"); + } + } } // Find all assignposts. Determine which ones can be @@ -270,10 +217,21 @@ class LifePostDlyVisitor final : public VNVisitor { // variables. squashAssignposts(); - // Replace any node4p varscopes with the new scope - LifePostElimVisitor{nodep}; + // Apply replacements + nodep->foreach([](AstVarRef* nodep) { + const AstVarScope* const vscp = nodep->varScopep(); + AstVarScope* const replacementp = VN_AS(vscp->user4p(), VarScope); + if (!replacementp) return; + UINFO(9, " Replace " << nodep << " target " << vscp << " with " << replacementp); + nodep->varScopep(replacementp); + nodep->varp(replacementp->varp()); + }); } + void visit(AstVarRef* nodep) override { + // We only try to optimize NBA shadow variables + if (!nodep->varScopep()->optimizeLifePost()) return; + // Mark variables referenced outside _eval__nba if (!m_inEvalNba) { nodep->varScopep()->user1(true); @@ -284,72 +242,69 @@ class LifePostDlyVisitor final : public VNVisitor { const AstVarScope* const vscp = nodep->varScopep(); UASSERT_OBJ(vscp, nodep, "Scope not assigned"); - const LifeLocation loc(m_execMTaskp, ++m_sequence); - if (nodep->access().isWriteOrRW()) m_writes[vscp].insert(loc); - if (nodep->access().isReadOrRW()) m_reads[vscp].insert(loc); - } - void visit(AstAssignPre* nodep) override { - // Do not record varrefs within assign pre. - // - // The pre-assignment into the dly var should not count as its - // first write; we only want to consider reads and writes that - // would still happen if the dly var were eliminated. - if (!m_inEvalNba) iterateChildren(nodep); - } - void visit(AstAssignPost* nodep) override { - // Don't record ASSIGNPOST in the read/write maps, record them in a - // separate map - if (const AstVarRef* const rhsp = VN_CAST(nodep->rhsp(), VarRef)) { - // rhsp is the dly var - const AstVarScope* const dlyVarp = rhsp->varScopep(); - UASSERT_OBJ(m_assignposts.find(dlyVarp) == m_assignposts.end(), nodep, - "LifePostLocation attempted duplicate dlyvar map addition"); - const LifeLocation loc(m_execMTaskp, ++m_sequence); - m_assignposts[dlyVarp] = LifePostLocation(loc, nodep); + ++m_sequence; + if (nodep->access().isWriteOrRW()) { + m_writes[vscp].emplace_back(nodep, m_execGraphp, m_execMTaskp, m_sequence); + } + if (nodep->access().isReadOrRW()) { + m_reads[vscp].emplace_back(nodep, m_execGraphp, m_execMTaskp, m_sequence); } } - void visit(AstNodeModule* nodep) override { - // Only track the top scopes, not lower level functions - if (nodep->isTop()) iterateChildren(nodep); + void visit(AstNodeAssign* nodep) override { + // Record RHS before assignment + iterateConst(nodep->rhsp()); + // If a straight assignment between NBA variables, consider for removal + if (const AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef)) { + if (const AstVarRef* const rhsp = VN_CAST(nodep->rhsp(), VarRef)) { + if (lhsp->varScopep()->optimizeLifePost() // + && rhsp->varScopep()->optimizeLifePost()) { + m_assigns.emplace_back(nodep, m_execGraphp, m_execMTaskp, ++m_sequence); + } + } + } + // Record LHS after assignment + iterateConst(nodep->lhsp()); } void visit(AstNodeCCall* nodep) override { - iterateChildren(nodep); - if (!nodep->funcp()->entryPoint()) { - // Enter the function and trace it - m_tracingCall = true; - iterate(nodep->funcp()); - } + iterateChildrenConst(nodep); + // Entry points are roots of the trace, no need to do it here + if (nodep->funcp()->entryPoint()) return; + // Trace cellee + trace(nodep->funcp()); } void visit(AstExecGraph* nodep) override { - // Treat the ExecGraph like a call to each mtask body - VL_RESTORER(m_execMTaskp); - if (m_inEvalNba) { - UASSERT_OBJ(!m_mtasksGraphp, nodep, "Cannot handle more than one AstExecGraph"); - m_mtasksGraphp = nodep->depGraphp(); - } + UASSERT_OBJ(!m_execGraphp, nodep, "Nested AstExecGraph"); + VL_RESTORER(m_execGraphp); + m_execGraphp = nodep; + + // Set up the path checker for this graph + UASSERT_OBJ(!nodep->user1p(), nodep, "AstExecGraph visited twice"); + m_checkers.emplace_back(new GraphPathChecker{nodep->depGraphp()}); + nodep->user1p(m_checkers.back().get()); + + // Trace each mtask body. Note: the vertices are in topological order, + // and we do not reset m_sequence, so a lower sequence number does + // guarantee a node is not earlier than a higher sequence number, but + // might still be concurrent. for (V3GraphVertex& mtaskVtx : nodep->depGraphp()->vertices()) { const ExecMTask* const mtaskp = mtaskVtx.as(); + VL_RESTORER(m_execMTaskp); m_execMTaskp = mtaskp; - m_sequence = 0; - iterate(mtaskp->bodyp()); + iterateConst(mtaskp->bodyp()); } } void visit(AstCFunc* nodep) override { - if (!m_tracingCall && !nodep->entryPoint()) return; - VL_RESTORER(m_inEvalNba); - if (nodep == m_evalNbap) m_inEvalNba = true; - m_tracingCall = false; - iterateChildren(nodep); + // Start a trace from each entry point + if (nodep->entryPoint()) trace(nodep); } //----- - void visit(AstVar*) override {} // Don't want varrefs under it - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS explicit LifePostDlyVisitor(AstNetlist* netlistp) : m_evalNbap{netlistp->evalNbap()} { - iterate(netlistp); + iterateConst(netlistp); } ~LifePostDlyVisitor() override { V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel); @@ -361,7 +316,6 @@ public: void V3LifePost::lifepostAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - // Mark redundant AssignPost { LifePostDlyVisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("life_post", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index d4ef3a5e8..b1c5125ff 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -169,6 +169,9 @@ private: } void visit(AstAssignPost* nodep) override { if (writesToVirtIface(nodep)) { + // Not sure if needed, but be paranoid to match previous behavior as didn't optimize + // before .. + nodep->foreach([](AstVarRef* refp) { refp->varScopep()->optimizeLifePost(false); }); // Convert to always, as we have to assign the trigger var FileLine* const flp = nodep->fileline(); AstAlwaysPost* const postp = new AstAlwaysPost{flp}; From 0bf9fc270f143292020e3189035dbc23ab1c8475 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 09:27:59 +0100 Subject: [PATCH 190/252] Iternals: Remove AstAssignPre/AstAssignPost (#6307) Replace with AstAlwaysPre/AstAlwaysPost with AstAssign under them. Step towards #6280 --- docs/internals.rst | 2 +- src/V3AstNodeOther.h | 8 +++++++ src/V3AstNodeStmt.h | 22 ------------------ src/V3Delayed.cpp | 27 +++++++++++++--------- src/V3EmitV.cpp | 10 ++++++++ src/V3GraphTest.cpp | 4 ++-- src/V3Order.cpp | 6 ++--- src/V3OrderGraph.h | 6 ++--- src/V3OrderGraphBuilder.cpp | 28 +++++++++-------------- src/V3Sched.cpp | 3 ++- src/V3Sched.h | 2 +- src/V3SchedPartition.cpp | 11 ++++----- src/V3SchedVirtIface.cpp | 21 +++++------------ src/V3Width.cpp | 1 - test_regress/t/t_json_only_debugcheck.out | 8 +++---- test_regress/t/t_xml_debugcheck.out | 16 ++++++------- 16 files changed, 80 insertions(+), 95 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 99295c218..3ce56b65d 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -450,7 +450,7 @@ as defined in the standard: - All other logic is assigned to the 'nba' region. For completeness, note that a subset of the 'act' region logic, specifically, -the logic related to the pre-assignments of NBA updates (i.e., AstAssignPre +the logic related to the pre-assignments of NBA updates (i.e., AstAlwaysPre nodes), is handled separately, but is executed as part of the 'act' region. Also note that all logic representing the committing of an NBA (i.e., Ast*Post) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 821e2bf0c..a3786d519 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2551,6 +2551,14 @@ public: : ASTGEN_SUPER_AlwaysPostponed(fl, stmtsp) {} ASTGEN_MEMBERS_AstAlwaysPostponed; }; +class AstAlwaysPre final : public AstNodeProcedure { + // Like always but 'pre' scheduled, e.g. for implementing NBAs + +public: + explicit AstAlwaysPre(FileLine* fl) + : ASTGEN_SUPER_AlwaysPre(fl, nullptr) {} + ASTGEN_MEMBERS_AstAlwaysPre; +}; class AstAlwaysReactive final : public AstNodeProcedure { // Like always but Reactive scheduling region // @astgen op1 := sentreep : Optional[AstSenTree] // Sensitivity list, removed in V3Active diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 4a3783276..154578d43 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -1162,28 +1162,6 @@ public: } bool brokeLhsMustBeLvalue() const override { return true; } }; -class AstAssignPost final : public AstNodeAssign { - // Like Assign, but predelayed assignment requiring special order handling -public: - AstAssignPost(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignPost(fl, lhsp, rhsp) {} - ASTGEN_MEMBERS_AstAssignPost; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignPost{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; -class AstAssignPre final : public AstNodeAssign { - // Like Assign, but predelayed assignment requiring special order handling -public: - AstAssignPre(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) - : ASTGEN_SUPER_AssignPre(fl, lhsp, rhsp) {} - ASTGEN_MEMBERS_AstAssignPre; - AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { - return new AstAssignPre{fileline(), lhsp, rhsp}; - } - bool brokeLhsMustBeLvalue() const override { return true; } -}; class AstAssignVarScope final : public AstNodeAssign { // Assign two VarScopes to each other public: diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 463d9fe8a..a11d01410 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -593,11 +593,15 @@ class DelayedVisitor final : public VNVisitor { activep->senTreeStorep(vscpInfo.senTreep()); scopep->addBlocksp(activep); // Add 'Pre' scheduled 'shadowVariable = originalVariable' assignment - activep->addStmtsp(new AstAssignPre{flp, new AstVarRef{flp, shadowVscp, VAccess::WRITE}, - new AstVarRef{flp, vscp, VAccess::READ}}); + AstAlwaysPre* const prep = new AstAlwaysPre{flp}; + activep->addStmtsp(prep); + prep->addStmtsp(new AstAssign{flp, new AstVarRef{flp, shadowVscp, VAccess::WRITE}, + new AstVarRef{flp, vscp, VAccess::READ}}); // Add 'Post' scheduled 'originalVariable = shadowVariable' assignment - activep->addStmtsp(new AstAssignPost{flp, new AstVarRef{flp, vscp, VAccess::WRITE}, - new AstVarRef{flp, shadowVscp, VAccess::READ}}); + AstAlwaysPost* const postp = new AstAlwaysPost{flp}; + activep->addStmtsp(postp); + postp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, vscp, VAccess::WRITE}, + new AstVarRef{flp, shadowVscp, VAccess::READ}}); } void convertSchemeShadowVar(AstAssignDly* nodep, AstVarScope* vscp, VarScopeInfo& vscpInfo) { UASSERT_OBJ(vscpInfo.m_scheme == Scheme::ShadowVar, vscp, "Inconsistent NBA scheme"); @@ -735,9 +739,10 @@ class DelayedVisitor final : public VNVisitor { new AstAssign{flp, new AstVarRef{flp, flagVscp, VAccess::WRITE}, new AstConst{flp, AstConst::BitTrue{}}}); // Add the 'Pre' scheduled reset for the flag - vscpInfo.flagSharedKit().activep->addStmtsp( - new AstAssignPre{flp, new AstVarRef{flp, flagVscp, VAccess::WRITE}, - new AstConst{flp, AstConst::BitFalse{}}}); + AstAlwaysPre* const prep = new AstAlwaysPre{flp}; + vscpInfo.flagSharedKit().activep->addStmtsp(prep); + prep->addStmtsp(new AstAssign{flp, new AstVarRef{flp, flagVscp, VAccess::WRITE}, + new AstConst{flp, AstConst::BitFalse{}}}); // Add the 'Post' scheduled commit AstIf* const ifp = new AstIf{flp, new AstVarRef{flp, flagVscp, VAccess::READ}}; vscpInfo.flagSharedKit().postp->addStmtsp(ifp); @@ -1164,8 +1169,9 @@ class DelayedVisitor final : public VNVisitor { return new AstVarRef{flp, dlyvscp, access}; }; - AstAssignPre* const prep = new AstAssignPre{flp, dlyRef(VAccess::WRITE), - new AstConst{flp, AstConst::BitFalse{}}}; + AstAlwaysPre* const prep = new AstAlwaysPre{flp}; + prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE), + new AstConst{flp, AstConst::BitFalse{}}}); AstAlwaysPost* const postp = new AstAlwaysPost{flp}; { AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)}; @@ -1295,8 +1301,7 @@ class DelayedVisitor final : public VNVisitor { } // Pre/Post logic are created here and their content need no further changes, so ignore. - void visit(AstAssignPre*) override {} - void visit(AstAssignPost*) override {} + void visit(AstAlwaysPre*) override {} void visit(AstAlwaysPost*) override {} //-------------------- diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 1990c54c9..5608c19d9 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -132,6 +132,16 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { iterateAndNextConstNull(nodep->stmtsp()); putqs(nodep, "end\n"); } + void visit(AstAlwaysPre* nodep) override { + putfs(nodep, "always /* PRE */ begin\n"); + iterateAndNextConstNull(nodep->stmtsp()); + putqs(nodep, "end\n"); + } + void visit(AstAlwaysPost* nodep) override { + putfs(nodep, "always /* POST */ begin\n"); + iterateAndNextConstNull(nodep->stmtsp()); + putqs(nodep, "end\n"); + } void visit(AstAlwaysPublic* nodep) override { putfs(nodep, "/*verilator public_flat_rw "); if (m_sentreep) { diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index 6cf2498e7..1c4cab516 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -188,7 +188,7 @@ public: V3GraphTestVertex* const posedge = n = new V3GraphTestVertex{gp, "*posedge clk*"}; { new V3GraphEdge{gp, clk, n, 2}; } - // AssignPre's VarRefs on LHS: generate special BLK + // AlwaysPre's VarRefs on LHS: generate special BLK // normal: VarRefs on LHS: generate normal // underSBlock: VarRefs on RHS: consume 'pre' (required to save cutable tests) n = new V3GraphTestVertex{gp, "a_dlylogic_sensitive_vertex -// For AssignPre's +// For AlwaysPre's // Add vertex for this logic // Add edge logic_sensitive_vertex->logic_vertex // Add edge logic_consumed_var_PREVAR->logic_vertex @@ -38,11 +38,11 @@ // Add vertex for this logic // Add edge logic_sensitive_vertex->logic_vertex // Add edge logic_generated_var_PREORDER->logic_vertex -// This ensures the AssignPre gets scheduled before this logic +// This ensures the AlwaysPre gets scheduled before this logic // Add edge logic_vertex->consumed_var_PREVAR // Add edge logic_vertex->consumed_var_POSTVAR // Add edge logic_vertex->logic_generated_var (same as if comb) -// For AssignPost's +// For AlwaysPost's // Add vertex for this logic // Add edge logic_sensitive_vertex->logic_vertex // Add edge logic_consumed_var->logic_vertex (same as if comb) diff --git a/src/V3OrderGraph.h b/src/V3OrderGraph.h index 697aa2fe3..9f2386724 100644 --- a/src/V3OrderGraph.h +++ b/src/V3OrderGraph.h @@ -34,13 +34,13 @@ // roles are: // // OrderVarStdVertex: Data dependencies for combinational logic and delayed assignment -// updates (AssignPost). +// updates (AlwaysPost). // OrderVarPostVertex: Ensures all sequential logic blocks reading a signal do so before any // combinational or delayed assignments update that signal. -// OrderVarPordVertex: Ensures a _d = _q AssignPre used to implement delayed (non-blocking) +// OrderVarPordVertex: Ensures a _d = _q AlwaysPre used to implement delayed (non-blocking) // assignments is the first write of a _d, before any sequential blocks // write to that _d. -// OrderVarPreVertex: This is an optimization. Try to ensure that a _d = _q AssignPre is the +// OrderVarPreVertex: This is an optimization. Try to ensure that a _d = _q AlwaysPre is the // last read of a _q, after all reads of that _q by sequential logic. The // model is still correct if we cannot satisfy this due to other interfering // constraints. If respecting this constraint is possible, then combined diff --git a/src/V3OrderGraphBuilder.cpp b/src/V3OrderGraphBuilder.cpp index 58711906c..7080c0406 100644 --- a/src/V3OrderGraphBuilder.cpp +++ b/src/V3OrderGraphBuilder.cpp @@ -102,8 +102,8 @@ class OrderGraphBuilder final : public VNVisitor { AstSenTree* m_hybridp = nullptr; bool m_inClocked = false; // Underneath clocked AstActive - bool m_inPre = false; // Underneath AstAssignPre - bool m_inPost = false; // Underneath AstAssignPost/AstAlwaysPost + bool m_inPre = false; // Underneath AlwaysPre + bool m_inPost = false; // Underneath AstAlwaysPost std::function m_readTriggersCombLogic; // METHODS @@ -232,7 +232,7 @@ class OrderGraphBuilder final : public VNVisitor { // Add edge from produced VarPostVertex -> to producing LogicVertex OrderVarVertex* const postVxp = getVarVertex(varscp, VarVertexType::POST); m_graphp->addHardEdge(postVxp, m_logicVxp, WEIGHT_POST); - } else if (m_inPre) { // AstAssignPre + } else if (m_inPre) { // AstAlwaysPre // Add edge from producing LogicVertex -> produced VarPordVertex OrderVarVertex* const ordVxp = getVarVertex(varscp, VarVertexType::PORD); m_graphp->addHardEdge(m_logicVxp, ordVxp, WEIGHT_NORMAL); @@ -271,7 +271,7 @@ class OrderGraphBuilder final : public VNVisitor { m_graphp->addHardEdge(varVxp, m_logicVxp, WEIGHT_MEDIUM); } } else if (m_inPre) { - // AstAssignPre logic + // AstAlwaysPre logic // Add edge from consumed VarPreVertex -> to consuming LogicVertex // This one is cutable (vs the producer) as there's only one such consumer, // but may be many producers @@ -281,7 +281,7 @@ class OrderGraphBuilder final : public VNVisitor { // Sequential (clocked) logic // Add edge from consuming LogicVertex -> to consumed VarPreVertex // Generation of 'pre' because we want to indicate it should be before - // AstAssignPre + // AstAlwaysPre OrderVarVertex* const preVxp = getVarVertex(varscp, VarVertexType::PRE); m_graphp->addHardEdge(m_logicVxp, preVxp, WEIGHT_NORMAL); // Add edge from consuming LogicVertex -> to consumed VarPostVertex @@ -305,6 +305,12 @@ class OrderGraphBuilder final : public VNVisitor { void visit(AstAlways* nodep) override { // iterateLogic(nodep); } + void visit(AstAlwaysPre* nodep) override { + UASSERT_OBJ(!m_inPre, nodep, "Should not nest"); + VL_RESTORER(m_inPre); + m_inPre = true; + iterateLogic(nodep); + } void visit(AstAlwaysPost* nodep) override { UASSERT_OBJ(!m_inPost, nodep, "Should not nest"); VL_RESTORER(m_inPost); @@ -326,18 +332,6 @@ class OrderGraphBuilder final : public VNVisitor { iterateLogic(nodep); } void visit(AstAssignW* nodep) override { iterateLogic(nodep); } - void visit(AstAssignPre* nodep) override { - UASSERT_OBJ(!m_inPre, nodep, "Should not nest"); - VL_RESTORER(m_inPre); - m_inPre = true; - iterateLogic(nodep); - } - void visit(AstAssignPost* nodep) override { - UASSERT_OBJ(!m_inPost, nodep, "Should not nest"); - VL_RESTORER(m_inPost); - m_inPost = true; - iterateLogic(nodep); - } //--- Verilator concoctions void visit(AstAlwaysPublic* nodep) override { // diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index d19bebba0..8e4c4f528 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -107,12 +107,13 @@ void invertAndMergeSenTreeMap( AstSenTree* findTriggeredIface(const AstVarScope* vscp, const VirtIfaceTriggers::IfaceSensMap& vifTrigged, const VirtIfaceTriggers::IfaceMemberSensMap& vifMemberTriggered) { + UASSERT_OBJ(vscp->varp()->sensIfacep(), vscp, "Not an virtual interface trigger"); const auto ifaceIt = vifTrigged.find(vscp->varp()->sensIfacep()); if (ifaceIt != vifTrigged.end()) return ifaceIt->second; for (const auto& memberIt : vifMemberTriggered) { if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) { return memberIt.second; } } - return nullptr; + vscp->v3fatalSrc("Did not find virtual interface trigger"); } //============================================================================ diff --git a/src/V3Sched.h b/src/V3Sched.h index 1f0e0ffb1..da51a597b 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -94,7 +94,7 @@ struct LogicClasses final { // Combinational (including hybrid) logic, and clocked logic in partitioned to compute all clock // signals in the 'act' region. For details see the internals documentation. struct LogicRegions final { - LogicByScope m_pre; // AstAssignPre logic in 'act' region + LogicByScope m_pre; // AstAlwaysPre logic in 'act' region LogicByScope m_act; // 'act' region logic LogicByScope m_nba; // 'nba' region logic LogicByScope m_obs; // AstAlwaysObserved logic in 'obs' region diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 28048f098..2923e800f 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -26,7 +26,7 @@ // is then assigned to the 'act' region, and all other logic is assigned to // the 'nba' region. // -// For later practical purposes, AstAssignPre logic that would be assigned to +// For later practical purposes, AstAlwaysPre logic that would be assigned to // the 'act' region is returned separately. Nevertheless, this logic is part of // the 'act' region. // @@ -241,8 +241,7 @@ class SchedGraphBuilder final : public VNVisitor { void visit(AstAlwaysPublic* nodep) override { visitLogic(nodep); } // Pre and Post logic are handled separately - void visit(AstAssignPre* nodep) override {} - void visit(AstAssignPost* nodep) override {} + void visit(AstAlwaysPre* nodep) override {} void visit(AstAlwaysPost* nodep) override {} // LCOV_EXCL_START @@ -375,14 +374,14 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo markVars(activep); } - // AstAssignPre, AstAssignPost and AstAlwaysPost should only appear under a clocked + // AstAlwaysPre and AstAlwaysPost should only appear under a clocked // AstActive, and should be the only thing left at this point. for (const auto& pair : clockedLogic) { AstScope* const scopep = pair.first; AstActive* const activep = pair.second; for (AstNode *nodep = activep->stmtsp(), *nextp; nodep; nodep = nextp) { nextp = nodep->nextp(); - if (AstAssignPre* const logicp = VN_CAST(nodep, AssignPre)) { + if (AstAlwaysPre* const logicp = VN_CAST(nodep, AlwaysPre)) { bool toActiveRegion = false; logicp->foreach([&](const AstNodeVarRef* vrefp) { AstVarScope* const vscp = vrefp->varScopep(); @@ -398,7 +397,7 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo logicp->unlinkFrBack(); lbs.add(scopep, activep->sentreep(), logicp); } else { - UASSERT_OBJ(VN_IS(nodep, AssignPost) || VN_IS(nodep, AlwaysPost), nodep, + UASSERT_OBJ(VN_IS(nodep, AlwaysPost), nodep, "Unexpected node type " << nodep->typeName()); nodep->unlinkFrBack(); result.m_nba.add(scopep, activep->sentreep(), nodep); diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index b1c5125ff..996dc9357 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -19,7 +19,7 @@ // Each interface type written to via virtual interface, or written to normally but read via // virtual interface: // Create a trigger var for it -// Each AssignW, AssignPost: +// Each AssignW: // If it writes to a virtual interface, or to a variable read via virtual interface: // Convert to an always // Each statement: @@ -150,6 +150,11 @@ private: m_trigAssignIfacep = nullptr; VL_RESTORER(m_trigAssignMemberVarp); m_trigAssignMemberVarp = nullptr; + // Not sure if needed, but be paranoid to match previous behavior as didn't optimize + // before .. + if (VN_IS(nodep, AlwaysPost) && writesToVirtIface(nodep)) { + nodep->foreach([](AstVarRef* refp) { refp->varScopep()->optimizeLifePost(false); }); + } iterateChildren(nodep); } void visit(AstCFunc* nodep) override { @@ -167,20 +172,6 @@ private: nodep->convertToAlways(); } } - void visit(AstAssignPost* nodep) override { - if (writesToVirtIface(nodep)) { - // Not sure if needed, but be paranoid to match previous behavior as didn't optimize - // before .. - nodep->foreach([](AstVarRef* refp) { refp->varScopep()->optimizeLifePost(false); }); - // Convert to always, as we have to assign the trigger var - FileLine* const flp = nodep->fileline(); - AstAlwaysPost* const postp = new AstAlwaysPost{flp}; - nodep->replaceWith(postp); - postp->addStmtsp( - new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()}); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - } - } void visit(AstNodeIf* nodep) override { unsupportedWriteToVirtIface(nodep->condp(), "if condition"); { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 6a7fd368c..70f52ac73 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5374,7 +5374,6 @@ class WidthVisitor final : public VNVisitor { // handled in each visitor. // Then LHS sign-extends only if *RHS* is signed assertAtStatement(nodep); - // UINFOTREE(1, nodep, "", "AssignPre"); { // UINFOTREE(1, nodep, "", "assin:"); userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); diff --git a/test_regress/t/t_json_only_debugcheck.out b/test_regress/t/t_json_only_debugcheck.out index 9451c4e7f..779a57ae3 100644 --- a/test_regress/t/t_json_only_debugcheck.out +++ b/test_regress/t/t_json_only_debugcheck.out @@ -1161,14 +1161,14 @@ {"type":"VAR","name":"__Vtemp_3","addr":"(UR)","loc":"d,90:123,90:124","dtypep":"(RB)","origName":"__Vtemp_3","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ], "stmtsp": [ - {"type":"ASSIGNPRE","name":"","addr":"(VR)","loc":"d,23:17,23:20","dtypep":"(R)", + {"type":"ASSIGN","name":"","addr":"(VR)","loc":"d,23:17,23:20","dtypep":"(R)", "rhsp": [ {"type":"VARREF","name":"t.cyc","addr":"(WR)","loc":"d,23:17,23:20","dtypep":"(R)","access":"RD","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "lhsp": [ {"type":"VARREF","name":"__Vdly__t.cyc","addr":"(XR)","loc":"d,23:17,23:20","dtypep":"(R)","access":"WR","varp":"(MR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNPRE","name":"","addr":"(YR)","loc":"d,24:9,24:10","dtypep":"(AC)", + {"type":"ASSIGN","name":"","addr":"(YR)","loc":"d,24:9,24:10","dtypep":"(AC)", "rhsp": [ {"type":"VARREF","name":"t.e","addr":"(ZR)","loc":"d,24:9,24:10","dtypep":"(AC)","access":"RD","varp":"(L)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], @@ -2507,14 +2507,14 @@ ]} ]} ]}, - {"type":"ASSIGNPOST","name":"","addr":"(JKB)","loc":"d,23:17,23:20","dtypep":"(R)", + {"type":"ASSIGN","name":"","addr":"(JKB)","loc":"d,23:17,23:20","dtypep":"(R)", "rhsp": [ {"type":"VARREF","name":"__Vdly__t.cyc","addr":"(KKB)","loc":"d,23:17,23:20","dtypep":"(R)","access":"RD","varp":"(MR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "lhsp": [ {"type":"VARREF","name":"t.cyc","addr":"(LKB)","loc":"d,23:17,23:20","dtypep":"(R)","access":"WR","varp":"(Q)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNPOST","name":"","addr":"(MKB)","loc":"d,24:9,24:10","dtypep":"(AC)", + {"type":"ASSIGN","name":"","addr":"(MKB)","loc":"d,24:9,24:10","dtypep":"(AC)", "rhsp": [ {"type":"VARREF","name":"__Vdly__t.e","addr":"(NKB)","loc":"d,24:9,24:10","dtypep":"(AC)","access":"RD","varp":"(PR)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index ca34646a2..afb850422 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -711,14 +711,14 @@ - + - - + + - + @@ -1510,14 +1510,14 @@ - + - - + + - + From 6b9cc8a36e1288861345ded31e7533281c9cb69f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 11:14:17 +0100 Subject: [PATCH 191/252] Internals: Fix AstNodeCoverDecl, it is not a statement (#6280) (#6308) --- src/V3AstNodeOther.h | 89 ++++++++++++++++++++++++++++++++++++++++++++ src/V3AstNodeStmt.h | 87 ------------------------------------------- src/V3AstNodes.cpp | 2 +- 3 files changed, 90 insertions(+), 88 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index a3786d519..7a7ad5e2f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -54,6 +54,52 @@ public: bool unnamed() const { return m_unnamed; } bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); } }; +class AstNodeCoverDecl VL_NOT_FINAL : public AstNode { + // Coverage analysis point declaration + // + // [After V3CoverageJoin] Duplicate declaration to get data from instead + // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] + string m_page; // Coverage point's page tag + string m_text; // Coverage point's text + string m_hier; // Coverage point's hierarchy + int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment +public: + AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) + : AstNode(t, fl) + , m_page{page} + , m_text{comment} {} + ASTGEN_MEMBERS_AstNodeCoverDecl; + const char* broken() const override { + if (m_dataDeclp + && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing + v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); + } + return nullptr; + } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + int binNum() const { return m_binNum; } + void binNum(int flag) { m_binNum = flag; } + virtual int size() const = 0; + const string& comment() const { return m_text; } // text to insert in code + const string& page() const { return m_page; } + const string& hier() const { return m_hier; } + void hier(const string& flag) { m_hier = flag; } + void comment(const string& flag) { m_text = flag; } + bool sameNode(const AstNode* samep) const override { + const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); + return (fileline() == asamep->fileline() && hier() == asamep->hier() + && comment() == asamep->comment() && page() == asamep->page()); + } + bool isPredictOptimizable() const override { return false; } + void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } + // dataDecl nullptr means "use this one", but often you want "this" to + // indicate to get data from here + AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } + AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } +}; class AstNodeFTask VL_NOT_FINAL : public AstNode { // Output variable in functions, nullptr in tasks // @astgen op1 := fvarp : Optional[AstNode] @@ -2248,6 +2294,49 @@ public: void joinType(const VJoinType& flag) { m_joinType = flag; } }; +// === AstNodeCoverDecl === +class AstCoverOtherDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for other than toggle types of coverage + string m_linescov; + int m_offset; // Offset column numbers to uniq-ify IFs +public: + AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, + const string& linescov, int offset) + : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) + , m_linescov{linescov} + , m_offset{offset} {} + ASTGEN_MEMBERS_AstCoverOtherDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int offset() const { return m_offset; } + int size() const override { return 1; } + const string& linescov() const { return m_linescov; } + bool sameNode(const AstNode* samep) const override { + const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); + return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); + } +}; +class AstCoverToggleDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for toggle coverage + const VNumRange m_range; // Packed array range covering each toggle bit +public: + AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, + const VNumRange& range) + : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) + , m_range{range} {} + ASTGEN_MEMBERS_AstCoverToggleDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int size() const override { return m_range.elements(); } + const VNumRange& range() const { return m_range; } + bool sameNode(const AstNode* samep) const override { + const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); + return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); + } +}; + // === AstNodeFTask === class AstFunc final : public AstNodeFTask { // A function inside a module diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 154578d43..fcc1977e7 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -85,52 +85,6 @@ public: ASTGEN_MEMBERS_AstNodeCase; int instrCount() const override { return INSTR_COUNT_BRANCH; } }; -class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt { - // Coverage analysis point declaration - // - // [After V3CoverageJoin] Duplicate declaration to get data from instead - // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] - string m_page; // Coverage point's page tag - string m_text; // Coverage point's text - string m_hier; // Coverage point's hierarchy - int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment -public: - AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) - : AstNodeStmt(t, fl) - , m_page{page} - , m_text{comment} {} - ASTGEN_MEMBERS_AstNodeCoverDecl; - const char* broken() const override { - if (m_dataDeclp - && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing - v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); - } - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - int binNum() const { return m_binNum; } - void binNum(int flag) { m_binNum = flag; } - virtual int size() const = 0; - const string& comment() const { return m_text; } // text to insert in code - const string& page() const { return m_page; } - const string& hier() const { return m_hier; } - void hier(const string& flag) { m_hier = flag; } - void comment(const string& flag) { m_text = flag; } - bool sameNode(const AstNode* samep) const override { - const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); - return (fileline() == asamep->fileline() && hier() == asamep->hier() - && comment() == asamep->comment() && page() == asamep->page()); - } - bool isPredictOptimizable() const override { return false; } - void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } - // dataDecl nullptr means "use this one", but often you want "this" to - // indicate to get data from here - AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } - AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } -}; class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { // Cover or Assert // Parents: {statement list} @@ -1239,47 +1193,6 @@ public: : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} ASTGEN_MEMBERS_AstGenCase; }; -class AstCoverOtherDecl final : public AstNodeCoverDecl { - // Coverage analysis point declaration - // Used for other than toggle types of coverage - string m_linescov; - int m_offset; // Offset column numbers to uniq-ify IFs -public: - AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, - const string& linescov, int offset) - : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) - , m_linescov{linescov} - , m_offset{offset} {} - ASTGEN_MEMBERS_AstCoverOtherDecl; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int offset() const { return m_offset; } - int size() const override { return 1; } - const string& linescov() const { return m_linescov; } - bool sameNode(const AstNode* samep) const override { - const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); - return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); - } -}; -class AstCoverToggleDecl final : public AstNodeCoverDecl { - // Coverage analysis point declaration - // Used for toggle coverage - const VNumRange m_range; // Packed array range covering each toggle bit -public: - AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, - const VNumRange& range) - : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) - , m_range{range} {} - ASTGEN_MEMBERS_AstCoverToggleDecl; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int size() const override { return m_range.elements(); } - const VNumRange& range() const { return m_range; } - bool sameNode(const AstNode* samep) const override { - const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); - return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); - } -}; // === AstNodeCoverOrAssert === class AstAssert final : public AstNodeCoverOrAssert { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a4f223e32..60a87ef90 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2914,7 +2914,7 @@ void AstBegin::dumpJson(std::ostream& str) const { dumpJsonGen(str); } void AstNodeCoverDecl::dump(std::ostream& str) const { - this->AstNodeStmt::dump(str); + this->AstNode::dump(str); if (!page().empty()) str << " page=" << page(); if (this->dataDeclNullp()) { static bool s_recursing = false; From 6a67c0a0e53a62caf361585d139ab8d9ef9157bc Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 11:14:28 +0100 Subject: [PATCH 192/252] Iternals: Fix AstSplitPlaceholder, it is a statement (#6280) (#6309) --- src/V3AstNodeOther.h | 7 ------- src/V3AstNodeStmt.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 7a7ad5e2f..76e15c560 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1616,13 +1616,6 @@ public: bool hasCombo() const; // Includes a COMBO SenItem bool hasHybrid() const; // Includes a HYBRID SenItem }; -class AstSplitPlaceholder final : public AstNode { -public: - // Dummy node used within V3Split; never exists outside of V3Split. - explicit AstSplitPlaceholder(FileLine* fl) - : ASTGEN_SUPER_SplitPlaceholder(fl) {} - ASTGEN_MEMBERS_AstSplitPlaceholder; -}; class AstStrengthSpec final : public AstNode { VStrength m_s0; // Drive 0 strength VStrength m_s1; // Drive 1 strength diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index fcc1977e7..7d47b230c 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -815,6 +815,13 @@ public: ASTGEN_MEMBERS_AstSetuphold; bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstSplitPlaceholder final : public AstNodeStmt { +public: + // Dummy node used within V3Split; never exists outside of V3Split. + explicit AstSplitPlaceholder(FileLine* fl) + : ASTGEN_SUPER_SplitPlaceholder(fl) {} + ASTGEN_MEMBERS_AstSplitPlaceholder; +}; class AstStackTraceT final : public AstNodeStmt { // $stacktrace used as task public: From 636a6b8cd2c5cb361df2cf32132eb3892c71dfab Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 15:06:38 +0100 Subject: [PATCH 193/252] Optimize complex combinational logic in DFG (#6298) This patch adds DfgLogic, which is a vertex that represents a whole, arbitrarily complex combinational AstAlways or AstAssignW in the DfgGraph. Implementing this requires computing the variables live at entry to the AstAlways (variables read by the block), so there is a new ControlFlowGraph data structure and a classical data-flow analysis based live variable analysis to do that at the variable level (as opposed to bit/element level). The actual CFG construction and live variable analysis is best effort, and might fail for currently unhandled constructs or data types. This can be extended later. V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices. The DfgLogic are then subsequently synthesized into primitive operations by the new V3DfgSynthesize pass, which is a combination of the old V3DfgAstToDfg conversion and new code to handle AstAlways blocks with complex flow control. V3DfgSynthesize by default will synthesize roughly the same constructs as V3DfgAstToDfg used to handle before, plus any logic that is part of a combinational cycle within the DfgGraph. This enables breaking up these cycles, for which there are extensions to V3DfgBreakCycles in this patch as well. V3DfgSynthesize will then delete all non synthesized or non synthesizable DfgLogic vertices and the rest of the Dfg pipeline is identical, with minor changes to adjust for the changed representation. Because with this change we can now eliminate many more UNOPTFLAT, DFG has been disabled in all the tests that specifically target testing the scheduling and reporting of circular combinational logic. --- docs/gen/ex_DIDNOTCONVERGE_msg.rst | 2 +- docs/guide/exe_verilator.rst | 4 + src/CMakeLists.txt | 5 + src/Makefile_obj.in | 4 + src/V3AstNodeOther.h | 4 + src/V3Cfg.cpp | 90 + src/V3Cfg.h | 270 +++ src/V3CfgBuilder.cpp | 291 +++ src/V3CfgLiveVariables.cpp | 240 +++ src/V3Dfg.cpp | 242 ++- src/V3Dfg.h | 182 +- src/V3DfgAstToDfg.cpp | 1041 ++-------- src/V3DfgBreakCycles.cpp | 130 +- src/V3DfgContext.h | 158 +- src/V3DfgDecomposition.cpp | 1 + src/V3DfgDfgToAst.cpp | 133 +- src/V3DfgOptimizer.cpp | 8 +- src/V3DfgPasses.cpp | 41 +- src/V3DfgPasses.h | 4 + src/V3DfgPeephole.cpp | 93 +- src/V3DfgPeepholePatterns.h | 4 +- src/V3DfgRegularize.cpp | 14 +- src/V3DfgSynthesize.cpp | 1696 +++++++++++++++++ src/V3DfgVertices.h | 240 ++- src/V3EmitV.cpp | 10 +- src/V3EmitV.h | 1 + src/V3Options.cpp | 1 + src/V3Options.h | 2 + src/astgen | 3 +- test_regress/t/t_balance_cats.v | 2 - test_regress/t/t_clk_condflop.v | 2 - test_regress/t/t_comb_input_1.py | 2 +- test_regress/t/t_comb_input_2.py | 2 +- .../t/t_comb_loop_through_unpacked_array.py | 2 +- test_regress/t/t_detectarray_1.py | 2 +- test_regress/t/t_detectarray_2.py | 2 +- test_regress/t/t_detectarray_3.py | 2 +- test_regress/t/t_dfg_break_cycles.py | 7 +- test_regress/t/t_dfg_break_cycles.v | 33 + test_regress/t/t_dfg_multidriver_dfg_bad.out | 119 +- test_regress/t/t_dfg_multidriver_dfg_bad.py | 4 +- test_regress/t/t_dfg_multidriver_dfg_bad.v | 31 +- test_regress/t/t_dfg_peephole.py | 1 + test_regress/t/t_dfg_peephole.v | 20 +- test_regress/t/t_dfg_synthesis.cpp | 60 + test_regress/t/t_dfg_synthesis.py | 99 + test_regress/t/t_dfg_synthesis.v | 228 +++ test_regress/t/t_dist_docs_summary.py | 1 + test_regress/t/t_dpi_qw.py | 2 +- test_regress/t/t_func_crc.py | 2 +- test_regress/t/t_hier_block.v | 2 - test_regress/t/t_hier_block_chained.py | 5 +- .../t/t_lint_always_comb_multidriven_bad.out | 28 +- ...mb_multidriven_compile_public_flat_bad.out | 51 + ...omb_multidriven_compile_public_flat_bad.py | 2 +- test_regress/t/t_lint_didnotconverge_bad.out | 2 +- test_regress/t/t_lint_inherit.py | 2 +- test_regress/t/t_order.v | 2 - test_regress/t/t_order_clkinst.py | 2 +- test_regress/t/t_order_comboclkloop.py | 2 +- test_regress/t/t_order_comboloop.py | 2 +- test_regress/t/t_order_doubleloop.py | 2 +- test_regress/t/t_order_first.v | 4 - test_regress/t/t_order_multialways.v | 2 - test_regress/t/t_order_quad.py | 4 +- test_regress/t/t_order_wireloop.py | 4 +- test_regress/t/t_split_var_0.py | 2 +- test_regress/t/t_split_var_0.v | 3 - test_regress/t/t_split_var_1_bad.py | 4 +- test_regress/t/t_split_var_2_trace.py | 5 +- test_regress/t/t_split_var_3_wreal.py | 2 +- test_regress/t/t_split_var_4.py | 2 +- test_regress/t/t_split_var_5.py | 2 +- test_regress/t/t_timing_debug1.out | 304 ++- test_regress/t/t_timing_sched.v | 2 - test_regress/t/t_unopt_array.py | 2 +- test_regress/t/t_unopt_array_csplit.py | 5 +- test_regress/t/t_unopt_array_typedef.py | 2 +- test_regress/t/t_unopt_bound.py | 2 +- test_regress/t/t_unopt_combo.py | 2 +- test_regress/t/t_unopt_combo_bad.out | 2 +- test_regress/t/t_unopt_combo_bad.py | 2 +- test_regress/t/t_unopt_combo_isolate.py | 2 +- test_regress/t/t_unopt_combo_isolate_vlt.py | 5 +- test_regress/t/t_unopt_combo_waive.py | 2 +- .../t/t_unopt_converge_initial_run_bad.py | 2 +- test_regress/t/t_unopt_converge_ndbg_bad.py | 2 +- test_regress/t/t_unopt_converge_print_bad.py | 2 +- test_regress/t/t_unopt_converge_run_bad.py | 2 +- test_regress/t/t_unopt_converge_unopt_bad.out | 2 +- test_regress/t/t_unopt_converge_unopt_bad.py | 2 +- test_regress/t/t_unoptflat_simple_2_bad.out | 6 +- test_regress/t/t_unoptflat_simple_2_bad.py | 2 +- test_regress/t/t_unoptflat_simple_3_bad.py | 2 +- test_regress/t/t_unoptflat_simple_bad.py | 2 +- 95 files changed, 4511 insertions(+), 1518 deletions(-) create mode 100644 src/V3Cfg.cpp create mode 100644 src/V3Cfg.h create mode 100644 src/V3CfgBuilder.cpp create mode 100644 src/V3CfgLiveVariables.cpp create mode 100644 src/V3DfgSynthesize.cpp create mode 100644 test_regress/t/t_dfg_synthesis.cpp create mode 100755 test_regress/t/t_dfg_synthesis.py create mode 100644 test_regress/t/t_dfg_synthesis.v create mode 100644 test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out diff --git a/docs/gen/ex_DIDNOTCONVERGE_msg.rst b/docs/gen/ex_DIDNOTCONVERGE_msg.rst index 86af7245a..bd055184b 100644 --- a/docs/gen/ex_DIDNOTCONVERGE_msg.rst +++ b/docs/gen/ex_DIDNOTCONVERGE_msg.rst @@ -1,5 +1,5 @@ .. comment: generated by t_lint_didnotconverge_bad .. code-block:: - -V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a) + -V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] b) %Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 945d944d3..58e0e7839 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -564,6 +564,10 @@ Summary: Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be replaced with the specified environment variable. +.. option:: -fdfg-synthesize-all + + Rarely needed. Attempt to synthesize all combinational logic in DFG. + .. option:: -FI Force include of the specified C++ header file. All generated C++ files diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd7b97654..ccb25ffef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ set(HEADERS V3CUse.h V3Case.h V3Cast.h + V3Cfg.h V3Class.h V3Clean.h V3Clock.h @@ -213,6 +214,9 @@ set(COMMON_SOURCES V3CUse.cpp V3Case.cpp V3Cast.cpp + V3Cfg.cpp + V3CfgBuilder.cpp + V3CfgLiveVariables.cpp V3Class.cpp V3Clean.cpp V3Clock.cpp @@ -238,6 +242,7 @@ set(COMMON_SOURCES V3DfgPasses.cpp V3DfgPeephole.cpp V3DfgRegularize.cpp + V3DfgSynthesize.cpp V3DiagSarif.cpp V3DupFinder.cpp V3EmitCBase.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index ed9767907..98d06b6f6 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -226,6 +226,9 @@ RAW_OBJS_PCH_ASTNOMT = \ V3CUse.o \ V3Case.o \ V3Cast.o \ + V3Cfg.o \ + V3CfgBuilder.o \ + V3CfgLiveVariables.o \ V3Class.o \ V3Clean.o \ V3Clock.o \ @@ -249,6 +252,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3DfgPasses.o \ V3DfgPeephole.o \ V3DfgRegularize.o \ + V3DfgSynthesize.o \ V3DiagSarif.o \ V3DupFinder.o \ V3EmitCMain.o \ diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 76e15c560..bf18a7049 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1873,6 +1873,7 @@ class AstVar final : public AstNode { bool m_ignorePostRead : 1; // Ignore reads in 'Post' blocks during ordering bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations) + bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing void init() { m_ansi = false; @@ -1921,6 +1922,7 @@ class AstVar final : public AstNode { m_ignorePostRead = false; m_ignorePostWrite = false; m_ignoreSchedWrite = false; + m_dfgMultidriven = false; m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN; } @@ -2084,6 +2086,8 @@ public: void setIgnorePostWrite() { m_ignorePostWrite = true; } bool ignoreSchedWrite() const { return m_ignoreSchedWrite; } void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; } + bool dfgMultidriven() const { return m_dfgMultidriven; } + void setDfgMultidriven() { m_dfgMultidriven = true; } // METHODS void name(const string& name) override { m_name = name; } diff --git a/src/V3Cfg.cpp b/src/V3Cfg.cpp new file mode 100644 index 000000000..54ec09ae8 --- /dev/null +++ b/src/V3Cfg.cpp @@ -0,0 +1,90 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Control flow graph (CFG) implementation +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Control flow graph (CFG) implementation +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Cfg.h" + +#include "V3Ast.h" +#include "V3EmitV.h" + +VL_DEFINE_DEBUG_FUNCTIONS; + +//###################################################################### +// ControlFlowGraph method definitions + +bool ControlFlowGraph::containsLoop() const { + for (const V3GraphVertex& vtx : vertices()) { + const BasicBlock& current = static_cast(vtx); + for (const V3GraphEdge& edge : current.outEdges()) { + const BasicBlock& successor = *static_cast(edge.top()); + // IDs are the reverse post-order numbering, so easy to check for a back-edge + if (successor.id() < current.id()) return true; + } + } + return false; +} + +//###################################################################### +// BasicBlock method definitions + +std::string BasicBlock::name() const { + std::stringstream ss; + ss << "BB " + std::to_string(id()) + ":\n"; + for (AstNode* nodep : m_stmtps) { + if (const AstIf* const ifp = VN_CAST(nodep, If)) { + ss << "if ("; + V3EmitV::debugVerilogForTree(ifp->condp(), ss); + ss << ") ..."; + } else if (const AstWhile* const whilep = VN_CAST(nodep, While)) { + ss << "while ("; + V3EmitV::debugVerilogForTree(whilep->condp(), ss); + ss << ") ..."; + } else { + V3EmitV::debugVerilogForTree(nodep, ss); + } + } + std::string text = VString::replaceSubstr(ss.str(), "\n", "\\l "); + if (inEmpty()) text = "**ENTER**\n" + text; + if (outEmpty()) text = text + "\n**EXIT**"; + return text; +} +std::string BasicBlock::dotShape() const { return "rect"; } +std::string BasicBlock::dotRank() const { + if (inEmpty()) return "source"; + if (outEmpty()) return "sink"; + return ""; +} + +//###################################################################### +// ControlFlowEdge method definitions + +std::string ControlFlowEdge::dotLabel() const { + std::string label = "E" + std::to_string(id()); + const BasicBlock& source = *fromp()->as(); + const ControlFlowEdge* const untknp = source.untknEdgep(); + if (this == untknp) { + label += " / F"; + } else if (untknp) { + label += " / T"; + } + return label; +} diff --git a/src/V3Cfg.h b/src/V3Cfg.h new file mode 100644 index 000000000..2eba9514e --- /dev/null +++ b/src/V3Cfg.h @@ -0,0 +1,270 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Control flow graph (CFG) +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Control flow graph (CFG) and related data structures. +// +// A control flow graph is a directed graph with with basic blocks as +// vertices connected by control flow edges. A basic block is a sequence +// of statements that are always executed as a unit (there are no branches +// targeting the middle of a basic block). The last statement in a basic +// block is called the terminator statemet. The terminator statements are +// the control flow transfer statements (branches) in the program. In this +// implementation, an unconditoinal jump (goto) is implicit and not stored +// in the basic block. A consequence of the implicit jump is that a basic +// block might be empty (not contain any statements). Conditional branches +// are represented by storing the corresponding conditional AstNodeStmt as +// the last statement in the basic block. Most importantly, only the actual +// condition check (e.g.: the 'condp' part of an AstIf) and branch is +// executed in the host basic block, but not any of the body statements of +// the conditoinal. The control flow graph has 2 unique basic blocks. The +// 'enter' block is the unique entry point of the represented program. It +// has no predecessors and itself dominates all other basic blocks. The +// 'exit' block is the unique exit point. It has no successors, and itself +// post-dominates all other basic blocks. +// +// The current implementation is designed to be immutable after +// construction. This can be relaxed in the future if necessary, however +// it is unlikely to be necessary as we cannot as of now convert a control +// flow graph back to Ast or any other form of output. We can also only +// represent 2-way conditionals, therefore all basic blocks have up to 2 +// successors. The exit block has 0, blocks terminated by an unconditional +// implicit jump have exactly 1, and blocks terminated by a conditional +// branch have exactly 2 successors. +// +// Basic blocks have a unique ID, these are assigned based on the reverse +// post-ordering of the basic blocks within the control flow graph, and +// therefore they define a topological ordering of the basic blocks. The +// basic blocks within the graph are stored in this order. This means that +// in control flow graphs without back edges (loops), a basic block is +// always stored after its predecessors. +// +//************************************************************************* + +#ifndef VERILATOR_V3CFG_H_ +#define VERILATOR_V3CFG_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" +#include "V3Graph.h" + +#include +#include +#include +#include + +//###################################################################### +// DenseMap - This can be made generic if needed + +// Map from unique non-negative integers (IDs) to generic values. As opposed +// to a std::map/std::unordered_map, in DenseMap, all entries are value +// initialized at construction, and the map is always dense. This can be +// improved if necessary but is sufficient for our current purposes. +template +class DenseMap final { + std::vector m_map; // The map, stored as a vector + +public: + // CONSTRUCTOR + explicit DenseMap(size_t size) + : m_map{size} {} + + T_Value& operator[](const T_Key& key) { return m_map.at((key.*Index)()); } + const T_Value& operator[](const T_Key& key) const { return m_map.at((key.*Index)()); } +}; + +//###################################################################### +// ControlFlowGraph data structure + +class ControlFlowGraph; +class BasicBlock; +class ControlFlowEdge; + +// A basic block (verticies of the control flow graph) +class BasicBlock final : public V3GraphVertex { + VL_RTTI_IMPL(BasicBlock, V3GraphVertex) + friend class CfgBuilder; + + // STATE - Immutable after construction, set by CfgBuilder + // V3GraphEdge::user() is set to the unique id by CfgBuilder + std::vector m_stmtps; // statements in this BasicBlock + + // CONSTRUCTOR - via CfgBuilder only + inline explicit BasicBlock(ControlFlowGraph* graphp); + ~BasicBlock() override = default; + +public: + // METHODS - all const + + // Statements in this BasicBlock + const std::vector& stmtps() const { return m_stmtps; } + // Unique ID of this BasicBlock - defines topological ordering + size_t id() const { return V3GraphVertex::user(); } + + // The edge corresponding to the terminator branch being taken (including unonditoinal goto) + inline const ControlFlowEdge* takenEdgep() const; + // The edge corresponding to the terminator branch being not taken (or nullptr if goto) + inline const ControlFlowEdge* untknEdgep() const; + // Same as takenpEdgep/untknEdgep but returns the successor basic blocks + inline const BasicBlock* takenSuccessorp() const; + inline const BasicBlock* untknSuccessorp() const; + + void forEachSuccessor(std::function f) const { + for (const V3GraphEdge& edge : outEdges()) f(*edge.top()->as()); + } + + void forEachPredecessor(std::function f) const { + for (const V3GraphEdge& edge : inEdges()) f(*edge.fromp()->as()); + } + + FileLine* fileline() const override { + return !m_stmtps.empty() ? m_stmtps.front()->fileline() : nullptr; + } + + // For Graphviz dumps only + std::string name() const override; + std::string dotShape() const override; + std::string dotRank() const override; +}; + +// A control flow graph edge +class ControlFlowEdge final : public V3GraphEdge { + VL_RTTI_IMPL(ControlFlowEdge, V3GraphEdge) + friend class CfgBuilder; + + // STATE - Immutable after construction, set by CfgBuilder + // V3GraphEdge::user() is set to the unique id by CfgBuilder + + // CONSTRUCTOR - via CfgBuilder only + inline ControlFlowEdge(ControlFlowGraph* graphp, BasicBlock* srcp, BasicBlock* dstp); + ~ControlFlowEdge() override = default; + +public: + // METHODS - all const + + // Unique ID of this ControlFlowEdge - no particular meaning + size_t id() const { return user(); } + + // Source/destination BasicBlock + const BasicBlock& src() const { return *static_cast(fromp()); } + const BasicBlock& dst() const { return *static_cast(top()); } + + // For Graphviz dumps only + std::string dotLabel() const override; +}; + +template +using BasicBlockMap = DenseMap; + +template +using ControlFlowEdgeMap = DenseMap; + +// The control flow graph +class ControlFlowGraph final : public V3Graph { + friend class CfgBuilder; + + // STATE - Immutable after construction, set by CfgBuilder + BasicBlock* m_enterp = nullptr; // The singular entry vertex + BasicBlock* m_exitp = nullptr; // The singular exit vertex + size_t m_nBasicBlocks = 0; // Number of BasicBlocks in this ControlFlowGraph + size_t m_nEdges = 0; // Number of ControlFlowEdges in this ControlFlowGraph + + // CONSTRUCTOR - via CfgBuilder only + ControlFlowGraph() = default; + +public: + ~ControlFlowGraph() override = default; + + // METHODS + void foreach(std::function f) const { + for (const V3GraphVertex& vtx : vertices()) f(*vtx.as()); + } + + // Accessors + const BasicBlock& enter() const { return *m_enterp; } + const BasicBlock& exit() const { return *m_exitp; } + + // Number of basic blocks in this graph + size_t nBasicBlocks() const { return m_nBasicBlocks; } + // Number of control flow edges in this graph + size_t nEdges() const { return m_nEdges; } + + // Create a BasicBlock map for this graph + template + BasicBlockMap makeBasicBlockMap() const { + return BasicBlockMap{nBasicBlocks()}; + } + // Create a ControlFlowEdgeMap map for this graph + template + ControlFlowEdgeMap makeEdgeMap() const { + return ControlFlowEdgeMap{nEdges()}; + } + + // Returns true iff the graph contains a loop (back-edge) + bool containsLoop() const; +}; + +//###################################################################### +// Inline method definitions + +BasicBlock::BasicBlock(ControlFlowGraph* cfgp) + : V3GraphVertex{cfgp} {} + +const ControlFlowEdge* BasicBlock::takenEdgep() const { + // It's always the first edge + const V3GraphEdge* const frontp = outEdges().frontp(); + return static_cast(frontp); +} + +const ControlFlowEdge* BasicBlock::untknEdgep() const { + // It's always the second (last) edge + const V3GraphEdge* const frontp = outEdges().frontp(); + const V3GraphEdge* const backp = outEdges().backp(); + return backp != frontp ? static_cast(backp) : nullptr; +} + +const BasicBlock* BasicBlock::takenSuccessorp() const { + const ControlFlowEdge* const edgep = takenEdgep(); + return edgep ? static_cast(edgep->top()) : nullptr; +} + +const BasicBlock* BasicBlock::untknSuccessorp() const { + const ControlFlowEdge* const edgep = untknEdgep(); + return edgep ? static_cast(edgep->top()) : nullptr; +} + +ControlFlowEdge::ControlFlowEdge(ControlFlowGraph* graphp, BasicBlock* srcp, BasicBlock* dstp) + : V3GraphEdge{graphp, srcp, dstp, 1, false} {} + +//###################################################################### +// ControlFlowGraph functions + +namespace V3Cfg { +// Build control flow graph for given node +std::unique_ptr build(const AstNodeProcedure*); + +// Compute AstVars live on entry to given CFG. That is, variables that might +// be read before wholly assigned in the CFG. Returns nullptr if the analysis +// failed due to unhandled statements or data types involved in the CFG. +// On success, returns a vector of AstVar or AstVarScope nodes live on entry. +std::unique_ptr> liveVars(const ControlFlowGraph&); + +// Same as liveVars, but return AstVarScopes insted +std::unique_ptr> liveVarScopes(const ControlFlowGraph&); +} //namespace V3Cfg + +#endif // VERILATOR_V3CFG_H_ diff --git a/src/V3CfgBuilder.cpp b/src/V3CfgBuilder.cpp new file mode 100644 index 000000000..a4b315b99 --- /dev/null +++ b/src/V3CfgBuilder.cpp @@ -0,0 +1,291 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Control flow graph (CFG) builder +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Control flow graph (CFG) builder +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" +#include "V3Cfg.h" +#include "V3EmitV.h" + +#include +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +class CfgBuilder final : VNVisitorConst { + // STATE + // The graph being built, or nullptr if failed to build one + std::unique_ptr m_cfgp{new ControlFlowGraph}; + // Current basic block to add statements to + BasicBlock* m_currBBp = nullptr; + // Continuation block for given JumpBlock + std::unordered_map m_jumpBlockContp; + + // METHODS + + // Create new Basicblock block in the CFG + BasicBlock& addBasicBlock() { return *new BasicBlock{m_cfgp.get()}; } + + // Create new taken (or unconditional) ControlFlowEdge in the CFG + void addTakenEdge(BasicBlock& src, BasicBlock& dst) { + UASSERT_OBJ(src.outEmpty(), &src, "Taken edge should be added first"); + new ControlFlowEdge{m_cfgp.get(), &src, &dst}; + } + + // Create new untaken ControlFlowEdge in the CFG + void addUntknEdge(BasicBlock& src, BasicBlock& dst) { + UASSERT_OBJ(src.outSize1(), &src, "Untaken edge shold be added second"); + UASSERT_OBJ(src.outEdges().frontp()->top() != &dst, &src, + "Untaken branch targets the same block as the taken branch"); + new ControlFlowEdge{m_cfgp.get(), &src, &dst}; + } + + // Add the given statement to the current BasicBlock + void addStmt(AstNodeStmt* nodep) { m_currBBp->m_stmtps.emplace_back(nodep); } + + // Used to handle statements not representable in the CFG + void nonRepresentable(AstNodeStmt*) { + if (!m_cfgp) return; + m_cfgp.reset(); + } + + // Used to handle simple (non-branching) statements in the CFG + void simpleStatement(AstNodeStmt* nodep, bool representable = true) { + if (!m_cfgp) return; + // If non-representable, reset graph + if (!representable) { + m_cfgp.reset(); + return; + } + // Just add to current block + addStmt(nodep); + } + + // VISITORS + + // Eventually we should handle all procedural statements, however, what + // is a procedural statemen is a bit unclear (#6280), so in the first + // instance we will only handle select statemetns that cover the requied + // use cases, and in the base case we conservatively assume the statement + // is non-representable. More visits can be added case by case if needed. + void visit(AstNode* nodep) override { + if (!m_cfgp) return; + UINFO(9, "Unhandled AstNode type " << nodep->typeName()); + m_cfgp.reset(); + } + + // Non-representable statements + void visit(AstAssignDly* nodep) override { nonRepresentable(nodep); } + void visit(AstCase* nodep) override { nonRepresentable(nodep); } // V3Case will eliminate + void visit(AstCReset* nodep) override { nonRepresentable(nodep); } + void visit(AstDelay* nodep) override { nonRepresentable(nodep); } + + // Representable non control-flow statements + void visit(AstAssign* nodep) override { simpleStatement(nodep, !nodep->timingControlp()); } + void visit(AstComment*) override {} // ignore entirely + void visit(AstDisplay* nodep) override { simpleStatement(nodep); } + void visit(AstFinish* nodep) override { simpleStatement(nodep); } + void visit(AstStmtExpr* nodep) override { simpleStatement(nodep); } + void visit(AstStop* nodep) override { simpleStatement(nodep); } + + // Representable control flow statements + void visit(AstIf* nodep) override { + if (!m_cfgp) return; + + // Add terminator statement to current block - semantically the condition check only ... + addStmt(nodep); + + // Create then/else/continuation blocks + BasicBlock& thenBB = addBasicBlock(); + BasicBlock& elseBB = addBasicBlock(); + BasicBlock& contBB = addBasicBlock(); + addTakenEdge(*m_currBBp, thenBB); + addUntknEdge(*m_currBBp, elseBB); + + // Build then branch + m_currBBp = &thenBB; + iterateAndNextConstNull(nodep->thensp()); + if (!m_cfgp) return; + if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + + // Build else branch + m_currBBp = &elseBB; + iterateAndNextConstNull(nodep->elsesp()); + if (!m_cfgp) return; + if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + + // Set continuation + m_currBBp = &contBB; + } + void visit(AstWhile* nodep) override { + if (!m_cfgp) return; + + // Create the header block + BasicBlock& headBB = addBasicBlock(); + addTakenEdge(*m_currBBp, headBB); + + // The While goes in the header block - semantically the condition check only ... + m_currBBp = &headBB; + addStmt(nodep); + + // Create the body/continuation blocks + BasicBlock& bodyBB = addBasicBlock(); + BasicBlock& contBB = addBasicBlock(); + addTakenEdge(headBB, bodyBB); + addUntknEdge(headBB, contBB); + + // Build the body + m_currBBp = &bodyBB; + iterateAndNextConstNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->incsp()); + if (!m_cfgp) return; + if (m_currBBp) addTakenEdge(*m_currBBp, headBB); + + // Set continuation + m_currBBp = &contBB; + } + void visit(AstJumpBlock* nodep) override { + if (!m_cfgp) return; + + // Don't acutally need to add this 'nodep' to any block - but we could later if needed + + // Create continuation block + BasicBlock& contBB = addBasicBlock(); + const bool newEntry = m_jumpBlockContp.emplace(nodep, &contBB).second; + UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twice"); + + // Build the body + iterateAndNextConstNull(nodep->stmtsp()); + if (!m_cfgp) return; + if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + + // Set continuation + m_currBBp = &contBB; + } + void visit(AstJumpGo* nodep) override { + if (!m_cfgp) return; + + // Non-representable if not last in statement list (V3Const will fix this later) + if (nodep->nextp()) { + m_cfgp.reset(); + return; + } + + // Don't acutally need to add this 'nodep' to any block - but we could later if needed + + // Make current block go to the continuation of the JumpBlock + addTakenEdge(*m_currBBp, *m_jumpBlockContp.at(nodep->blockp())); + + // There should be no statements after a JumpGo! + m_currBBp = nullptr; + } + + // CONSTRUCTOR + explicit CfgBuilder(const AstNodeProcedure* nodep) { + // Build the graph, starting from the entry block + m_currBBp = &addBasicBlock(); + m_cfgp->m_enterp = m_currBBp; + // Visit each statement to build the control flow graph + iterateAndNextConstNull(nodep->stmtsp()); + if (!m_cfgp) return; + // The final block is the exit block + m_cfgp->m_exitp = m_currBBp; + + // Remove empty blocks - except enter/exit + for (V3GraphVertex* const vtxp : m_cfgp->vertices().unlinkable()) { + if (vtxp == &m_cfgp->enter()) continue; + if (vtxp == &m_cfgp->exit()) continue; + BasicBlock* const bbp = vtxp->as(); + if (!bbp->stmtps().empty()) continue; + UASSERT(bbp->outSize1(), "Empty block should have a single successor"); + BasicBlock* const succp = const_cast(bbp->takenSuccessorp()); + for (V3GraphEdge* const edgep : bbp->inEdges().unlinkable()) edgep->relinkTop(succp); + vtxp->unlinkDelete(m_cfgp.get()); + } + // Remove redundant entry block + while (m_cfgp->enter().stmtps().empty() && m_cfgp->enter().outSize1()) { + BasicBlock* const succp = m_cfgp->enter().outEdges().frontp()->top()->as(); + if (!succp->inSize1()) break; + m_cfgp->m_enterp->unlinkDelete(m_cfgp.get()); + m_cfgp->m_enterp = succp; + } + // Remove redundant exit block + while (m_cfgp->exit().stmtps().empty() && m_cfgp->exit().inSize1()) { + BasicBlock* const prep = m_cfgp->exit().inEdges().frontp()->fromp()->as(); + if (!prep->outSize1()) break; + m_cfgp->m_exitp->unlinkDelete(m_cfgp.get()); + m_cfgp->m_exitp = prep; + } + + // Compute reverse post-order enumeration and sort blocks, assign IDs + { + // Simple recursive algorith will do ... + std::vector postOrderEnumeration; + std::unordered_set visited; + const std::function visitBasicBlock = [&](BasicBlock* bbp) { + // Mark and skip if already visited + if (!visited.emplace(bbp).second) return; + // Visit successors + for (const V3GraphEdge& e : bbp->outEdges()) { + visitBasicBlock(static_cast(e.top())); + } + // Add to post order enumeration + postOrderEnumeration.emplace_back(bbp); + }; + visitBasicBlock(m_cfgp->m_enterp); + const uint32_t n = postOrderEnumeration.size(); + UASSERT_OBJ(n == m_cfgp->vertices().size(), nodep, "Inconsistent enumeration size"); + + // Set size in graph + m_cfgp->m_nBasicBlocks = n; + + // Assign ids equal to the reverse post order number and sort vertices + for (uint32_t i = 0; i < postOrderEnumeration.size(); ++i) { + BasicBlock* const bbp = postOrderEnumeration[n - 1 - i]; + bbp->user(i); + m_cfgp->vertices().unlink(bbp); + m_cfgp->vertices().linkBack(bbp); + } + } + + // Assign IDs to edges + { + size_t nEdges = 0; + for (V3GraphVertex& v : m_cfgp->vertices()) { + for (V3GraphEdge& e : v.outEdges()) e.user(nEdges++); + } + // Set size in graph + m_cfgp->m_nEdges = nEdges; + } + + if (dumpGraphLevel() >= 9) m_cfgp->dumpDotFilePrefixed("cfgbuilder"); + } + +public: + static std::unique_ptr apply(const AstNodeProcedure* nodep) { + return std::move(CfgBuilder{nodep}.m_cfgp); + } +}; + +std::unique_ptr V3Cfg::build(const AstNodeProcedure* nodep) { + return CfgBuilder::apply(nodep); +} diff --git a/src/V3CfgLiveVariables.cpp b/src/V3CfgLiveVariables.cpp new file mode 100644 index 000000000..790ee7fee --- /dev/null +++ b/src/V3CfgLiveVariables.cpp @@ -0,0 +1,240 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: CFG liveness analysis +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Classical data flow analysis computing live variables input to a CFG +// https://en.wikipedia.org/wiki/Live-variable_analysis +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" +#include "V3Cfg.h" + +#include +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +template +class CfgLiveVariables final : VNVisitorConst { + // TYPES + using Variable = std::conditional_t; + + // State associted with each basic block + struct BlockState final { + // Variables used in block, before a complete assignment in the same block + std::unordered_set m_gen; + // Variables that are assigned a complete value in the basic block + std::unordered_set m_kill; + std::unordered_set m_liveIn; // Variables live on entry to the block + std::unordered_set m_liveOut; // Variables live on exit from the block + bool m_isOnWorkList = false; // Block is on work list + bool m_wasProcessed = false; // Already processed at least once + }; + + // STATE + const ControlFlowGraph& m_cfg; // The CFG beign analysed + // State for each block + BasicBlockMap m_blockState = m_cfg.makeBasicBlockMap(); + BlockState* m_currp = nullptr; // State of current block being analysed + bool m_abort = false; // Abort analysis - unhandled construct + + // METHODS + static Variable* getTarget(const AstNodeVarRef* refp) { + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + return reinterpret_cast(refp->varScopep()); + } else { + return reinterpret_cast(refp->varp()); + } + } + + // This is to match DFG, but can be extended independently - eventually should handle all + static bool isSupportedPackedDType(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) { + return typep->keyword().isIntNumeric(); + } + if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) { + return isSupportedPackedDType(typep->subDTypep()); + } + if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) { + return typep->packed(); + } + return false; + } + + // Check and return if variable is incompatible + bool incompatible(Variable* varp) { + if (!isSupportedPackedDType(varp->dtypep())) return true; + AstVar* astVarp = nullptr; + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + astVarp = reinterpret_cast(varp)->varp(); + } else { + astVarp = reinterpret_cast(varp); + } + if (astVarp->ignoreSchedWrite()) return true; + return false; + } + + void updateGen(AstNode* nodep) { + UASSERT_OBJ(!m_abort, nodep, "Doing useless work"); + m_abort = nodep->exists([&](const AstNodeVarRef* refp) { + // Cross reference is ambiguous + if (VN_IS(refp, VarXRef)) return true; + // Only care about reads + if (refp->access().isWriteOnly()) return false; + // Grab referenced variable + Variable* const tgtp = getTarget(refp); + // Bail if not a compatible type + if (incompatible(tgtp)) return true; + // Add to gen set, if not killed - assume whole variable read + if (m_currp->m_kill.count(tgtp)) return false; + m_currp->m_gen.emplace(tgtp); + return false; + }); + } + + void updateKill(AstNode* nodep) { + UASSERT_OBJ(!m_abort, nodep, "Doing useless work"); + m_abort = nodep->exists([&](const AstNodeVarRef* refp) { + // Cross reference is ambiguous + if (VN_IS(refp, VarXRef)) return true; + // Only care about writes + if (refp->access().isReadOnly()) return false; + // Grab referenced variable + Variable* const tgtp = getTarget(refp); + // Bail if not a compatible type + if (incompatible(tgtp)) return true; + // If whole written, add to kill set + if (refp->nextp()) return false; + if (VN_IS(refp->abovep(), Sel)) return false; + m_currp->m_kill.emplace(tgtp); + return false; + }); + } + + void single(AstNode* nodep) { + // Assume all reads hapen before any writes + if (m_abort) return; + updateGen(nodep); + if (m_abort) return; + updateKill(nodep); + } + + // Apply transfer function of block, return true if changed + bool transfer(const BasicBlock& bb) { + BlockState& state = m_blockState[bb]; + + // liveIn = gen union (liveOut - kill) + std::unordered_set liveIn = state.m_gen; + for (Variable* const varp : state.m_liveOut) { + if (state.m_kill.count(varp)) continue; + liveIn.insert(varp); + } + if (liveIn == state.m_liveIn) return false; + std::swap(liveIn, state.m_liveIn); + return true; + } + + // VISIT + void visit(AstNode* nodep) override { // + UASSERT_OBJ(!m_abort, nodep, "Repeat traversal after abort"); + m_abort = true; + UINFO(9, "Unhandled AstNode type " << nodep->typeName()); + } + + void visit(AstAssign* nodep) override { single(nodep); } + void visit(AstDisplay* nodep) override { single(nodep); } + void visit(AstFinish* nodep) override { single(nodep); } + void visit(AstStmtExpr* nodep) override { single(nodep); } + void visit(AstStop* nodep) override { single(nodep); } + + // Only the condition check belongs to the terminated basic block + void visit(AstIf* nodep) override { single(nodep->condp()); } + void visit(AstWhile* nodep) override { single(nodep->condp()); } + + // CONSTRUCTOR + explicit CfgLiveVariables(const ControlFlowGraph& cfg) + : m_cfg{cfg} { + // For each basic block, compute the gen and kill set via visit + cfg.foreach([&](const BasicBlock& bb) { + if (m_abort) return; + VL_RESTORER(m_currp); + m_currp = &m_blockState[bb]; + for (AstNode* const stmtp : bb.stmtps()) iterateConst(stmtp); + }); + if (m_abort) return; + + // Perform the flow analysis + std::deque workList; + const auto enqueue = [&](const BasicBlock& bb) { + BlockState& state = m_blockState[bb]; + if (state.m_isOnWorkList) return; + state.m_isOnWorkList = true; + workList.emplace_back(&bb); + }; + + enqueue(cfg.exit()); + + while (!workList.empty()) { + const BasicBlock* const currp = workList.front(); + workList.pop_front(); + BlockState& state = m_blockState[*currp]; + state.m_isOnWorkList = false; + + // Compute meet (liveOut = union liveIn of successors) + currp->forEachSuccessor([&](const BasicBlock& bb) { + auto& liveIn = m_blockState[bb].m_liveIn; + state.m_liveOut.insert(liveIn.begin(), liveIn.end()); + }); + + // Apply transfer function of block + const bool changed = transfer(*currp); + // Enqueue predecessors + if (changed || !state.m_wasProcessed) currp->forEachPredecessor(enqueue); + // Mark as done with first visit + state.m_wasProcessed = true; + } + } + +public: + static std::unique_ptr> apply(const ControlFlowGraph& cfg) { + CfgLiveVariables analysis{cfg}; + // If failed, return nullptr + if (analysis.m_abort) return nullptr; + // Gather variables live in to the entry blockstd::unique_ptr> + const std::unordered_set& lin = analysis.m_blockState[cfg.enter()].m_liveIn; + std::vector* const resultp = new std::vector{lin.begin(), lin.end()}; + // Sort for stability + std::stable_sort(resultp->begin(), resultp->end(), [](Variable* ap, Variable* bp) { // + return ap->name() < bp->name(); + }); + return std::unique_ptr>{resultp}; + } +}; + +std::unique_ptr> V3Cfg::liveVars(const ControlFlowGraph& cfg) { + return CfgLiveVariables::apply(cfg); +} + +std::unique_ptr> V3Cfg::liveVarScopes(const ControlFlowGraph& cfg) { + return CfgLiveVariables::apply(cfg); +} diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index ca744c99e..3311c3f81 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -18,6 +18,7 @@ #include "V3Dfg.h" +#include "V3EmitV.h" #include "V3File.h" VL_DEFINE_DEBUG_FUNCTIONS; @@ -77,6 +78,8 @@ std::unique_ptr DfgGraph::clone() const { break; } } + + if (AstNode* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp); } // Clone operation vertices for (const DfgVertex& vtx : m_opVertices) { @@ -88,6 +91,11 @@ std::unique_ptr DfgGraph::clone() const { vtxp2clonep.emplace(&vtx, cp); break; } + case VDfgType::atUnitArray: { + DfgUnitArray* const cp = new DfgUnitArray{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } case VDfgType::atMux: { DfgMux* const cp = new DfgMux{*clonep, vtx.fileline(), vtx.dtypep()}; vtxp2clonep.emplace(&vtx, cp); @@ -103,6 +111,16 @@ std::unique_ptr DfgGraph::clone() const { vtxp2clonep.emplace(&vtx, cp); break; } + case VDfgType::atLogic: { + vtx.v3fatalSrc("DfgLogic cannot be cloned"); + VL_UNREACHABLE; + break; + } + case VDfgType::atUnresolved: { + vtx.v3fatalSrc("DfgUnresolved cannot be cloned"); + VL_UNREACHABLE; + break; + } default: { vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); VL_UNREACHABLE; @@ -130,7 +148,7 @@ std::unique_ptr DfgGraph::clone() const { vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { if (DfgVertex* const srcp = edge.sourcep()) { cp->addDriver(vp->driverFileLine(i), // - vp->driverIndex(i), // + vp->driverLo(i), // vtxp2clonep.at(srcp)); } }); @@ -140,10 +158,14 @@ std::unique_ptr DfgGraph::clone() const { const DfgSplicePacked* const vp = vtx.as(); DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as(); vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { - cp->addDriver(vp->driverFileLine(i), // - vp->driverLsb(i), // - vtxp2clonep.at(srcp)); + if (DfgVertex* const srcVp = edge.sourcep()) { + DfgVertex* const srcCp = vtxp2clonep.at(srcVp); + UASSERT_OBJ(!srcCp->is(), srcCp, "Cannot clone DfgLogic"); + if (srcVp == vp->defaultp()) { + cp->defaultp(srcCp); + } else { + cp->addDriver(vp->driverFileLine(i), vp->driverLo(i), srcCp); + } } }); break; @@ -268,8 +290,11 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { AstNode* const nodep = varVtxp->nodep(); AstVar* const varp = varVtxp->varp(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << '\n'; + os << " [label=\"" << nodep->prettyName() << '\n'; os << cvtToHex(varVtxp) << '\n'; + if (AstNode* const tmpForp = varVtxp->tmpForp()) { + os << "temporary for: " << tmpForp->prettyName() << "\n"; + } varVtxp->dtypep()->dumpSmall(os); os << " / F" << varVtxp->fanout() << '"'; @@ -285,6 +310,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange } else if (varVtxp->hasDfgRefs()) { os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow + } else if (varVtxp->tmpForp()) { + os << ", shape=box, style=filled, fillcolor=gray80"; } else { os << ", shape=box"; } @@ -296,8 +323,11 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { AstNode* const nodep = arrVtxp->nodep(); AstVar* const varp = arrVtxp->varp(); os << toDotId(vtx); - os << " [label=\"" << nodep->name() << '\n'; + os << " [label=\"" << nodep->prettyName() << '\n'; os << cvtToHex(arrVtxp) << '\n'; + if (AstNode* const tmpForp = arrVtxp->tmpForp()) { + os << "temporary for: " << tmpForp->prettyName() << "\n"; + } arrVtxp->dtypep()->dumpSmall(os); os << " / F" << arrVtxp->fanout() << '"'; if (varp->direction() == VDirection::INPUT) { @@ -312,6 +342,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange } else if (arrVtxp->hasDfgRefs()) { os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow + } else if (arrVtxp->tmpForp()) { + os << ", shape=box3d, style=filled, fillcolor=gray80"; } else { os << ", shape=box3d"; } @@ -354,7 +386,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { return; } - if (vtx.is()) { + if (vtx.is() || vtx.is() || vtx.is()) { os << toDotId(vtx); os << " [label=\"" << vtx.typeName() << '\n'; os << cvtToHex(&vtx) << '\n'; @@ -369,6 +401,19 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { return; } + if (const DfgLogic* const logicp = vtx.cast()) { + os << toDotId(vtx); + std::stringstream ss; + V3EmitV::debugVerilogForTree(logicp->nodep(), ss); + os << " [label=\""; + os << VString::replaceSubstr(VString::replaceSubstr(ss.str(), "\n", "\\l"), "\"", "\\\""); + os << "\\n" << cvtToHex(&vtx); + os << "\"\n"; + os << ", shape=box, style=\"rounded,filled\", fillcolor=cornsilk, nojustify=true"; + os << "]\n"; + return; + } + os << toDotId(vtx); os << " [label=\"" << vtx.typeName() << '\n'; os << cvtToHex(&vtx) << '\n'; @@ -384,7 +429,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { // Dump one DfgEdge in Graphviz format static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) { - if (!edge.sourcep()) return; + UASSERT(edge.sourcep(), "Can't dump unconnected DfgEdge"); const DfgVertex& sink = *edge.sinkp(); // sink is never nullptr os << toDotId(*edge.sourcep()) << " -> " << toDotId(sink); if (sink.arity() > 1 || sink.is()) { @@ -399,30 +444,47 @@ void DfgGraph::dumpDot(std::ostream& os, const std::string& label, // Header os << "digraph dfg {\n"; - os << "graph [label=\"" << name(); - if (!label.empty()) os << "-" << label; - os << "\", labelloc=t, labeljust=l]\n"; - os << "graph [rankdir=LR]\n"; + os << "rankdir=LR\n"; - if (!p) { - // Emit all vertices and edges - forEachVertex([&](const DfgVertex& vtx) { - dumpDotVertex(os, vtx); - vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(os, e, i); }); + // If predicate not given, dump everything + if (!p) p = [](const DfgVertex&) { return true; }; + + std::unordered_set emitted; + // Emit all vertices associated with a DfgLogic + forEachVertex([&](const DfgVertex& vtx) { + const DfgLogic* const logicp = vtx.cast(); + if (!logicp) return; + if (logicp->synth().empty()) return; + if (!p(vtx)) return; + os << "subgraph cluster_" << cvtToHex(logicp) << " {\n"; + dumpDotVertex(os, *logicp); + emitted.insert(logicp); + for (DfgVertex* const vtxp : logicp->synth()) { + if (!p(*vtxp)) continue; + dumpDotVertex(os, *vtxp); + emitted.insert(vtxp); + } + os << "}\n"; + }); + // Emit all remaining vertices + forEachVertex([&](const DfgVertex& vtx) { + if (emitted.count(&vtx)) return; + if (!p(vtx)) return; + dumpDotVertex(os, vtx); + }); + // Emit all edges + forEachVertex([&](const DfgVertex& vtx) { // + if (!p(vtx)) return; + vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { // + if (!e.sourcep() || !p(*e.sourcep())) return; + dumpDotEdge(os, e, i); }); - } else { - // Emit vertices that satify the predicate 'p' - forEachVertex([&](const DfgVertex& vtx) { - if (!p(vtx)) return; - dumpDotVertex(os, vtx); - vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { - if (!e.sourcep() || !p(*e.sourcep())) return; - dumpDotEdge(os, e, i); - }); - }); - } + }); // Footer + os << "label=\"" << name() + (label.empty() ? "" : "-" + label) << "\"\n"; + os << "labelloc=t\n"; + os << "labeljust=l\n"; os << "}\n"; } @@ -448,51 +510,65 @@ void DfgGraph::dumpDotFilePrefixed(const std::string& label, dumpDotFile(v3Global.debugFilename(filename) + ".dot", label, p); } -// LCOV_EXCL_START // Debug functions for developer use only -void DfgGraph::dumpDotUpstreamCone(const std::string& fileName, const DfgVertex& vtx, - const std::string& name) const { - // Open output file - const std::unique_ptr os{V3File::new_ofstream(fileName)}; - if (os->fail()) v3fatal("Can't write file: " << fileName); - - // Header - *os << "digraph dfg {\n"; - if (!name.empty()) *os << "graph [label=\"" << name << "\", labelloc=t, labeljust=l]\n"; - *os << "graph [rankdir=LR]\n"; - - // Work queue for depth first traversal starting from this vertex - std::vector queue{&vtx}; - +template +static std::unique_ptr> +dfgGraphCollectCone(const std::vector vtxps) { + // Work queue for traversal starting from all the seed vertices + std::vector queue = vtxps; // Set of already visited vertices - std::unordered_set visited; - + std::unordered_set* const resp = new std::unordered_set{}; // Depth first traversal while (!queue.empty()) { // Pop next work item const DfgVertex* const vtxp = queue.back(); queue.pop_back(); - - // Mark vertex as visited - const bool isFirstEncounter = visited.insert(vtxp).second; - - // If we have already visited this vertex during the traversal, then move on. - if (!isFirstEncounter) continue; - - // Enqueue all sources of this vertex. - vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); - - // Emit this vertex and all of its source edges - dumpDotVertex(*os, *vtxp); - vtxp->forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(*os, e, i); }); + // Mark vertex as visited, move on if already visited + if (!resp->insert(vtxp).second) continue; + // Enqueue all siblings of this vertex. + if VL_CONSTEXPR_CXX17 (T_SinksNotSources) { + vtxp->forEachSink([&](const DfgVertex& sink) { queue.push_back(&sink); }); + } else { + vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); + } } - - // Footer - *os << "}\n"; - // Done - os->close(); + return std::unique_ptr>{resp}; +} + +std::unique_ptr> +DfgGraph::sourceCone(const std::vector vtxps) const { + return dfgGraphCollectCone(vtxps); +} + +std::unique_ptr> +DfgGraph::sinkCone(const std::vector vtxps) const { + return dfgGraphCollectCone(vtxps); +} + +// predicate for supported data types +static bool dfgGraphIsSupportedDTypePacked(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) { + return typep->keyword().isIntNumeric(); + } + if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) { + return dfgGraphIsSupportedDTypePacked(typep->subDTypep()); + } + if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) { + return typep->packed(); + } + return false; +} + +bool DfgGraph::isSupported(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + // Support 1 dimensional unpacked arrays of packed types + if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) { + return dfgGraphIsSupportedDTypePacked(typep->subDTypep()); + } + // Support packed types + return dfgGraphIsSupportedDTypePacked(dtypep); } -// LCOV_EXCL_STOP //------------------------------------------------------------------------------ // DfgEdge @@ -658,6 +734,11 @@ DfgVertexVar* DfgVertex::getResultVar() { if (!resp->hasModRdRefs()) resp = varp; return; } + // Prefer real variabels over temporaries + if (!resp->tmpForp() != !varp->tmpForp()) { + if (resp->tmpForp()) resp = varp; + return; + } // Prefer the earlier one in source order const FileLine& oldFlp = *(resp->fileline()); const FileLine& newFlp = *(varp->fileline()); @@ -742,39 +823,26 @@ bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as(); +bool DfgVertexSplice::selfEquals(const DfgVertex& that) const { + const DfgVertexSplice* const thatp = that.as(); + if (!defaultp() != !thatp->defaultp()) return false; const size_t arity = this->arity(); for (size_t i = 0; i < arity; ++i) { - if (driverIndex(i) != thatp->driverIndex(i)) return false; + if (i == 0 && defaultp()) continue; + if (driverLo(i) != thatp->driverLo(i)) return false; } return true; } -V3Hash DfgSpliceArray::selfHash() const { +V3Hash DfgVertexSplice::selfHash() const { V3Hash hash; const size_t arity = this->arity(); - for (size_t i = 0; i < arity; ++i) hash += driverIndex(i); - return hash; -} - -// DfgSplicePacked ---------- - -bool DfgSplicePacked::selfEquals(const DfgVertex& that) const { - const DfgSplicePacked* const thatp = that.as(); - const size_t arity = this->arity(); for (size_t i = 0; i < arity; ++i) { - if (driverLsb(i) != thatp->driverLsb(i)) return false; + if (i == 0 && defaultp()) continue; + hash += driverLo(i); } - return true; -} - -V3Hash DfgSplicePacked::selfHash() const { - V3Hash hash; - const size_t arity = this->arity(); - for (size_t i = 0; i < arity; ++i) hash += driverLsb(i); return hash; } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 2ee391d9f..2936dffa8 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -34,6 +34,7 @@ #include "verilatedos.h" #include "V3Ast.h" +#include "V3Cfg.h" #include "V3Error.h" #include "V3Global.h" #include "V3Hash.h" @@ -140,7 +141,7 @@ protected: DfgGraph* m_graphp; // The containing DfgGraph const VDfgType m_type; // Vertex type tag uint32_t m_userCnt = 0; // User data generation number - UserDataStorage m_userDataStorage; // User data storage + UserDataStorage m_userDataStorage = nullptr; // User data storage // CONSTRUCTOR DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep) VL_MT_DISABLED; @@ -168,59 +169,15 @@ private: virtual V3Hash selfHash() const VL_MT_DISABLED; public: - // Supported packed types - static bool isSupportedPackedDType(const AstNodeDType* dtypep) { - dtypep = dtypep->skipRefp(); - if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) { - return typep->keyword().isIntNumeric(); - } - if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) { - return isSupportedPackedDType(typep->subDTypep()); - } - if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) { - return typep->packed(); - } - return false; - } + // The data type of the result of the vertex + AstNodeDType* dtypep() const { return m_dtypep; } - // Returns true if an AstNode with the given 'dtype' can be represented as a DfgVertex - static bool isSupportedDType(const AstNodeDType* dtypep) { - dtypep = dtypep->skipRefp(); - // Support unpacked arrays of packed types - if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) { - return isSupportedPackedDType(typep->subDTypep()); - } - // Support packed types - return isSupportedPackedDType(dtypep); - } - - // Return data type used to represent any packed value of the given 'width'. All packed types - // of a given width use the same canonical data type, as the only interesting information is - // the total width. - static AstNodeDType* dtypeForWidth(uint32_t width) { - return v3Global.rootp()->typeTablep()->findLogicDType(width, width, VSigning::UNSIGNED); - } - - // Return data type used to represent the type of 'nodep' when converted to a DfgVertex - static AstNodeDType* dtypeFor(const AstNode* nodep) { - const AstNodeDType* const dtypep = nodep->dtypep()->skipRefp(); - UDEBUGONLY(UASSERT_OBJ(isSupportedDType(dtypep), nodep, "Unsupported dtype");); - // For simplicity, all packed types are represented with a fixed type - if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) { - AstNodeDType* const adtypep = new AstUnpackArrayDType{ - typep->fileline(), dtypeForWidth(typep->subDTypep()->width()), - typep->rangep()->cloneTree(false)}; - v3Global.rootp()->typeTablep()->addTypesp(adtypep); - return adtypep; - } - return dtypeForWidth(dtypep->width()); - } + // Is it a packed type (instead of an array) + bool isPacked() const { return VN_IS(dtypep(), BasicDType); } // Source location FileLine* fileline() const { return m_filelinep; } - // The data type of the result of the nodes - AstNodeDType* dtypep() const { return m_dtypep; } - void dtypep(AstNodeDType* nodep) { m_dtypep = nodep; } + // The type of this vertex VDfgType type() const { return m_type; } @@ -244,10 +201,16 @@ public: // Width of result uint32_t width() const { - UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'"); + UASSERT_OBJ(isPacked(), this, "non-packed has no 'width()'"); return dtypep()->width(); } + // Number of sub-elements in result vertex + uint32_t size() const { + if (isPacked()) return dtypep()->width(); + return VN_AS(dtypep(), UnpackArrayDType)->elementsConst(); + } + // Cache type for 'equals' below using EqualsCache = std::unordered_map, uint8_t>; @@ -307,6 +270,9 @@ public: return m_sinksp && !m_sinksp->m_nextp ? m_sinksp->m_sinkp : nullptr; } + // First sink of the vertex, if any, otherwise nullptr + DfgVertex* firtsSinkp() const { return m_sinksp ? m_sinksp->m_sinkp : nullptr; } + // Unlink from container (graph or builder), then delete this vertex void unlinkDelete(DfgGraph& dfg) VL_MT_DISABLED; @@ -598,12 +564,20 @@ protected: m_srcCnt = 0; } -public: + void clearSources() { + for (uint32_t i = 0; i < m_srcCnt; ++i) { + UASSERT_OBJ(m_srcsp[i].sourcep(), this, "Unconnected source"); + m_srcsp[i].unlinkSource(); + } + m_srcCnt = 0; + } + ASTGEN_MEMBERS_DfgVertexVariadic; DfgEdge* sourceEdge(size_t idx) const { return &m_srcsp[idx]; } DfgVertex* source(size_t idx) const { return m_srcsp[idx].sourcep(); } +public: std::pair sourceEdges() override { return {m_srcsp, m_srcCnt}; } std::pair sourceEdges() const override { return {m_srcsp, m_srcCnt}; } }; @@ -744,6 +718,9 @@ public: std::vector> extractCyclicComponents(std::string label) VL_MT_DISABLED; + //----------------------------------------------------------------------- + // Debug dumping + // Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of // the graph which is included in the output. // If the predicate function 'p' is provided, only those vertices are dumped that satifty it. @@ -763,10 +740,70 @@ public: void dumpDotFilePrefixed(const std::string& label, std::function p = {}) const VL_MT_DISABLED; - // Dump upstream (source) logic cone starting from given vertex into a file with the given - // 'filename'. 'name' is the name of the graph, which is included in the output. - void dumpDotUpstreamCone(const std::string& filename, const DfgVertex& vtx, - const std::string& name = "") const VL_MT_DISABLED; + // Returns the set of vertices in the upstream cones of the given vertices + std::unique_ptr> + sourceCone(const std::vector) const VL_MT_DISABLED; + // Returns the set of vertices in the downstream cones of the given vertices + std::unique_ptr> + sinkCone(const std::vector) const VL_MT_DISABLED; + + //----------------------------------------------------------------------- + // Static methods for data types + + // Some data types are interned, in order to facilitate type comparison + // via pointer compariosn. These are functoins to construct the canonical + // DFG data types + + // Returns data type used to represent any packed value of the given 'width'. + static AstNodeDType* dtypePacked(uint32_t width) { + return v3Global.rootp()->typeTablep()->findLogicDType(width, width, VSigning::UNSIGNED); + } + + // Returns data type used to represent any array with the given type and number of elements. + static AstNodeDType* dtypeArray(AstNodeDType* subDtypep, uint32_t size) { + UASSERT_OBJ(isSupported(subDtypep), subDtypep, "Unsupported element type"); + FileLine* const flp = subDtypep->fileline(); + AstRange* const rangep = new AstRange{flp, static_cast(size - 1), 0}; + AstNodeDType* const dtypep = new AstUnpackArrayDType{flp, subDtypep, rangep}; + v3Global.rootp()->typeTablep()->addTypesp(dtypep); + return dtypep; + } + + // Return data type used to represent the type of 'nodep' when converted to a DfgVertex + static AstNodeDType* toDfgDType(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + UASSERT_OBJ(isSupported(dtypep), dtypep, "Unsupported dtype"); + // For simplicity, all packed types are represented with a fixed type + if (const AstUnpackArrayDType* const uatp = VN_CAST(dtypep, UnpackArrayDType)) { + return dtypeArray(toDfgDType(uatp->subDTypep()), uatp->elementsConst()); + } + return dtypePacked(dtypep->width()); + } + + //----------------------------------------------------------------------- + // Static methods for compatibility tests + + // Returns true if the given data type can be represented in the graph + static bool isSupported(const AstNodeDType* dtypep) VL_MT_DISABLED; + + // Returns true if variable can be represented in the graph + static bool isSupported(const AstVar* varp) { + if (varp->isIfaceRef()) return false; // Cannot handle interface references + if (varp->delayp()) return false; // Cannot handle delayed variables + if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore + if (varp->dfgMultidriven()) return false; // Discovered as multidriven on earlier DFG run + return isSupported(varp->dtypep()); + } + + // Returns true if variable can be represented in the graph + static bool isSupported(const AstVarScope* vscp) { + // If the variable is not in a regular module, then we do not support it. + // This is especially needed for variabels in interfaces which might be + // referenced via virtual intefaces, which cannot be resovled statically. + if (!VN_IS(vscp->scopep()->modp(), Module)) return false; + // Check the AstVar + return isSupported(vscp->varp()); + } }; // Specializations of privateTypeTest @@ -921,12 +958,41 @@ bool DfgVertex::isOnes() const { return false; } +//------------------------------------------------------------------------------ +// Inline method definitions - for DfgConst +//------------------------------------------------------------------------------ + +DfgConst::DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num) + : DfgVertex{dfg, dfgType(), flp, DfgGraph::dtypePacked(num.width())} + , m_num{num} {} +DfgConst::DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value) + : DfgVertex{dfg, dfgType(), flp, DfgGraph::dtypePacked(width)} + , m_num{flp, static_cast(width), value} {} + +//------------------------------------------------------------------------------ +// Inline method definitions - for DfgVertexSplice +//------------------------------------------------------------------------------ + +DfgVertex* DfgVertexSplice::wholep() const { + if (defaultp()) return nullptr; + if (arity() != 1) return nullptr; + if (driverLo(0) != 0) return nullptr; + DfgVertex* const srcp = DfgVertexVariadic::source(1); + if (srcp->size() != size()) return nullptr; + if (DfgUnitArray* const uap = srcp->cast()) { + if (DfgVertexSplice* sp = uap->srcp()->cast()) { + if (!sp->wholep()) return nullptr; + } + } + return srcp; +} + //------------------------------------------------------------------------------ // Inline method definitions - for DfgVertexVar //------------------------------------------------------------------------------ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) - : DfgVertexUnary{dfg, type, varp->fileline(), dtypeFor(varp)} + : DfgVertexUnary{dfg, type, varp->fileline(), DfgGraph::toDfgDType(varp->dtypep())} , m_varp{varp} , m_varScopep{nullptr} { UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph"); @@ -936,7 +1002,7 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) UASSERT_OBJ((varp->user1() >> 4) > 0, varp, "Reference count overflow"); } DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) - : DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)} + : DfgVertexUnary{dfg, type, vscp->fileline(), DfgGraph::toDfgDType(vscp->varp()->dtypep())} , m_varp{vscp->varp()} , m_varScopep{vscp} { UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph"); diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 147bd2b1a..ec18a8212 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -14,20 +14,16 @@ // //************************************************************************* // -// Convert and AstModule to a DfgGraph. We proceed by visiting convertible logic blocks (e.g.: -// AstAssignW of appropriate type and with no delays), recursively constructing DfgVertex instances -// for the expressions that compose the subject logic block. If all expressions in the current -// logic block can be converted, then we delete the logic block (now represented in the DfgGraph), -// and connect the corresponding DfgVertex instances appropriately. If some of the expressions were -// not convertible in the current logic block, we revert (delete) the DfgVertex instances created -// for the logic block, and leave the logic block in the AstModule. Any variable reference from -// non-converted logic blocks (or other constructs under the AstModule) are marked as being -// referenced in the AstModule, which is relevant for later optimization. +// Convert and AstModule (before V3Scope), or the entire AstNetlist +// (after V3Scope) to an initial DfgGraph composed onlyof DfgLogic, +// DfgUnresolved and DfgVertexVar vertices. This will later be synthesized +// into primitive operations by V3DfgPasses::synthesize. // //************************************************************************* #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT +#include "V3Cfg.h" #include "V3Const.h" #include "V3Dfg.h" #include "V3DfgPasses.h" @@ -36,42 +32,20 @@ VL_DEFINE_DEBUG_FUNCTIONS; -namespace { - -// Create a DfgVertex out of a AstNodeExpr. For most AstNodeExpr subtypes, this can be done -// automatically. For the few special cases, we provide specializations below -template -T_Vertex* makeVertex(const T_Node* nodep, DfgGraph& dfg) { - return new T_Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; -} - -template <> -DfgArraySel* makeVertex(const AstArraySel* nodep, DfgGraph& dfg) { - // Some earlier passes create malformed ArraySels, just bail on those... - // See t_bitsel_wire_array_bad - if (VN_IS(nodep->fromp(), Const)) return nullptr; - if (!VN_IS(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) return nullptr; - return new DfgArraySel{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; -} - -} //namespace - -// Visitor that can convert combinational Ast logic constructs/assignments to Dfg template -class AstToDfgConverter final : public VNVisitor { +class AstToDfgVisitor final : public VNVisitor { // NODE STATE - // AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node + // AstVar/AstVarScope::user2() -> DfgVertexVar* : the corresponding variable vertex + // AstVar/AstVarScope::user3() -> bool : Already gathered - used fine grained below + const VNUser2InUse m_user2InUse; // TYPES + using RootType = std::conditional_t; using Variable = std::conditional_t; // STATE - DfgGraph& m_dfg; // The graph being built V3DfgAstToDfgContext& m_ctx; // The context for stats - bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit' - bool m_converting = false; // We are trying to convert some logic at the moment - std::vector m_uncommittedSpliceps; // New splices made during convertLValue // METHODS static Variable* getTarget(const AstVarRef* refp) { @@ -83,9 +57,33 @@ class AstToDfgConverter final : public VNVisitor { } } - DfgVertexVar* getNet(Variable* varp) { + std::unique_ptr> getLiveVariables(const ControlFlowGraph& cfg) { + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + std::unique_ptr> result = V3Cfg::liveVarScopes(cfg); + const auto resultp = reinterpret_cast*>(result.release()); + return std::unique_ptr>{resultp}; + } else { + std::unique_ptr> result = V3Cfg::liveVars(cfg); + const auto resultp = reinterpret_cast*>(result.release()); + return std::unique_ptr>{resultp}; + } + } + + // Mark variables referenced under node + static void markReferenced(const AstNode* nodep) { + nodep->foreach([](const AstVarRef* refp) { + Variable* const tgtp = getTarget(refp); + // Mark as read from non-DFG logic + if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp); + // Mark as written from non-DFG logic + if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp); + }); + } + + DfgVertexVar* getVarVertex(Variable* varp) { if (!varp->user2p()) { - AstNodeDType* const dtypep = varp->dtypep()->skipRefp(); + const AstNodeDType* const dtypep = varp->dtypep()->skipRefp(); DfgVertexVar* const vtxp = VN_IS(dtypep, UnpackArrayDType) ? static_cast(new DfgVarArray{m_dfg, varp}) @@ -95,861 +93,137 @@ class AstToDfgConverter final : public VNVisitor { return varp->user2u().template to(); } - // Returns true if the expression cannot (or should not) be represented by DFG - bool unhandled(AstNodeExpr* nodep) { - // Short-circuiting if something was already unhandled - if (!m_foundUnhandled) { - // Impure nodes cannot be represented - if (!nodep->isPure()) { - m_foundUnhandled = true; - ++m_ctx.m_nonRepImpure; - } - // Check node has supported dtype - if (!DfgVertex::isSupportedDType(nodep->dtypep())) { - m_foundUnhandled = true; - ++m_ctx.m_nonRepDType; - } - } - return m_foundUnhandled; - } - - bool isSupported(const AstVar* varp) { - if (varp->isIfaceRef()) return false; // Cannot handle interface references - if (varp->delayp()) return false; // Cannot handle delayed variables - if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore - return DfgVertex::isSupportedDType(varp->dtypep()); - } - - bool isSupported(const AstVarScope* vscp) { - // Check the Var fist - if (!isSupported(vscp->varp())) return false; - // If the variable is not in a regular module, then do not convert it. - // This is especially needed for variabels in interfaces which might be - // referenced via virtual intefaces, which cannot be resovled statically. - if (!VN_IS(vscp->scopep()->modp(), Module)) return false; - // Otherwise OK - return true; - } - - bool isSupported(const AstVarRef* nodep) { - // Cannot represent cross module references - if (nodep->classOrPackagep()) return false; - // Check target - return isSupported(getTarget(nodep)); - } - - // Given an RValue expression, return the equivalent Vertex, or nullptr if not representable. - DfgVertex* convertRValue(AstNodeExpr* nodep) { - UASSERT_OBJ(!m_converting, nodep, "'convertingRValue' should not be called recursively"); - VL_RESTORER(m_converting); - VL_RESTORER(m_foundUnhandled); - m_converting = true; - m_foundUnhandled = false; - - // Convert the expression - iterate(nodep); - - // If falied to convert, return nullptr - if (m_foundUnhandled) return nullptr; - - // Traversal set user2p to the equivalent vertex - DfgVertex* const vtxp = nodep->user2u().to(); - UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex after covnersion"); - return vtxp; - } - - // Given an LValue expression, return the splice node that writes the - // destination, together with the index to use for splicing in the value. - // Returns {nullptr, 0}, if the given LValue expression is not supported. - std::pair convertLValue(AstNodeExpr* nodep) { - if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { - if (!isSupported(vrefp)) { - ++m_ctx.m_nonRepLhs; - return {nullptr, 0}; - } - // Get the variable vertex - DfgVertexVar* const vtxp = getNet(getTarget(vrefp)); - // Ensure the Splice driver exists for this variable - if (!vtxp->srcp()) { - FileLine* const flp = vtxp->fileline(); - AstNodeDType* const dtypep = vtxp->dtypep(); - if (vtxp->is()) { - DfgSplicePacked* const newp = new DfgSplicePacked{m_dfg, flp, dtypep}; - m_uncommittedSpliceps.emplace_back(newp); - vtxp->srcp(newp); - } else if (vtxp->is()) { - DfgSpliceArray* const newp = new DfgSpliceArray{m_dfg, flp, dtypep}; - m_uncommittedSpliceps.emplace_back(newp); - vtxp->srcp(newp); - } else { - nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE - } - } - // Return the Splice driver - return {vtxp->srcp()->as(), 0}; - } - - if (AstSel* selp = VN_CAST(nodep, Sel)) { - // Only handle constant selects - const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); - if (!lsbp) { - ++m_ctx.m_nonRepLhs; - return {nullptr, 0}; - } - uint32_t lsb = lsbp->toUInt(); - - // Convert the 'fromp' sub-expression - const auto pair = convertLValue(selp->fromp()); - if (!pair.first) return {nullptr, 0}; - DfgSplicePacked* const splicep = pair.first->template as(); - // Adjust index. - lsb += pair.second; - - // AstSel doesn't change type kind (array vs packed), so we can use - // the existing splice driver with adjusted lsb - return {splicep, lsb}; - } - - if (AstArraySel* const aselp = VN_CAST(nodep, ArraySel)) { - // Only handle constant selects - const AstConst* const indexp = VN_CAST(aselp->bitp(), Const); - if (!indexp) { - ++m_ctx.m_nonRepLhs; - return {nullptr, 0}; - } - uint32_t index = indexp->toUInt(); - - // Convert the 'fromp' sub-expression - const auto pair = convertLValue(aselp->fromp()); - if (!pair.first) return {nullptr, 0}; - DfgSpliceArray* const splicep = pair.first->template as(); - // Adjust index. Note pair.second is always 0, but we might handle array slices later.. - index += pair.second; - - // Ensure the Splice driver exists for this element - if (!splicep->driverAt(index)) { - FileLine* const flp = nodep->fileline(); - AstNodeDType* const dtypep = DfgVertex::dtypeFor(nodep); - if (VN_IS(dtypep, BasicDType)) { - DfgSplicePacked* const newp = new DfgSplicePacked{m_dfg, flp, dtypep}; - m_uncommittedSpliceps.emplace_back(newp); - splicep->addDriver(flp, index, newp); - } else if (VN_IS(dtypep, UnpackArrayDType)) { - DfgSpliceArray* const newp = new DfgSpliceArray{m_dfg, flp, dtypep}; - m_uncommittedSpliceps.emplace_back(newp); - splicep->addDriver(flp, index, newp); - } else { - nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE - } - } - - // Return the splice driver - return {splicep->driverAt(index)->as(), 0}; - } - - ++m_ctx.m_nonRepLhs; - return {nullptr, 0}; - } - - // Given the LHS of an assignment, and the vertex representing the RHS, - // connect up the RHS to drive the targets. - // Returns true on success, false if the LHS is not representable. - bool convertAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* vtxp) { - // Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'. - // There might be multiple of these if 'lhsp' is a concatenation. - struct Assignment final { - DfgVertexSplice* m_lhsp; - uint32_t m_idx; - DfgVertex* m_rhsp; - Assignment() = delete; - Assignment(DfgVertexSplice* lhsp, uint32_t idx, DfgVertex* rhsp) - : m_lhsp{lhsp} - , m_idx{idx} - , m_rhsp{rhsp} {} - }; - - // Convert each concatenation LHS separately, gather all assignments - // we need to do into 'assignments', return true if all LValues - // converted successfully. - std::vector assignments; - const std::function convertAllLValues - = [&](AstNodeExpr* lhsp, DfgVertex* vtxp) -> bool { - // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) - lhsp = VN_AS(V3Const::constifyExpensiveEdit(lhsp), NodeExpr); - - // Concatenation on the LHS, convert each parts - if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { - AstNodeExpr* const cLhsp = concatp->lhsp(); - AstNodeExpr* const cRhsp = concatp->rhsp(); - // Convert Left of concat - FileLine* const lFlp = cLhsp->fileline(); - DfgSel* const lVtxp = new DfgSel{m_dfg, lFlp, DfgVertex::dtypeFor(cLhsp)}; - lVtxp->fromp(vtxp); - lVtxp->lsb(cRhsp->width()); - if (!convertAllLValues(cLhsp, lVtxp)) return false; - // Convert Rigth of concat - FileLine* const rFlp = cRhsp->fileline(); - DfgSel* const rVtxp = new DfgSel{m_dfg, rFlp, DfgVertex::dtypeFor(cRhsp)}; - rVtxp->fromp(vtxp); - rVtxp->lsb(0); - return convertAllLValues(cRhsp, rVtxp); - } - - // Non-concatenation, convert the LValue - const auto pair = convertLValue(lhsp); - if (!pair.first) return false; - assignments.emplace_back(pair.first, pair.second, vtxp); - return true; - }; - // Convert the given LHS assignment, give up if any LValues failed to convert - if (!convertAllLValues(lhsp, vtxp)) { - for (DfgVertexSplice* const splicep : m_uncommittedSpliceps) { - VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); - } - m_uncommittedSpliceps.clear(); + // Gather variables written by the given logic node. + // Return nullptr if any are not supported. + std::unique_ptr> gatherWritten(const AstNode* nodep) { + const VNUser3InUse user3InUse; + std::unique_ptr> resp{new std::vector{}}; + // We can ignore AstVarXRef here. The only thing we can do with DfgLogic is + // synthesize it into regular vertices, which will fail on a VarXRef at that point. + const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool { + if (VN_IS(vrefp, VarXRef)) return true; + if (vrefp->access().isReadOnly()) return false; + Variable* const varp = getTarget(VN_AS(vrefp, VarRef)); + if (!DfgGraph::isSupported(varp)) return true; + if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp)); return false; + }); + if (abort) { + ++m_ctx.m_nonRepVar; + return nullptr; } - m_uncommittedSpliceps.clear(); - - // All successful, connect the drivers - for (const Assignment& a : assignments) { - if (DfgSplicePacked* const spp = a.m_lhsp->template cast()) { - spp->addDriver(flp, a.m_idx, a.m_rhsp); - } else if (DfgSpliceArray* const sap = a.m_lhsp->template cast()) { - sap->addDriver(flp, a.m_idx, a.m_rhsp); - } else { - a.m_lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - } - return true; + return resp; } - // Convert the assignment with the given LHS and RHS into DFG. - // Returns true on success, false if not representable. - bool convertEquation(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) { - // Check data types are compatible. - if (!DfgVertex::isSupportedDType(lhsp->dtypep()) - || !DfgVertex::isSupportedDType(rhsp->dtypep())) { - ++m_ctx.m_nonRepDType; + // Gather variables read by the given logic node. + // Return nullptr if any are not supported. + std::unique_ptr> gatherRead(const AstNode* nodep) { + const VNUser3InUse user3InUse; + std::unique_ptr> resp{new std::vector{}}; + // We can ignore AstVarXRef here. The only thing we can do with DfgLogic is + // synthesize it into regular vertices, which will fail on a VarXRef at that point. + const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool { + if (VN_IS(vrefp, VarXRef)) return true; + if (vrefp->access().isWriteOnly()) return false; + Variable* const varp = getTarget(VN_AS(vrefp, VarRef)); + if (!DfgGraph::isSupported(varp)) return true; + if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp)); return false; + }); + if (abort) { + ++m_ctx.m_nonRepVar; + return nullptr; + } + return resp; + } + + // Gather variables live in to the given CFG. + // Return nullptr if any are not supported. + std::unique_ptr> gatherLive(const ControlFlowGraph& cfg) { + // Run analysis + std::unique_ptr> varps = getLiveVariables(cfg); + if (!varps) { + ++m_ctx.m_nonRepLive; + return nullptr; } - // For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c) - if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) { - ++m_ctx.m_nonRepDType; - return false; + // Convert to vertics + const VNUser3InUse user3InUse; + std::unique_ptr> resp{new std::vector{}}; + resp->reserve(varps->size()); + for (Variable* const varp : *varps) { + if (!DfgGraph::isSupported(varp)) { + ++m_ctx.m_nonRepVar; + return nullptr; + } + UASSERT_OBJ(!varp->user3SetOnce(), varp, "Live variables should be unique"); + resp->emplace_back(getVarVertex(varp)); } + return resp; + } - // Cannot handle mismatched widths. Mismatched assignments should have been fixed up in - // earlier passes anyway, so this should never be hit, but being paranoid just in case. - if (lhsp->width() != rhsp->width()) { // LCOV_EXCL_START - ++m_ctx.m_nonRepWidth; - return false; - } // LCOV_EXCL_STOP + // Connect inputs and outputs of a DfgLogic + void connect(DfgLogic& vtx, const std::vector& iVarps, + const std::vector& oVarps) { + // Connect inputs + for (DfgVertexVar* const iVarp : iVarps) vtx.addInput(iVarp); + // Connect outputs + for (DfgVertexVar* const oVarp : oVarps) { + if (!oVarp->srcp()) oVarp->srcp(new DfgUnresolved{m_dfg, oVarp}); + oVarp->srcp()->as()->addDriver(&vtx); + } + } - // Convert the RHS expression - DfgVertex* const rVtxp = convertRValue(rhsp); - if (!rVtxp) return false; + // Convert AstAssignW to DfgLogic, return true if successful. + bool convert(AstAssignW* nodep) { + // Cannot handle assignment with timing control + if (nodep->timingControlp()) return false; - // Connect the RHS vertex to the LHS targets - if (!convertAssignment(flp, lhsp, rVtxp)) return false; - - // All good + // Potentially convertible block + ++m_ctx.m_inputs; + // Gather written variables, give up if any are not supported + const std::unique_ptr> oVarpsp = gatherWritten(nodep); + if (!oVarpsp) return false; + // Gather read variables, give up if any are not supported + const std::unique_ptr> iVarpsp = gatherRead(nodep); + if (!iVarpsp) return false; + // Create the DfgLogic + DfgLogic* const logicp = new DfgLogic{m_dfg, nodep}; + // Connect it up + connect(*logicp, *iVarpsp, *oVarpsp); + // Done ++m_ctx.m_representable; return true; } - // Convert an AstNodeAssign (AstAssign or AstAssignW) - bool convertNodeAssign(AstNodeAssign* nodep) { - UASSERT_OBJ(VN_IS(nodep, AssignW) || VN_IS(nodep, Assign), nodep, "Invalid subtype"); - ++m_ctx.m_inputEquations; - - // Cannot handle assignment with timing control yet - if (nodep->timingControlp()) { - ++m_ctx.m_nonRepTiming; - return false; - } - - return convertEquation(nodep->fileline(), nodep->lhsp(), nodep->rhsp()); - } - - // Convert special simple form Always block into DFG. - // Returns true on success, false if not representable/not simple. - bool convertSimpleAlways(AstAlways* nodep) { - // Only consider single statement block - if (!nodep->isJustOneBodyStmt()) return false; - - AstNode* const stmtp = nodep->stmtsp(); - - if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) { - return convertNodeAssign(assignp); - } - - if (AstIf* const ifp = VN_CAST(stmtp, If)) { - // Will only handle single assignments to the same LHS in both branches - AstAssign* const thenp = VN_CAST(ifp->thensp(), Assign); - AstAssign* const elsep = VN_CAST(ifp->elsesp(), Assign); - if (!thenp || !elsep || thenp->nextp() || elsep->nextp() - || !thenp->lhsp()->sameTree(elsep->lhsp())) { - return false; - } - - ++m_ctx.m_inputEquations; - if (thenp->timingControlp() || elsep->timingControlp()) { - ++m_ctx.m_nonRepTiming; - return false; - } - - // Create a conditional for the rhs by borrowing the components from the AstIf - AstCond* const rhsp = new AstCond{ifp->fileline(), // - ifp->condp()->unlinkFrBack(), // - thenp->rhsp()->unlinkFrBack(), // - elsep->rhsp()->unlinkFrBack()}; - const bool success = convertEquation(ifp->fileline(), thenp->lhsp(), rhsp); - // Put the AstIf back together - ifp->condp(rhsp->condp()->unlinkFrBack()); - thenp->rhsp(rhsp->thenp()->unlinkFrBack()); - elsep->rhsp(rhsp->elsep()->unlinkFrBack()); - // Delete the auxiliary conditional - VL_DO_DANGLING(rhsp->deleteTree(), rhsp); - return success; - } - - return false; - } - - // VISITORS - - // Unhandled node - void visit(AstNode* nodep) override { - if (!m_foundUnhandled && m_converting) ++m_ctx.m_nonRepUnknown; - m_foundUnhandled = true; - } - - // Expressions - mostly auto generated, but a few special ones - void visit(AstVarRef* nodep) override { - UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - // This visit method is only called on RValues, where only read refs are supportes - if (!nodep->access().isReadOnly() || !isSupported(nodep)) { - m_foundUnhandled = true; - ++m_ctx.m_nonRepVarRef; - return; - } - nodep->user2p(getNet(getTarget(nodep))); - } - void visit(AstConst* nodep) override { - UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - DfgVertex* const vtxp = new DfgConst{m_dfg, nodep->fileline(), nodep->num()}; - nodep->user2p(vtxp); - } - void visit(AstSel* nodep) override { - UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); - UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); - if (unhandled(nodep)) return; - - iterate(nodep->fromp()); - if (m_foundUnhandled) return; - - FileLine* const flp = nodep->fileline(); - DfgVertex* vtxp = nullptr; - if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { - DfgSel* const selp = new DfgSel{m_dfg, flp, DfgVertex::dtypeFor(nodep)}; - selp->fromp(nodep->fromp()->user2u().to()); - selp->lsb(constp->toUInt()); - vtxp = selp; - } else { - iterate(nodep->lsbp()); - if (m_foundUnhandled) return; - DfgMux* const muxp = new DfgMux{m_dfg, flp, DfgVertex::dtypeFor(nodep)}; - muxp->fromp(nodep->fromp()->user2u().to()); - muxp->lsbp(nodep->lsbp()->user2u().to()); - vtxp = muxp; - } - nodep->user2p(vtxp); - } -// The rest of the visit methods for expressions are generated by 'astgen' -#include "V3Dfg__gen_ast_to_dfg.h" - -public: - // PUBLIC METHODS - - // Convert AstAssignW to Dfg, return true if successful. - bool convert(AstAssignW* nodep) { - if (convertNodeAssign(nodep)) { - // Remove node from Ast. Now represented by the Dfg. - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - return true; - } - - return false; - } - - // Convert AstAlways to Dfg, return true if successful. + // Convert AstAlways to DfgLogic, return true if successful. bool convert(AstAlways* nodep) { - // Ignore sequential logic + // Can only handle combinational logic + if (nodep->sentreep()) return false; const VAlwaysKwd kwd = nodep->keyword(); - if (nodep->sentreep() || (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) { + if (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB) return false; + + // Potentially convertible block + ++m_ctx.m_inputs; + // Attempt to build CFG of AstAlways, give up if failed + std::unique_ptr cfgp = V3Cfg::build(nodep); + if (!cfgp) { + ++m_ctx.m_nonRepCfg; return false; } - - // Attemp to convert special forms - if (convertSimpleAlways(nodep)) { - // Remove node from Ast. Now represented by the Dfg. - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - return true; - } - - return false; - } - - // CONSTRUCTOR - AstToDfgConverter(DfgGraph& dfg, V3DfgAstToDfgContext& ctx) - : m_dfg{dfg} - , m_ctx{ctx} {} -}; - -// Resolves multiple drivers (keep only the first one), -// and ensures drivers are stored in ascending index order -class AstToDfgNormalizeDrivers final { - // TYPES - struct Driver final { - FileLine* m_flp; // Location of driver in source - uint32_t m_low; // Low index of driven range - DfgVertex* m_vtxp; // Driving vertex - Driver() = delete; - Driver(FileLine* flp, uint32_t low, DfgVertex* vtxp) - : m_flp{flp} - , m_low{low} - , m_vtxp{vtxp} {} - }; - - // STATE - DfgGraph& m_dfg; // The graph being processed - DfgVertexVar& m_var; // The variable being normalzied - - // METHODS - - // Normalize packed driver - void normalizePacked(const std::string& sub, DfgSplicePacked* const splicep) { - UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSplicePacked"); - - // The drivers of 'splicep' - std::vector drivers; - drivers.reserve(splicep->arity()); - - // Sometime assignment ranges are coalesced by V3Const, - // so we unpack concatenations for better error reporting. - const std::function gather - = [&](FileLine* flp, uint32_t lsb, DfgVertex* vtxp) -> void { - if (DfgConcat* const concatp = vtxp->cast()) { - DfgVertex* const rhsp = concatp->rhsp(); - auto const rhs_width = rhsp->width(); - gather(rhsp->fileline(), lsb, rhsp); - DfgVertex* const lhsp = concatp->lhsp(); - gather(lhsp->fileline(), lsb + rhs_width, lhsp); - concatp->unlinkDelete(m_dfg); - } else { - drivers.emplace_back(flp, lsb, vtxp); - } - }; - - // Gather and unlink all drivers - splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - DfgVertex* const driverp = edge.sourcep(); - UASSERT(driverp, "Should not have created undriven sources"); - UASSERT_OBJ(!driverp->is(), splicep, "Should not be DfgVertexSplice"); - gather(splicep->driverFileLine(i), splicep->driverLsb(i), driverp); - edge.unlinkSource(); - }); - splicep->resetSources(); - - const auto cmp = [](const Driver& a, const Driver& b) { - if (a.m_low != b.m_low) return a.m_low < b.m_low; - return a.m_flp->operatorCompare(*b.m_flp) < 0; - }; - - // Sort drivers by LSB - std::stable_sort(drivers.begin(), drivers.end(), cmp); - - // Fix multiply driven ranges - for (auto it = drivers.begin(); it != drivers.end();) { - Driver& a = *it++; - const uint32_t aWidth = a.m_vtxp->width(); - const uint32_t aEnd = a.m_low + aWidth; - while (it != drivers.end()) { - Driver& b = *it; - // If no overlap, then nothing to do - if (b.m_low >= aEnd) break; - - const uint32_t bWidth = b.m_vtxp->width(); - const uint32_t bEnd = b.m_low + bWidth; - const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - - // Loop index often abused, so suppress - if (!m_var.varp()->isUsedLoopIdx()) { - AstNode* const nodep = m_var.nodep(); - nodep->v3warn( // - MULTIDRIVEN, - "Bits [" // - << overlapEnd << ":" << b.m_low << "] of signal '" - << nodep->prettyName() << sub - << "' have multiple combinational drivers\n" - << a.m_flp->warnOther() << "... Location of first driver\n" - << a.m_flp->warnContextPrimary() << '\n' - << b.m_flp->warnOther() << "... Location of other driver\n" - << b.m_flp->warnContextSecondary() << nodep->warnOther() - << "... Only the first driver will be respected"); - } - - // If the first driver completely covers the range of the second driver, - // we can just delete the second driver completely, otherwise adjust the - // second driver to apply from the end of the range of the first driver. - if (aEnd >= bEnd) { - it = drivers.erase(it); - } else { - const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd); - DfgSel* const selp = new DfgSel{m_dfg, b.m_vtxp->fileline(), dtypep}; - selp->fromp(b.m_vtxp); - selp->lsb(aEnd - b.m_low); - b.m_low = aEnd; - b.m_vtxp = selp; - std::stable_sort(it, drivers.end(), cmp); - } - } - } - - // Reinsert drivers in order - for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); - } - - // Normalize array driver - void normalizeArray(const std::string& sub, DfgSpliceArray* const splicep) { - UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSpliceArray"); - - // The drivers of 'splicep' - std::vector drivers; - drivers.reserve(splicep->arity()); - - // Normalize, gather, and unlink all drivers - splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - DfgVertex* const driverp = edge.sourcep(); - UASSERT(driverp, "Should not have created undriven sources"); - const uint32_t idx = splicep->driverIndex(i); - // Normalize - if (DfgSplicePacked* const splicePackedp = driverp->cast()) { - normalizePacked(sub + "[" + std::to_string(idx) + "]", splicePackedp); - } else if (DfgSpliceArray* const spliceArrayp = driverp->cast()) { - normalizeArray(sub + "[" + std::to_string(idx) + "]", spliceArrayp); - } else if (driverp->is()) { - driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - // Gather - drivers.emplace_back(splicep->driverFileLine(i), idx, driverp); - // Unlink - edge.unlinkSource(); - }); - splicep->resetSources(); - - const auto cmp = [](const Driver& a, const Driver& b) { - if (a.m_low != b.m_low) return a.m_low < b.m_low; - return a.m_flp->operatorCompare(*b.m_flp) < 0; - }; - - // Sort drivers by index - std::stable_sort(drivers.begin(), drivers.end(), cmp); - - // Fix multiply driven ranges - for (auto it = drivers.begin(); it != drivers.end();) { - Driver& a = *it++; - AstUnpackArrayDType* aArrayDTypep = VN_CAST(a.m_vtxp->dtypep(), UnpackArrayDType); - const uint32_t aElements = aArrayDTypep ? aArrayDTypep->elementsConst() : 1; - const uint32_t aEnd = a.m_low + aElements; - while (it != drivers.end()) { - Driver& b = *it; - // If no overlap, then nothing to do - if (b.m_low >= aEnd) break; - - AstUnpackArrayDType* bArrayDTypep = VN_CAST(b.m_vtxp->dtypep(), UnpackArrayDType); - const uint32_t bElements = bArrayDTypep ? bArrayDTypep->elementsConst() : 1; - const uint32_t bEnd = b.m_low + bElements; - const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - - AstNode* const nodep = m_var.nodep(); - nodep->v3warn( // - MULTIDRIVEN, - "Elements [" // - << overlapEnd << ":" << b.m_low << "] of signal '" << nodep->prettyName() - << sub << "' have multiple combinational drivers\n" - << a.m_flp->warnOther() << "... Location of first driver\n" - << a.m_flp->warnContextPrimary() << '\n' - << b.m_flp->warnOther() << "... Location of other driver\n" - << b.m_flp->warnContextSecondary() << nodep->warnOther() - << "... Only the first driver will be respected"); - - // If the first driver completely covers the range of the second driver, - // we can just delete the second driver completely, otherwise adjust the - // second driver to apply from the end of the range of the first driver. - if (aEnd >= bEnd) { - it = drivers.erase(it); - } else { - const auto distance = std::distance(drivers.begin(), it); - DfgVertex* const bVtxp = b.m_vtxp; - FileLine* const flp = b.m_vtxp->fileline(); - AstNodeDType* const elemDtypep = DfgVertex::dtypeFor( - VN_AS(splicep->dtypep(), UnpackArrayDType)->subDTypep()); - // Remove this driver - it = drivers.erase(it); - // Add missing items element-wise - for (uint32_t i = aEnd; i < bEnd; ++i) { - DfgArraySel* const aselp = new DfgArraySel{m_dfg, flp, elemDtypep}; - aselp->fromp(bVtxp); - aselp->bitp(new DfgConst{m_dfg, flp, 32, i}); - drivers.emplace_back(flp, i, aselp); - } - it = drivers.begin(); - std::advance(it, distance); - std::stable_sort(it, drivers.end(), cmp); - } - } - } - - // Reinsert drivers in order - for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); - } - - // CONSTRUCTOR - AstToDfgNormalizeDrivers(DfgGraph& dfg, DfgVertexVar& var) - : m_dfg{dfg} - , m_var{var} { - // Nothing to do for un-driven (input) variables - if (!var.srcp()) return; - - // The driver of a variable must always be a splice vertex, normalize it - if (DfgSpliceArray* const sArrayp = var.srcp()->cast()) { - normalizeArray("", sArrayp); - } else if (DfgSplicePacked* const sPackedp = var.srcp()->cast()) { - normalizePacked("", sPackedp); - } else { - var.v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - } - -public: - // Normalize drivers of given variable - static void apply(DfgGraph& dfg, DfgVertexVar& var) { AstToDfgNormalizeDrivers{dfg, var}; } -}; - -// Coalesce contiguous driver ranges, -// and remove redundant splice vertices (when the variable is driven whole) -class AstToDfgCoalesceDrivers final { - // TYPES - struct Driver final { - FileLine* m_flp; // Location of driver in source - uint32_t m_low; // Low index of driven range - DfgVertex* m_vtxp; // Driving vertex - Driver() = delete; - Driver(FileLine* flp, uint32_t low, DfgVertex* vtxp) - : m_flp{flp} - , m_low{low} - , m_vtxp{vtxp} {} - }; - - // STATE - DfgGraph& m_dfg; // The graph being processed - V3DfgAstToDfgContext& m_ctx; // The context for stats - - // METHODS - - // Coalesce packed driver - return the coalesced vertex and location for 'splicep' - std::pair coalescePacked(DfgSplicePacked* const splicep) { - UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSplicePacked"); - - // The drivers of 'splicep' - std::vector drivers; - drivers.reserve(splicep->arity()); - - // Gather and unlink all drivers - int64_t prevHigh = -1; // High index of previous driven range - splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - DfgVertex* const driverp = edge.sourcep(); - UASSERT_OBJ(driverp, splicep, "Should not have created undriven sources"); - UASSERT_OBJ(!driverp->is(), splicep, "Should not be DfgVertexSplice"); - const uint32_t low = splicep->driverLsb(i); - UASSERT_OBJ(static_cast(low) > prevHigh, splicep, - "Drivers should have been normalized"); - prevHigh = low + driverp->width() - 1; - // Gather - drivers.emplace_back(splicep->driverFileLine(i), low, driverp); - // Unlink - edge.unlinkSource(); - }); - splicep->resetSources(); - - // Coalesce adjacent ranges - if (drivers.size() > 1) { - size_t mergeInto = 0; - size_t mergeFrom = 1; - do { - Driver& into = drivers[mergeInto]; - Driver& from = drivers[mergeFrom]; - const uint32_t intoWidth = into.m_vtxp->width(); - const uint32_t fromWidth = from.m_vtxp->width(); - - if (into.m_low + intoWidth == from.m_low) { - // Adjacent ranges, coalesce - const auto dtypep = DfgVertex::dtypeForWidth(intoWidth + fromWidth); - DfgConcat* const concatp = new DfgConcat{m_dfg, into.m_flp, dtypep}; - concatp->rhsp(into.m_vtxp); - concatp->lhsp(from.m_vtxp); - into.m_vtxp = concatp; - from.m_vtxp = nullptr; // Mark as moved - ++m_ctx.m_coalescedAssignments; - } else { - // There is a gap - future merges go into the next position - ++mergeInto; - // Move 'from' into the next position, unless it's already there - if (mergeFrom != mergeInto) { - Driver& next = drivers[mergeInto]; - UASSERT_OBJ(!next.m_vtxp, next.m_flp, "Should have been marked moved"); - next = from; - from.m_vtxp = nullptr; // Mark as moved - } - } - - // Consider next driver - ++mergeFrom; - } while (mergeFrom < drivers.size()); - // Rightsize vector - drivers.erase(drivers.begin() + (mergeInto + 1), drivers.end()); - } - - // If the variable is driven whole, we can just use that driver - if (drivers.size() == 1 // - && drivers[0].m_low == 0 // - && drivers[0].m_vtxp->width() == splicep->width()) { - VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); - // Use the driver directly - return {drivers[0].m_vtxp, drivers[0].m_flp}; - } - - // Reinsert drivers in order - for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); - // Use the original splice - return {splicep, splicep->fileline()}; - } - - // Coalesce array driver - return the coalesced vertex and location for 'splicep' - std::pair coalesceArray(DfgSpliceArray* const splicep) { - UASSERT_OBJ(splicep->arity() >= 1, splicep, "Undriven DfgSpliceArray"); - - // The drivers of 'splicep' - std::vector drivers; - drivers.reserve(splicep->arity()); - - // Coalesce, gather and unlink all drivers - int64_t prevHigh = -1; // High index of previous driven range - splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - DfgVertex* driverp = edge.sourcep(); - UASSERT_OBJ(driverp, splicep, "Should not have created undriven sources"); - const uint32_t low = splicep->driverIndex(i); - UASSERT_OBJ(static_cast(low) > prevHigh, splicep, - "Drivers should have been normalized"); - prevHigh = low; - FileLine* flp = splicep->driverFileLine(i); - // Coalesce - if (DfgSplicePacked* const spp = driverp->cast()) { - std::tie(driverp, flp) = coalescePacked(spp); - } else if (DfgSpliceArray* const sap = driverp->cast()) { - std::tie(driverp, flp) = coalesceArray(sap); - } else if (driverp->is()) { - driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - // Gather - drivers.emplace_back(flp, low, driverp); - // Unlink - edge.unlinkSource(); - }); - splicep->resetSources(); - - // If the variable is driven whole, we can just use that driver - if (drivers.size() == 1 // - && drivers[0].m_low == 0 // - && drivers[0].m_vtxp->dtypep()->isSame(splicep->dtypep())) { - VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); - // Use the driver directly - return {drivers[0].m_vtxp, drivers[0].m_flp}; - } - - // Reinsert drivers in order - for (const Driver& d : drivers) splicep->addDriver(d.m_flp, d.m_low, d.m_vtxp); - // Use the original splice - return {splicep, splicep->fileline()}; - } - - // CONSTRUCTOR - AstToDfgCoalesceDrivers(DfgGraph& dfg, DfgVertexVar& var, V3DfgAstToDfgContext& ctx) - : m_dfg{dfg} - , m_ctx{ctx} { - // Nothing to do for un-driven (input) variables - if (!var.srcp()) return; - - // The driver of a variable must always be a splice vertex, coalesce it - std::pair normalizedDriver; - if (DfgSpliceArray* const sArrayp = var.srcp()->cast()) { - normalizedDriver = coalesceArray(sArrayp); - } else if (DfgSplicePacked* const sPackedp = var.srcp()->cast()) { - normalizedDriver = coalescePacked(sPackedp); - } else { - var.v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE - } - var.srcp(normalizedDriver.first); - var.driverFileLine(normalizedDriver.second); - } - -public: - // Coalesce drivers of given variable - static void apply(DfgGraph& dfg, DfgVertexVar& var, V3DfgAstToDfgContext& ctx) { - AstToDfgCoalesceDrivers{dfg, var, ctx}; - } -}; - -// Visitor that converts a whole module (when T_Scoped is false), -// or the whole netlist (when T_Scoped is true). -template -class AstToDfgVisitor final : public VNVisitor { - // NODE STATE - const VNUser2InUse m_user2InUse; // Used by AstToDfgConverter - - // TYPES - using RootType = std::conditional_t; - using Variable = std::conditional_t; - - // STATE - AstToDfgConverter m_converter; // The convert instance to use for each construct - - // METHODS - static Variable* getTarget(const AstVarRef* refp) { - // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works - if VL_CONSTEXPR_CXX17 (T_Scoped) { - return reinterpret_cast(refp->varScopep()); - } else { - return reinterpret_cast(refp->varp()); - } - } - - // Mark variables referenced under node - static void markReferenced(AstNode* nodep) { - nodep->foreach([](const AstVarRef* refp) { - Variable* const tgtp = getTarget(refp); - // Mark as read from non-DFG logic - if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp); - // Mark as written from non-DFG logic - if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp); - }); + // Gather written variables, give up if any are not supported + const std::unique_ptr> oVarpsp = gatherWritten(nodep); + if (!oVarpsp) return false; + // Gather read variables, give up if any are not supported + const std::unique_ptr> iVarpsp = gatherLive(*cfgp); + if (!iVarpsp) return false; + // Create the DfgLogic + DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, std::move(cfgp)}; + // Connect it up + connect(*logicp, *iVarpsp, *oVarpsp); + // Done + ++m_ctx.m_representable; + return true; } // VISITORS @@ -975,15 +249,16 @@ class AstToDfgVisitor final : public VNVisitor { // Potentially representable constructs void visit(AstAssignW* nodep) override { - if (!m_converter.convert(nodep)) markReferenced(nodep); + if (!convert(nodep)) markReferenced(nodep); } void visit(AstAlways* nodep) override { - if (!m_converter.convert(nodep)) markReferenced(nodep); + if (!convert(nodep)) markReferenced(nodep); } // CONSTRUCTOR AstToDfgVisitor(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) - : m_converter{dfg, ctx} { + : m_dfg{dfg} + , m_ctx{ctx} { iterate(&root); } @@ -991,16 +266,10 @@ public: static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) { // Convert all logic under 'root' AstToDfgVisitor{dfg, root, ctx}; - if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-conv"); - // Normalize and coalesce all variable drivers - for (DfgVertexVar& var : dfg.varVertices()) { - AstToDfgNormalizeDrivers::apply(dfg, var); - AstToDfgCoalesceDrivers::apply(dfg, var, ctx); + // Remove unread and undriven variables (created when something failed to convert) + for (DfgVertexVar* const varp : dfg.varVertices().unlinkable()) { + if (!varp->srcp() && !varp->hasSinks()) VL_DO_DANGLING(varp->unlinkDelete(dfg), varp); } - if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-norm"); - // Remove all unused vertices - V3DfgPasses::removeUnused(dfg); - if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "ast2dfg-prun"); } }; diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index cf9d718cb..9d0d852d5 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -20,6 +20,7 @@ #include "V3DfgPasses.h" #include "V3Hash.h" +#include #include #include #include @@ -116,7 +117,7 @@ class TraceDriver final : public DfgVisitor { // Vertex is DfgConst, in which case this code is unreachable ... using Vtx = typename std::conditional::value, DfgSel, Vertex>::type; - AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); + AstNodeDType* const dtypep = DfgGraph::dtypePacked(width); Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; vtxp->template setUser(0); m_newVtxps.emplace_back(vtxp); @@ -281,18 +282,94 @@ class TraceDriver final : public DfgVisitor { } void visit(DfgSplicePacked* vtxp) override { - // Proceed with the driver that wholly covers the searched bits + struct Driver final { + DfgVertex* m_vtxp; + uint32_t m_lsb; // LSB of driven range (internal, not Verilog) + uint32_t m_msb; // MSB of driven range (internal, not Verilog) + Driver() = delete; + Driver(DfgVertex* vtxp, uint32_t lsb, uint32_t msb) + : m_vtxp{vtxp} + , m_lsb{lsb} + , m_msb{msb} {} + }; + std::vector drivers; + DfgVertex* const defaultp = vtxp->defaultp(); + + // Look at all the drivers, one might cover the whole range, but also gathe all drivers const auto pair = vtxp->sourceEdges(); + bool tryWholeDefault = defaultp; for (size_t i = 0; i < pair.second; ++i) { DfgVertex* const srcp = pair.first[i].sourcep(); - const uint32_t lsb = vtxp->driverLsb(i); + if (srcp == defaultp) continue; + + const uint32_t lsb = vtxp->driverLo(i); const uint32_t msb = lsb + srcp->width() - 1; - // If it does not cover the searched bit range, move on + drivers.emplace_back(srcp, lsb, msb); + // Check if this driver covers any of the bits, then we can't use whole default + if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false; + // If it does not cover the whole searched bit range, move on if (m_lsb < lsb || msb < m_msb) continue; - // Trace this driver + // Driver covers whole search range, trace that and we are done SET_RESULT(trace(srcp, m_msb - lsb, m_lsb - lsb)); return; } + + // Trace the default driver if no other drivers cover the searched range + if (defaultp && tryWholeDefault) { + SET_RESULT(trace(defaultp, m_msb, m_lsb)); + return; + } + + // Hard case: We need to combine multiple drivers to produce the searched bit range + + // Sort ragnes (they are non-overlapping) + std::sort(drivers.begin(), drivers.end(), + [](const Driver& a, const Driver& b) { return a.m_lsb < b.m_lsb; }); + + // Gather terms + std::vector termps; + for (const Driver& driver : drivers) { + // Driver is below the searched LSB, move on + if (m_lsb > driver.m_msb) continue; + // Driver is above the searched MSB, done + if (driver.m_lsb > m_msb) break; + // Gap below this driver, trace default to fill it + if (driver.m_lsb > m_lsb) { + if (!defaultp) return; + DfgVertex* const termp = trace(defaultp, driver.m_lsb - 1, m_lsb); + if (!termp) return; + termps.emplace_back(termp); + m_lsb = driver.m_lsb; + } + // Driver covers searched range, pick the needed/available bits + uint32_t lim = std::min(m_msb, driver.m_msb); + DfgVertex* const termp + = trace(driver.m_vtxp, lim - driver.m_lsb, m_lsb - driver.m_lsb); + if (!termp) return; + termps.emplace_back(termp); + m_lsb = lim + 1; + } + if (m_msb >= m_lsb) { + if (!defaultp) return; + DfgVertex* const termp = trace(defaultp, m_msb, m_lsb); + if (!termp) return; + termps.emplace_back(termp); + } + + // The earlier cheks cover the case when either a whole driver or the default covers + // the whole range, so there should be at least 2 terms required here. + UASSERT_OBJ(termps.size() >= 2, vtxp, "Should have returned in special cases"); + + // Concatenate all terms and set result + DfgVertex* resp = termps.front(); + for (size_t i = 1; i < termps.size(); ++i) { + DfgVertex* const termp = termps[i]; + DfgConcat* const catp = make(termp, resp->width() + termp->width()); + catp->rhsp(resp); + catp->lhsp(termp); + resp = catp; + } + SET_RESULT(resp); } void visit(DfgVarPacked* vtxp) override { @@ -315,12 +392,24 @@ class TraceDriver final : public DfgVisitor { } // Find driver if (!varp->srcp()) return; - DfgSpliceArray* const splicep = varp->srcp()->cast(); - if (!splicep) return; - DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); - if (!driverp) return; + + // Driver might be a splice + if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { + DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + if (!driverp) return; + DfgUnitArray* const uap = driverp->cast(); + if (!uap) return; + // Trace the driver + SET_RESULT(trace(uap->srcp(), m_msb, m_lsb)); + return; + } + + // Or a unit array + DfgUnitArray* const uap = varp->srcp()->cast(); + if (!uap) return; // Trace the driver - SET_RESULT(trace(driverp, m_msb, m_lsb)); + UASSERT_OBJ(idxp->toSizeT() == 0, vtxp, "Array Index out of range"); + SET_RESULT(trace(uap->srcp(), m_msb, m_lsb)); } void visit(DfgConcat* vtxp) override { @@ -629,9 +718,12 @@ class IndependentBits final : public DfgVisitor { void visit(DfgSplicePacked* vtxp) override { // Combine the masks of all drivers V3Number& m = MASK(vtxp); + DfgVertex* const defaultp = vtxp->defaultp(); + if (defaultp) m = MASK(defaultp); vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { const DfgVertex* const srcp = edge.sourcep(); - m.opSelInto(MASK(srcp), vtxp->driverLsb(i), srcp->width()); + if (srcp == defaultp) return; + m.opSelInto(MASK(srcp), vtxp->driverLo(i), srcp->width()); }); } @@ -656,8 +748,10 @@ class IndependentBits final : public DfgVisitor { if (!splicep) return; DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); if (!driverp) return; + DfgUnitArray* const uap = driverp->cast(); + if (!uap) return; // Update mask - MASK(vtxp) = MASK(driverp); + MASK(vtxp) = MASK(uap->srcp()); } void visit(DfgConcat* vtxp) override { @@ -829,7 +923,9 @@ class IndependentBits final : public DfgVisitor { if (VN_IS(currp->dtypep(), UnpackArrayDType)) { // For an unpacked array vertex, just enque it's sinks. // (There can be no loops through arrays directly) - currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); }); + currp->forEachSink([&](DfgVertex& vtx) { + if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + }); continue; } @@ -843,7 +939,9 @@ class IndependentBits final : public DfgVisitor { // If mask changed, enqueue sinks if (!prevMask.isCaseEq(maskCurr)) { - currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); }); + currp->forEachSink([&](DfgVertex& vtx) { + if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + }); // Check the mask only ever contrects (no bit goes 0 -> 1) if (VL_UNLIKELY(v3Global.opt.debugCheck())) { @@ -998,7 +1096,7 @@ class FixUpIndependentRanges final { } // Fall back on using the part of the variable (if dependent, or trace failed) if (!termp) { - AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(width); + AstNodeDType* const dtypep = DfgGraph::dtypePacked(width); DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; // Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp' selp->setUser(vtxp->getUser()); @@ -1079,7 +1177,7 @@ class FixUpIndependentRanges final { for (size_t i = 1; i < termps.size(); ++i) { DfgVertex* const termp = termps[i]; const uint32_t catWidth = replacementp->width() + termp->width(); - AstNodeDType* const dtypep = DfgVertex::dtypeForWidth(catWidth); + AstNodeDType* const dtypep = DfgGraph::dtypePacked(catWidth); DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; catp->rhsp(replacementp); catp->lhsp(termp); diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index 87487ad56..9fad9e2ee 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -69,45 +69,23 @@ class V3DfgAstToDfgContext final : public V3DfgSubContext { public: // STATE - VDouble0 m_coalescedAssignments; // Number of partial assignments coalesced - VDouble0 m_inputEquations; // Number of input combinational equations + VDouble0 m_inputs; // Number of input processes (logic constructs) VDouble0 m_representable; // Number of combinational equations representable - VDouble0 m_nonRepDType; // Equations non-representable due to data type - VDouble0 m_nonRepImpure; // Equations non-representable due to impure node - VDouble0 m_nonRepTiming; // Equations non-representable due to timing control - VDouble0 m_nonRepLhs; // Equations non-representable due to lhs - VDouble0 m_nonRepNode; // Equations non-representable due to node type - VDouble0 m_nonRepUnknown; // Equations non-representable due to unknown node - VDouble0 m_nonRepVarRef; // Equations non-representable due to variable reference - VDouble0 m_nonRepWidth; // Equations non-representable due to width mismatch + VDouble0 m_nonRepCfg; // Non-representable due to failing to build CFG + VDouble0 m_nonRepLive; // Non-representable due to failing liveness analysis + VDouble0 m_nonRepVar; // Non-representable due to unsupported variable properties private: V3DfgAstToDfgContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "AstToDfg"} {} ~V3DfgAstToDfgContext() { - addStat("coalesced assignments", m_coalescedAssignments); - addStat("input equations", m_inputEquations); + addStat("input processes", m_inputs); addStat("representable", m_representable); - addStat("non-representable (dtype)", m_nonRepDType); - addStat("non-representable (impure)", m_nonRepImpure); - addStat("non-representable (timing)", m_nonRepTiming); - addStat("non-representable (lhs)", m_nonRepLhs); - addStat("non-representable (node)", m_nonRepNode); - addStat("non-representable (unknown)", m_nonRepUnknown); - addStat("non-representable (var ref)", m_nonRepVarRef); - addStat("non-representable (width)", m_nonRepWidth); - + addStat("non-representable (cfg)", m_nonRepCfg); + addStat("non-representable (live)", m_nonRepLive); + addStat("non-representable (var)", m_nonRepVar); // Check the stats are consistent - UASSERT(m_representable // - + m_nonRepDType // - + m_nonRepImpure // - + m_nonRepTiming // - + m_nonRepLhs // - + m_nonRepNode // - + m_nonRepUnknown // - + m_nonRepVarRef // - + m_nonRepWidth // - == m_inputEquations, + UASSERT(m_representable + m_nonRepCfg + m_nonRepLive + m_nonRepVar == m_inputs, "Inconsistent statistics"); } }; @@ -167,12 +145,18 @@ class V3DfgDfgToAstContext final : public V3DfgSubContext { public: // STATE + VDouble0 m_outputVariables; // Number of output variables + VDouble0 m_outputVariablesWithDefault; // Number of outptu variables with a default driver VDouble0 m_resultEquations; // Number of result combinational equations private: V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "DfgToAst"} {} - ~V3DfgDfgToAstContext() { addStat("result equations", m_resultEquations); } + ~V3DfgDfgToAstContext() { + addStat("output variables", m_outputVariables); + addStat("output variables with default driver", m_outputVariablesWithDefault); + addStat("result equations", m_resultEquations); + } }; class V3DfgEliminateVarsContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance @@ -223,6 +207,115 @@ private: : V3DfgSubContext{ctx, label, "Regularize"} {} ~V3DfgRegularizeContext() { addStat("temporaries introduced", m_temporariesIntroduced); } }; +class V3DfgSynthesisContext final : public V3DfgSubContext { + // Only V3DfgContext can create an instance + friend class V3DfgContext; + +public: + // STATE + + // Stats for conversion + struct { + // Inputs + VDouble0 inputAssignments; // Number of input assignments + VDouble0 inputExpressions; // Number of input equations + // Successful + VDouble0 representable; // Number of representable constructs + // Unsuccessful + VDouble0 nonRepImpure; // Non representable: impure + VDouble0 nonRepDType; // Non representable: unsupported data type + VDouble0 nonRepLValue; // Non representable: unsupported LValue form + VDouble0 nonRepVarRef; // Non representable: unsupported var reference + VDouble0 nonRepNode; // Non representable: unsupported AstNode type + VDouble0 nonRepUnknown; // Non representable: unhandled AstNode type + } m_conv; + + // Stats for synthesis + struct { + // Inputs + VDouble0 inputAlways; // Number of always blocks attempted + VDouble0 inputAssign; // Number of continuous assignments attempted + // Successful + VDouble0 synthAlways; // Number of always blocks successfully synthesized + VDouble0 synthAssign; // Number of continuous assignments successfully synthesized + // Unsuccessful + VDouble0 nonSynConv; // Non synthesizable: non representable (above) + VDouble0 nonSynExtWrite; // Non synthesizable: has externally written variable + VDouble0 nonSynLoop; // Non synthesizable: loop in CFG + VDouble0 nonSynStmt; // Non synthesizable: unsupported statement + VDouble0 nonSynMultidrive; // Non synthesizable: multidriven value within statement + VDouble0 nonSynArray; // Non synthesizable: array type unhandled + VDouble0 nonSynLatch; // Non synthesizable: maybe latch + VDouble0 nonSynJoinInput; // Non synthesizable: needing to join input variable + VDouble0 nonSynFalseWrite; // Non synthesizable: does not write output + // Reverted + VDouble0 revertNonSyn; // Reverted due to being driven from non-synthesizable vertex + VDouble0 revertMultidrive; // Reverted due to multiple drivers + + } m_synt; + +private: + V3DfgSynthesisContext(V3DfgContext& ctx, const std::string& label) + : V3DfgSubContext{ctx, label, "Synthesis"} {} + ~V3DfgSynthesisContext() { + // Conversion statistics + addStat("conv / input assignments", m_conv.inputAssignments); + addStat("conv / input expressions", m_conv.inputExpressions); + addStat("conv / representable inputs", m_conv.representable); + addStat("conv / non-representable (impure)", m_conv.nonRepImpure); + addStat("conv / non-representable (dtype)", m_conv.nonRepDType); + addStat("conv / non-representable (lhs)", m_conv.nonRepLValue); + addStat("conv / non-representable (varref)", m_conv.nonRepVarRef); + addStat("conv / non-representable (node)", m_conv.nonRepNode); + addStat("conv / non-representable (unknown)", m_conv.nonRepUnknown); + VDouble0 nConvNonRep; + nConvNonRep += m_conv.nonRepImpure; + nConvNonRep += m_conv.nonRepDType; + nConvNonRep += m_conv.nonRepLValue; + nConvNonRep += m_conv.nonRepVarRef; + nConvNonRep += m_conv.nonRepNode; + nConvNonRep += m_conv.nonRepUnknown; + VDouble0 nConvExpect; + nConvExpect += m_conv.inputAssignments; + nConvExpect += m_conv.inputExpressions; + nConvExpect -= m_conv.representable; + UASSERT(nConvNonRep == nConvExpect, "Inconsistent statistics / conv"); + + // Synthesis statistics + addStat("synt / always blocks considered", m_synt.inputAlways); + addStat("synt / always blocks synthesized", m_synt.synthAlways); + addStat("synt / continuous assignments considered", m_synt.inputAssign); + addStat("synt / continuous assignments synthesized", m_synt.synthAssign); + addStat("synt / non-synthesizable (conv)", m_synt.nonSynConv); + addStat("synt / non-synthesizable (ext write)", m_synt.nonSynExtWrite); + addStat("synt / non-synthesizable (loop)", m_synt.nonSynLoop); + addStat("synt / non-synthesizable (stmt)", m_synt.nonSynStmt); + addStat("synt / non-synthesizable (multidrive)", m_synt.nonSynMultidrive); + addStat("synt / non-synthesizable (array)", m_synt.nonSynArray); + addStat("synt / non-synthesizable (latch)", m_synt.nonSynLatch); + addStat("synt / non-synthesizable (join input)", m_synt.nonSynJoinInput); + addStat("synt / non-synthesizable (false write)", m_synt.nonSynFalseWrite); + addStat("synt / reverted (non-synthesizable)", m_synt.revertNonSyn); + addStat("synt / reverted (multidrive)", m_synt.revertMultidrive); + + VDouble0 nSyntNonSyn; + nSyntNonSyn += m_synt.nonSynConv; + nSyntNonSyn += m_synt.nonSynExtWrite; + nSyntNonSyn += m_synt.nonSynLoop; + nSyntNonSyn += m_synt.nonSynStmt; + nSyntNonSyn += m_synt.nonSynMultidrive; + nSyntNonSyn += m_synt.nonSynArray; + nSyntNonSyn += m_synt.nonSynLatch; + nSyntNonSyn += m_synt.nonSynJoinInput; + nSyntNonSyn += m_synt.nonSynFalseWrite; + VDouble0 nSyntExpect; + nSyntExpect += m_synt.inputAlways; + nSyntExpect += m_synt.inputAssign; + nSyntExpect -= m_synt.synthAlways; + nSyntExpect -= m_synt.synthAssign; + UASSERT(nSyntNonSyn == nSyntExpect, "Inconsistent statistics / synt"); + } +}; ////////////////////////////////////////////////////////////////////////////// // Top level V3DfgContext @@ -248,6 +341,7 @@ public: V3DfgEliminateVarsContext m_eliminateVarsContext{*this, m_label}; V3DfgPeepholeContext m_peepholeContext{*this, m_label}; V3DfgRegularizeContext m_regularizeContext{*this, m_label}; + V3DfgSynthesisContext m_synthContext{*this, m_label}; // Node pattern collector V3DfgPatternStats m_patternStats; diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 214dd13c8..8de979b4b 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -199,6 +199,7 @@ class ExtractCyclicComponents final { } UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type"); clonep->setUser(component); + clonep->tmpForp(vtx.tmpForp()); } return *clonep; } diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index de5f5c125..09df3a63f 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -93,12 +93,23 @@ class DfgToAstVisitor final : DfgVisitor { // TYPES using VariableType = std::conditional_t; + struct Assignment final { + FileLine* m_flp; + AstNodeExpr* m_lhsp; + AstNodeExpr* m_rhsp; + Assignment() = delete; + Assignment(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* m_rhsp) + : m_flp{flp} + , m_lhsp{lhsp} + , m_rhsp{m_rhsp} {} + }; // STATE - AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped V3DfgDfgToAstContext& m_ctx; // The context for stats AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal + std::vector m_assignments; // Assignments to currently rendered variable + std::vector m_defaults; // Default assignments to currently rendered variable // METHODS @@ -147,57 +158,69 @@ class DfgToAstVisitor final : DfgVisitor { return resultp; } - void convertDriver(AstScope* scopep, FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) { - if (!driverp->is()) { - // Base case: assign vertex to current lhs - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp); - AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp}; - lhsp->foreach([flp](AstNode* nodep) { nodep->fileline(flp); }); - if VL_CONSTEXPR_CXX17 (T_Scoped) { - // Add it to the scope holding the target variable - getCombActive(scopep)->addStmtsp(assignp); - } else { - // Add it to the parend module of the DfgGraph - m_modp->addStmtsp(assignp); - } - ++m_ctx.m_resultEquations; - return; - } - + void convertDriver(std::vector& assignments, FileLine* flp, AstNodeExpr* lhsp, + DfgVertex* driverp) { if (DfgSplicePacked* const sPackedp = driverp->cast()) { - // Partial assignment of packed value + // Render defaults first + if (DfgVertex* const defaultp = sPackedp->defaultp()) { + convertDriver(m_defaults, flp, lhsp, defaultp); + } + // Render partial assignments of packed value sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - UASSERT_OBJ(edge.sourcep(), sPackedp, "Should have removed undriven sources"); + DfgVertex* const srcp = edge.sourcep(); + if (srcp == sPackedp->defaultp()) return; // Create Sel FileLine* const dflp = sPackedp->driverFileLine(i); - AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLsb(i)}; - const int width = static_cast(edge.sourcep()->width()); + AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLo(i)}; + const int width = static_cast(srcp->width()); AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width}; // Convert source - convertDriver(scopep, dflp, nLhsp, edge.sourcep()); - // Delete Sel if not consumed - if (!nLhsp->backp()) VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); + convertDriver(assignments, dflp, nLhsp, srcp); + // Delete Sel - was cloned + VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); }); return; } if (DfgSpliceArray* const sArrayp = driverp->cast()) { + UASSERT_OBJ(!sArrayp->defaultp(), flp, "Should not have a default assignment yet"); // Partial assignment of array variable sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - UASSERT_OBJ(edge.sourcep(), sArrayp, "Should have removed undriven sources"); + DfgVertex* const driverp = edge.sourcep(); + UASSERT_OBJ(driverp, sArrayp, "Should have removed undriven sources"); + UASSERT_OBJ(driverp->size() == 1, driverp, "We only handle single elements"); // Create ArraySel FileLine* const dflp = sArrayp->driverFileLine(i); - AstConst* const idxp = new AstConst{dflp, sArrayp->driverIndex(i)}; + AstConst* const idxp = new AstConst{dflp, sArrayp->driverLo(i)}; AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp}; // Convert source - convertDriver(scopep, dflp, nLhsp, edge.sourcep()); - // Delete ArraySel if not consumed - if (!nLhsp->backp()) VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); + if (DfgUnitArray* const uap = driverp->cast()) { + convertDriver(assignments, dflp, nLhsp, uap->srcp()); + } else { + convertDriver(assignments, dflp, nLhsp, driverp); + } + // Delete ArraySel - was cloned + VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); }); return; } - driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE + if (DfgUnitArray* const uap = driverp->cast()) { + // Single element array being assigned a unit array. Needs an ArraySel. + AstConst* const idxp = new AstConst{flp, 0}; + AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp}; + // Convert source + convertDriver(assignments, flp, nLhsp, uap->srcp()); + // Delete ArraySel - was cloned + VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); + return; + } + + // Base case: assign vertex to current lhs + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp); + assignments.emplace_back(flp, lhsp->cloneTreePure(false), rhsp); + ++m_ctx.m_resultEquations; + return; } // VISITORS @@ -245,13 +268,53 @@ class DfgToAstVisitor final : DfgVisitor { // If there is no driver (this vertex is an input to the graph), then nothing to do. if (!vtx.srcp()) continue; + ++m_ctx.m_outputVariables; + // Render variable assignments FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline(); - AstScope* const scopep = T_Scoped ? vtx.varScopep()->scopep() : nullptr; AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE}; - convertDriver(scopep, flp, lhsp, vtx.srcp()); - // convetDriver clones and might not use up the original lhsp - if (!lhsp->backp()) VL_DO_DANGLING(lhsp->deleteTree(), lhsp); + convertDriver(m_assignments, flp, lhsp, vtx.srcp()); + // convetDriver always clones lhsp + VL_DO_DANGLING(lhsp->deleteTree(), lhsp); + + if (m_defaults.empty()) { + // If there are no default assignments, render each driver as an AssignW + for (const Assignment& a : m_assignments) { + AstAssignW* const assignp = new AstAssignW{a.m_flp, a.m_lhsp, a.m_rhsp}; + a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); }); + if VL_CONSTEXPR_CXX17 (T_Scoped) { + // Add it to the scope holding the target variable + getCombActive(vtx.varScopep()->scopep())->addStmtsp(assignp); + } else { + // Add it to the parent module of the DfgGraph + m_modp->addStmtsp(assignp); + } + } + } else { + ++m_ctx.m_outputVariablesWithDefault; + // If there are default assignments, render all drivers under an AstAlways + AstAlways* const alwaysp + = new AstAlways{vtx.fileline(), VAlwaysKwd::ALWAYS_COMB, nullptr, nullptr}; + if VL_CONSTEXPR_CXX17 (T_Scoped) { + // Add it to the scope holding the target variable + getCombActive(vtx.varScopep()->scopep())->addStmtsp(alwaysp); + } else { + // Add it to the parent module of the DfgGraph + m_modp->addStmtsp(alwaysp); + } + for (const Assignment& a : m_defaults) { + AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp}; + a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); }); + alwaysp->addStmtsp(assignp); + } + for (const Assignment& a : m_assignments) { + AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp}; + a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); }); + alwaysp->addStmtsp(assignp); + } + } + m_assignments.clear(); + m_defaults.clear(); } } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 5f7e34ced..3a12470fb 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -252,7 +252,11 @@ class DataflowOptimize final { V3DfgContext m_ctx; // The context holding values that need to persist across multiple graphs void optimize(DfgGraph& dfg) { - if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "whole-input"); + if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "dfg-in"); + + // Synthesize DfgLogic vertices + V3DfgPasses::synthesize(dfg, m_ctx); + if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "synth"); // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of @@ -310,7 +314,7 @@ class DataflowOptimize final { // Merge back under the main DFG (we will convert everything back in one go) dfg.mergeGraphs(std::move(cyclicComponents)); - if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "whole-optimized"); + if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "dfg-out"); } DataflowOptimize(AstNetlist* netlistp, const string& label) diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index a6aad4c37..0ea612634 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -103,7 +103,7 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { DfgVertex* const sentinelp = reinterpret_cast(&dfg); DfgVertex* workListp = sentinelp; - // Add all unused vertices to the work list. This also allocates all DfgVertex::user. + // Add all unused operation vertices to the work list. This also allocates all DfgVertex::user. for (DfgVertex& vtx : dfg.opVertices()) { if (vtx.hasSinks()) { // This vertex is used. Allocate user, but don't add to work list. @@ -115,6 +115,19 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { } } + // Also add all unused temporaries created during synthesis + for (DfgVertexVar& vtx : dfg.varVertices()) { + if (!vtx.tmpForp()) continue; + if (vtx.hasSinks() || vtx.hasDfgRefs()) { + // This vertex is used. Allocate user, but don't add to work list. + vtx.setUser(nullptr); + } else { + // This vertex is unused. Add to work list. + vtx.setUser(workListp); + workListp = &vtx; + } + } + // Process the work list while (workListp != sentinelp) { // Pick up the head @@ -123,12 +136,23 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { workListp = vtxp->getUser(); // Prefetch next item VL_PREFETCH_RW(workListp); + // This item is now off the work list + vtxp->setUser(nullptr); + // DfgLogic should have been synthesized or removed + UASSERT_OBJ(!vtxp->is(), vtxp, "Should not be DfgLogic"); // If used, then nothing to do, so move on if (vtxp->hasSinks()) continue; + // If temporary used in another graph, we need to keep it + if (const DfgVertexVar* const varp = vtxp->cast()) { + UASSERT_OBJ(varp->tmpForp(), varp, "Non-temporary variable should not be visited"); + if (varp->hasDfgRefs()) continue; + } // Add sources of unused vertex to work list vtxp->forEachSource([&](DfgVertex& src) { - // We only remove actual operation vertices in this loop - if (src.is() || src.is()) return; + // We only remove actual operation vertices and synthesis temporaries in this loop + if (src.is()) return; + const DfgVertexVar* const varp = src.cast(); + if (varp && !varp->tmpForp()) return; // If already in work list then nothing to do if (src.getUser()) return; // Actually add to work list. @@ -282,7 +306,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // Required data types AstNodeDType* const idxDTypep = srcp->dtypep(); - AstNodeDType* const bitDTypep = DfgVertex::dtypeForWidth(1); + AstNodeDType* const bitDTypep = DfgGraph::dtypePacked(1); AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{ flp, bitDTypep, new AstRange{flp, static_cast(nBits - 1), 0}}; v3Global.rootp()->typeTablep()->addTypesp(tabDTypep); @@ -453,8 +477,13 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { DfgVarPacked* const varp = vtxp->cast(); if (!varp) continue; - // Can't remove if it has external drivers - if (!varp->isDrivenFullyByDfg()) continue; + if (!varp->tmpForp()) { + // Can't remove regular variable if it has external drivers + if (!varp->isDrivenFullyByDfg()) continue; + } else { + // Can't remove partially driven used temporaries + if (!varp->isDrivenFullyByDfg() && varp->hasSinks()) continue; + } // Can't remove if referenced external to the module/netlist if (varp->hasExtRefs()) continue; diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 98ce5e196..93febfa68 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -38,6 +38,10 @@ std::unique_ptr astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED; // Same as above, but for the entire netlist, after V3Scope std::unique_ptr astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED; +// Synthesize DfgLogic vertices into primitive operations. +// Removes all DfgLogic (even those that were not synthesized). +void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED; + // Attempt to make the given cyclic graph into an acyclic, or "less cyclic" // equivalent. If the returned pointer is null, then no improvement was // possible on the input graph. Otherwise the returned graph is an improvement diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index deadbf96f..634009af8 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -131,7 +131,7 @@ class V3DfgPeephole final : public DfgVisitor { // STATE DfgGraph& m_dfg; // The DfgGraph being visited V3DfgPeepholeContext& m_ctx; // The config structure - AstNodeDType* const m_bitDType = DfgVertex::dtypeForWidth(1); // Common, so grab it up front + AstNodeDType* const m_bitDType = DfgGraph::dtypePacked(1); // Common, so grab it up front // Head of work list. Note that we want all next pointers in the list to be non-zero (including // that of the last element). This allows as to do two important things: detect if an element // is in the list by checking for a non-zero next pointer, and easy prefetching without @@ -205,7 +205,7 @@ class V3DfgPeephole final : public DfgVisitor { } // Shorthand - static AstNodeDType* dtypeForWidth(uint32_t width) { return DfgVertex::dtypeForWidth(width); } + static AstNodeDType* dtypePacked(uint32_t width) { return DfgGraph::dtypePacked(width); } // Create a 32-bit DfgConst vertex DfgConst* makeI32(FileLine* flp, uint32_t val) { return new DfgConst{m_dfg, flp, 32, val}; } @@ -365,7 +365,7 @@ class V3DfgPeephole final : public DfgVisitor { // Concatenation dtypes need to be fixed up, other associative nodes preserve // types if VL_CONSTEXPR_CXX17 (std::is_same::value) { - childDtyptp = dtypeForWidth(bp->width() + cp->width()); + childDtyptp = dtypePacked(bp->width() + cp->width()); } Vertex* const childp = make(vtxp->fileline(), childDtyptp, bp, cp); @@ -820,10 +820,10 @@ class V3DfgPeephole final : public DfgVisitor { const uint32_t lSelWidth = width - rSelWidth; // The new Lhs vertex - DfgSel* const newLhsp = make(flp, dtypeForWidth(lSelWidth), lhsp, 0U); + DfgSel* const newLhsp = make(flp, dtypePacked(lSelWidth), lhsp, 0U); // The new Rhs vertex - DfgSel* const newRhsp = make(flp, dtypeForWidth(rSelWidth), rhsp, lsb); + DfgSel* const newRhsp = make(flp, dtypePacked(rSelWidth), rhsp, lsb); // The replacement Concat vertex DfgConcat* const newConcat @@ -912,6 +912,38 @@ class V3DfgPeephole final : public DfgVisitor { } } } + + // Sel from a partial temporary + if (DfgVarPacked* const varp = fromp->cast()) { + if (varp->tmpForp() && varp->srcp()) { + // Must be a splice, otherwise it would have been inlined + DfgSplicePacked* const splicep = varp->srcp()->as(); + + const auto pair = splicep->sourceEdges(); + for (size_t i = 0; i < pair.second; ++i) { + DfgVertex* const driverp = pair.first[i].sourcep(); + // Ignore default, we won't select into that for now ... + if (driverp == splicep->defaultp()) continue; + + const uint32_t dLsb = splicep->driverLo(i); + const uint32_t dMsb = dLsb + driverp->width() - 1; + // If it does not cover the whole searched bit range, move on + if (lsb < dLsb || dMsb < msb) continue; + + // Replace with sel from driver + APPLYING(PUSH_SEL_THROUGH_SPLICE) { + DfgSel* const replacementp = make(vtxp, driverp, lsb - dLsb); + replace(vtxp, replacementp); + // Special case just for this pattern: delete temporary if became unsued + if (!varp->hasSinks() && !varp->hasDfgRefs()) { + addToWorkList(splicep); // So it can be delete itself if unused + VL_DO_DANGLING(varp->unlinkDelete(m_dfg), varp); // Delete it + } + return; + } + } + } + } } //========================================================================= @@ -1179,20 +1211,40 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgArraySel* vtxp) override { - if (DfgConst* const idxp = vtxp->bitp()->cast()) { - if (DfgVarArray* const varp = vtxp->fromp()->cast()) { - if (varp->srcp() && !varp->varp()->isForced()) { - if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { - if (DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT())) { - if (!driverp->is()) { - APPLYING(INLINE_ARRAYSEL) { - replace(vtxp, driverp); - return; - } - } - } - } - } + DfgConst* const idxp = vtxp->bitp()->cast(); + if (!idxp) return; + DfgVarArray* const varp = vtxp->fromp()->cast(); + if (!varp) return; + if (varp->varp()->isForced()) return; + DfgVertex* const srcp = varp->srcp(); + if (!srcp) return; + + if (DfgSpliceArray* const splicep = srcp->cast()) { + DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + if (!driverp) return; + DfgUnitArray* const uap = driverp->cast(); + if (!uap) return; + if (uap->srcp()->is()) return; + // If driven by a variable that had a Driver in DFG, it is partial + if (DfgVertexVar* const dvarp = uap->srcp()->cast()) { + if (dvarp->srcp()) return; + } + APPLYING(INLINE_ARRAYSEL_SPLICE) { + replace(vtxp, uap->srcp()); + return; + } + } + + if (DfgUnitArray* const uap = srcp->cast()) { + UASSERT_OBJ(idxp->toSizeT() == 0, vtxp, "Array index out of range"); + if (uap->srcp()->is()) return; + // If driven by a variable that had a Driver in DFG, it is partial + if (DfgVertexVar* const dvarp = uap->srcp()->cast()) { + if (dvarp->srcp()) return; + } + APPLYING(INLINE_ARRAYSEL_UNIT) { + replace(vtxp, uap->srcp()); + return; } } } @@ -1257,8 +1309,7 @@ class V3DfgPeephole final : public DfgVisitor { if (lSelp->lsb() == rSelp->lsb() + rSelp->width()) { // Two consecutive Sels, make a single Sel. const uint32_t width = lSelp->width() + rSelp->width(); - return make(flp, dtypeForWidth(width), rSelp->fromp(), - rSelp->lsb()); + return make(flp, dtypePacked(width), rSelp->fromp(), rSelp->lsb()); } } return nullptr; diff --git a/src/V3DfgPeepholePatterns.h b/src/V3DfgPeepholePatterns.h index e3fe5c3d0..d1e254bc0 100644 --- a/src/V3DfgPeepholePatterns.h +++ b/src/V3DfgPeepholePatterns.h @@ -33,7 +33,8 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_BINARY) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_SEL) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_UNARY) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, INLINE_ARRAYSEL) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, INLINE_ARRAYSEL_SPLICE) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, INLINE_ARRAYSEL_UNIT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PULL_NOTS_THROUGH_COND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_OP_THROUGH_CONCAT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_REDUCTION) \ @@ -47,6 +48,7 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SEL_THROUGH_NOT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SEL_THROUGH_REPLICATE) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SEL_THROUGH_SHIFTL) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SEL_THROUGH_SPLICE) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_AND_WITH_ONES) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_AND_WITH_SELF) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_CONCAT_OF_ADJOINING_SELS) \ diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 64eef883a..e050e8dc8 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -45,15 +45,11 @@ class DfgRegularize final { // Ensure intermediate values used multiple times are written to variables for (DfgVertex& vtx : m_dfg.opVertices()) { const bool needsIntermediateVariable = [&]() { - // Splice vertices represent partial assignments, so they need a variable - // iff and only if they have a non-variable sink. - if (vtx.is()) { - const bool hasNonVarSink - = vtx.findSink([](const DfgVertex& snk) { // - return !snk.is() && !snk.is(); - }); - return hasNonVarSink; - } + // Splice vertices represent partial assignments. The must flow + // into variables, so they should never need a temporary. + if (vtx.is()) return false; + // Smilarly to splice, UnitArray should never need one eitehr + if (vtx.is()) return false; // Operations without multiple sinks need no variables if (!vtx.hasMultipleSinks()) return false; // Array selects need no variables, they are just memory references diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp new file mode 100644 index 000000000..4f16a52fe --- /dev/null +++ b/src/V3DfgSynthesize.cpp @@ -0,0 +1,1696 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Convert DfgLogic into primitive operations +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// +// Synthesize DfgLogic vertices in as a graph, as created by V3DfgAstToDfg +// into primitive vertices. +// +//************************************************************************* + +#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3Cfg.h" +#include "V3Const.h" +#include "V3Dfg.h" +#include "V3DfgPasses.h" +#include "V3EmitV.h" +#include "V3Os.h" + +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +namespace { + +// Create a DfgVertex out of a AstNodeExpr. For most AstNodeExpr subtypes, this can be done +// automatically. For the few special cases, we provide specializations below +template +T_Vertex* makeVertex(const T_Node* nodep, DfgGraph& dfg) { + return new T_Vertex{dfg, nodep->fileline(), DfgGraph::toDfgDType(nodep->dtypep())}; +} + +template <> +DfgArraySel* makeVertex(const AstArraySel* nodep, DfgGraph& dfg) { + // Some earlier passes create malformed ArraySels, just bail on those... + // See t_bitsel_wire_array_bad + if (VN_IS(nodep->fromp(), Const)) return nullptr; + if (!VN_IS(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) return nullptr; + return new DfgArraySel{dfg, nodep->fileline(), DfgGraph::toDfgDType(nodep->dtypep())}; +} + +} // namespace + +// Visitor that can convert Ast statements and expressions in Dfg vertices +template +class AstToDfgConverter final : public VNVisitor { + // NODE STATE + // AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node + // AstVar::user3() -> int temporary counter for variable + const VNUser3InUse m_user3InUse; + + // TYPES + using Variable = std::conditional_t; + + // STATE + DfgGraph& m_dfg; // The graph being built + V3DfgSynthesisContext& m_ctx; // The context for stats + + // Current logic vertex we are synthesizing + DfgLogic* m_logicp = nullptr; + // Variable updates produced by currently converted statement. This almost + // always have a single element, so a vector is ok + std::vector>* m_updatesp = nullptr; + + bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit' + bool m_converting = false; // We are trying to convert some logic at the moment + + // METHODS + static Variable* getTarget(const AstVarRef* refp) { + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + return reinterpret_cast(refp->varScopep()); + } else { + return reinterpret_cast(refp->varp()); + } + } + + // Allocate a new non-variable vertex, add it to the currently synthesized logic + template + Vertex* make(Args&&... args) { + static_assert(!std::is_base_of::value, "Do not use for variables"); + static_assert(std::is_base_of::value, "'Vertex' must be a 'DfgVertex'"); + Vertex* const vtxp = new Vertex{m_dfg, std::forward(args)...}; + m_logicp->synth().emplace_back(vtxp); + return vtxp; + } + + // Returns true if the expression cannot (or should not) be represented by DFG + bool unhandled(AstNodeExpr* nodep) { + // Short-circuiting if something was already unhandled + if (!m_foundUnhandled) { + // Impure nodes cannot be represented + if (!nodep->isPure()) { + m_foundUnhandled = true; + ++m_ctx.m_conv.nonRepImpure; + } + // Check node has supported dtype + if (!DfgGraph::isSupported(nodep->dtypep())) { + m_foundUnhandled = true; + ++m_ctx.m_conv.nonRepDType; + } + } + return m_foundUnhandled; + } + + bool isSupported(const AstVarRef* nodep) { + // Cannot represent cross module references + if (nodep->classOrPackagep()) return false; + // Check target + return DfgGraph::isSupported(getTarget(nodep)); + } + + // Given an RValue expression, return the equivalent Vertex, or nullptr if not representable. + DfgVertex* convertRValue(AstNodeExpr* nodep) { + UASSERT_OBJ(!m_converting, nodep, "'convertingRValue' should not be called recursively"); + VL_RESTORER(m_converting); + VL_RESTORER(m_foundUnhandled); + m_converting = true; + m_foundUnhandled = false; + + // Convert the expression + iterate(nodep); + + // If falied to convert, return nullptr + if (m_foundUnhandled) return nullptr; + + // Traversal set user2p to the equivalent vertex + DfgVertex* const vtxp = nodep->user2u().to(); + UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex after covnersion"); + return vtxp; + } + + // Given an LValue expression, return the splice node that writes the + // destination, together with the index to use for splicing in the value. + // Returns {nullptr, 0}, if the given LValue expression is not supported. + std::pair convertLValue(AstNodeExpr* nodep) { + if (const AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { + if (!isSupported(vrefp)) { + ++m_ctx.m_conv.nonRepLValue; + return {nullptr, 0}; + } + + // Get (or create a new) temporary for this variable + DfgVertexVar* const vtxp = [&]() -> DfgVertexVar* { + // The variable being assigned + Variable* const tgtp = getTarget(vrefp); + + // Find existing one, if any + for (const auto& pair : *m_updatesp) { + if (pair.first == tgtp) return pair.second; + } + + // Create new one + DfgVertexVar* const newp = createTmp(m_dfg, *m_logicp, tgtp, "SynthAssign"); + m_updatesp->emplace_back(tgtp, newp); + + // Create the Splice driver for the new temporary + if (newp->is()) { + newp->srcp(make(newp->fileline(), newp->dtypep())); + } else if (newp->is()) { + newp->srcp(make(newp->fileline(), newp->dtypep())); + } else { + nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE + } + + // Use new temporary + return newp; + }(); + + // Return the Splice driver + return {vtxp->srcp()->as(), 0}; + } + + if (const AstSel* selp = VN_CAST(nodep, Sel)) { + // Only handle constant selects + const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); + if (!lsbp) { + ++m_ctx.m_conv.nonRepLValue; + return {nullptr, 0}; + } + uint32_t lsb = lsbp->toUInt(); + + // Convert the 'fromp' sub-expression + const auto pair = convertLValue(selp->fromp()); + if (!pair.first) return {nullptr, 0}; + DfgSplicePacked* const splicep = pair.first->template as(); + // Adjust index. + lsb += pair.second; + + // AstSel doesn't change type kind (array vs packed), so we can use + // the existing splice driver with adjusted lsb + return {splicep, lsb}; + } + + if (const AstArraySel* const aselp = VN_CAST(nodep, ArraySel)) { + // Only handle constant selects + const AstConst* const indexp = VN_CAST(aselp->bitp(), Const); + if (!indexp) { + ++m_ctx.m_conv.nonRepLValue; + return {nullptr, 0}; + } + uint32_t index = indexp->toUInt(); + + // Convert the 'fromp' sub-expression + const auto pair = convertLValue(aselp->fromp()); + if (!pair.first) return {nullptr, 0}; + DfgSpliceArray* const splicep = pair.first->template as(); + // Adjust index. Note pair.second is always 0, but we might handle array slices later.. + index += pair.second; + + // Ensure the Splice driver exists for this element + if (!splicep->driverAt(index)) { + FileLine* const flp = nodep->fileline(); + AstNodeDType* const dtypep = DfgGraph::toDfgDType(nodep->dtypep()); + if (VN_IS(dtypep, BasicDType)) { + DfgSplicePacked* const newp = make(flp, dtypep); + AstNodeDType* const uaDtypep = DfgGraph::dtypeArray(dtypep, 1); + DfgUnitArray* const uap = make(flp, uaDtypep); + uap->srcp(newp); + splicep->addDriver(flp, index, uap); + } else if (VN_IS(dtypep, UnpackArrayDType)) { + DfgSpliceArray* const newp = make(flp, dtypep); + splicep->addDriver(flp, index, newp); + } else { + nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE + } + } + + // Return the splice driver + DfgVertex* driverp = splicep->driverAt(index); + if (DfgUnitArray* const uap = driverp->cast()) driverp = uap->srcp(); + return {driverp->as(), 0}; + } + + ++m_ctx.m_conv.nonRepLValue; + return {nullptr, 0}; + } + + // Given the LHS of an assignment, and the vertex representing the RHS, + // connect up the RHS to drive the targets. + // Returns true on success, false if the LHS is not representable. + bool convertAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* vtxp) { + // Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'. + // There might be multiple of these if 'lhsp' is a concatenation. + struct Assignment final { + DfgVertexSplice* m_lhsp; + uint32_t m_idx; + DfgVertex* m_rhsp; + Assignment() = delete; + Assignment(DfgVertexSplice* lhsp, uint32_t idx, DfgVertex* rhsp) + : m_lhsp{lhsp} + , m_idx{idx} + , m_rhsp{rhsp} {} + }; + + // Convert each concatenation LHS separately, gather all assignments + // we need to do into 'assignments', return true if all LValues + // converted successfully. + std::vector assignments; + const std::function convertAllLValues + = [&](AstNodeExpr* lhsp, DfgVertex* vtxp) -> bool { + // Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _) + lhsp = VN_AS(V3Const::constifyExpensiveEdit(lhsp), NodeExpr); + + // Concatenation on the LHS, convert each parts + if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) { + AstNodeExpr* const cLhsp = concatp->lhsp(); + AstNodeExpr* const cRhsp = concatp->rhsp(); + // Convert Left of concat + FileLine* const lFlp = cLhsp->fileline(); + AstNodeDType* const lDtp = DfgGraph::toDfgDType(cLhsp->dtypep()); + DfgSel* const lVtxp = make(lFlp, lDtp); + lVtxp->fromp(vtxp); + lVtxp->lsb(cRhsp->width()); + if (!convertAllLValues(cLhsp, lVtxp)) return false; + // Convert Rigth of concat + FileLine* const rFlp = cRhsp->fileline(); + AstNodeDType* const rDtp = DfgGraph::toDfgDType(cRhsp->dtypep()); + DfgSel* const rVtxp = make(rFlp, rDtp); + rVtxp->fromp(vtxp); + rVtxp->lsb(0); + return convertAllLValues(cRhsp, rVtxp); + } + + // Non-concatenation, convert the LValue + const auto pair = convertLValue(lhsp); + if (!pair.first) return false; + assignments.emplace_back(pair.first, pair.second, vtxp); + return true; + }; + + // Convert the given LHS assignment, give up if any LValues failed to convert + if (!convertAllLValues(lhsp, vtxp)) return false; + + // All successful, connect the drivers + for (const Assignment& item : assignments) { + if (DfgSplicePacked* const spp = item.m_lhsp->template cast()) { + spp->addDriver(flp, item.m_idx, item.m_rhsp); + } else if (DfgSpliceArray* const sap = item.m_lhsp->template cast()) { + AstUnpackArrayDType* const lDtp = VN_AS(sap->dtypep(), UnpackArrayDType); + const AstNodeDType* const lEleDtp = lDtp->subDTypep(); + AstNodeDType* const rDtp = item.m_rhsp->dtypep(); + if (lEleDtp->isSame(rDtp)) { + // RHS is assigning an element of this array. Need a DfgUnitArray adapter. + AstNodeDType* const uaDtp = DfgGraph::dtypeArray(rDtp, 1); + DfgUnitArray* const uap = make(flp, uaDtp); + uap->srcp(item.m_rhsp); + sap->addDriver(flp, item.m_idx, uap); + } else { + // RHS is assigning an array (or array slice). Should be the same element type. + const AstNodeDType* const rEleDtp = VN_AS(rDtp, UnpackArrayDType)->subDTypep(); + UASSERT_OBJ(lEleDtp->isSame(rEleDtp), item.m_rhsp, "Mismatched array types"); + sap->addDriver(flp, item.m_idx, item.m_rhsp); + } + } else { + item.m_lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE + } + } + + return true; + } + + // VISITORS + + // Unhandled node + void visit(AstNode* nodep) override { + if (!m_foundUnhandled && m_converting) ++m_ctx.m_conv.nonRepUnknown; + m_foundUnhandled = true; + } + + // Expressions - mostly auto generated, but a few special ones + void visit(AstVarRef* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + // This visit method is only called on RValues, where only read refs are supported + if (!nodep->access().isReadOnly() || !isSupported(nodep)) { + m_foundUnhandled = true; + ++m_ctx.m_conv.nonRepVarRef; + return; + } + + // Variable should have been bound before starting conversion + DfgVertex* const vtxp = getTarget(nodep)->user2u().template to(); + UASSERT_OBJ(vtxp, nodep, "Referenced variable has no associated DfgVertexVar"); + nodep->user2p(vtxp); + } + void visit(AstConst* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + DfgVertex* const vtxp = make(nodep->fileline(), nodep->num()); + nodep->user2p(vtxp); + } + void visit(AstSel* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + + iterate(nodep->fromp()); + if (m_foundUnhandled) return; + + FileLine* const flp = nodep->fileline(); + DfgVertex* vtxp = nullptr; + if (const AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { + DfgSel* const selp = make(flp, DfgGraph::toDfgDType(nodep->dtypep())); + selp->fromp(nodep->fromp()->user2u().to()); + selp->lsb(constp->toUInt()); + vtxp = selp; + } else { + iterate(nodep->lsbp()); + if (m_foundUnhandled) return; + DfgMux* const muxp = make(flp, DfgGraph::toDfgDType(nodep->dtypep())); + muxp->fromp(nodep->fromp()->user2u().to()); + muxp->lsbp(nodep->lsbp()->user2u().to()); + vtxp = muxp; + } + nodep->user2p(vtxp); + } +// The rest of the visit methods for expressions are generated by 'astgen' +#include "V3Dfg__gen_ast_to_dfg.h" + +public: + // PUBLIC METHODS + + // Create a new temporary variable capable of holding 'varp' + static DfgVertexVar* createTmp(DfgGraph& dfg, DfgLogic& logic, Variable* varp, + const std::string& prefix) { + AstVar* const astVarp = T_Scoped ? reinterpret_cast(varp)->varp() + : reinterpret_cast(varp); + const std::string prfx = prefix + "_" + astVarp->name(); + const std::string name = dfg.makeUniqueName(prfx, astVarp->user3Inc()); + AstNodeDType* const dtypep = DfgGraph::toDfgDType(astVarp->dtypep()); + AstScope* const scp = T_Scoped ? reinterpret_cast(varp)->scopep() : nullptr; + DfgVertexVar* const vtxp = dfg.makeNewVar(astVarp->fileline(), name, dtypep, scp); + logic.synth().emplace_back(vtxp); + vtxp->varp()->isInternal(true); + vtxp->tmpForp(varp); + return vtxp; + } + + // Convert AstAssign to Dfg, return true if successful. + // Fills 'updates' with bindings for assigned variables. + bool convert(std::vector>& updates, DfgLogic& vtx, + AstAssign* nodep) { + UASSERT_OBJ(updates.empty(), nodep, "'updates' should be empty"); + VL_RESTORER(m_updatesp); + VL_RESTORER(m_logicp); + m_updatesp = &updates; + m_logicp = &vtx; + // Assignment with timing control shouldn't make it this far + UASSERT_OBJ(!nodep->timingControlp(), nodep, "Shouldn't make it this far"); + // Convert it + ++m_ctx.m_conv.inputAssignments; + AstNodeExpr* const lhsp = nodep->lhsp(); + AstNodeExpr* const rhsp = nodep->rhsp(); + // Check data types are compatible. + if (!DfgGraph::isSupported(lhsp->dtypep()) || !DfgGraph::isSupported(rhsp->dtypep())) { + ++m_ctx.m_conv.nonRepDType; + return false; + } + // For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c) + if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) { + ++m_ctx.m_conv.nonRepDType; + return false; + } + // Widths should match at this point + UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Mismatched width reached DFG"); + // Convert the RHS expression + DfgVertex* const rVtxp = convertRValue(rhsp); + if (!rVtxp) return false; + // Connect the RHS vertex to the LHS targets + const bool success = convertAssignment(nodep->fileline(), lhsp, rVtxp); + if (success) ++m_ctx.m_conv.representable; + return success; + } + + // Convert RValue expression to Dfg. Returns nullptr if failed. + DfgVertex* convert(DfgLogic& vtx, AstNodeExpr* nodep) { + VL_RESTORER(m_logicp); + m_logicp = &vtx; + // Convert it + ++m_ctx.m_conv.inputExpressions; + DfgVertex* const vtxp = convertRValue(nodep); + if (vtxp) ++m_ctx.m_conv.representable; + return vtxp; + } + + // CONSTRUCTOR + AstToDfgConverter(DfgGraph& dfg, V3DfgSynthesisContext& ctx) + : m_dfg{dfg} + , m_ctx{ctx} {} +}; + +// For debugging, we can stop synthesizing after a certain number of vertices. +// for this we need a global counter (inside the template makes multiple copies) +static size_t s_dfgSynthDebugCount = 0; +// The number of vertices we stop after can be passed in through the environment +// you can then use a bisection search over this value and look at the dumps +// produced with the lowest failing value +static const size_t s_dfgSynthDebugLimit + = std::stoull(V3Os::getenvStr("VERILATOR_DFG_SYNTH_DEBUG", "0")); + +template +class AstToDfgSynthesize final { + // NODE STATE + // AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node + + // TYPES + using Variable = std::conditional_t; + using SymTab = std::unordered_map; + + // Represents a [potentially partial] driver of a variable + struct Driver final { + FileLine* m_flp = nullptr; // Location of driver in source + uint32_t m_lo = 0; // Low index of driven range (internal, not Verilog) + uint32_t m_hi = 0; // High index of driven range (internal, not Verilog) + DfgVertex* m_vtxp = nullptr; // Driving vertex + + Driver() = default; + Driver(FileLine* flp, uint32_t lo, DfgVertex* vtxp) + : m_flp{flp} + , m_lo{lo} + , m_hi{lo + vtxp->size() - 1} + , m_vtxp{vtxp} {} + operator bool() const { return m_vtxp != nullptr; } + + bool operator<(const Driver& other) const { + if (m_lo != other.m_lo) return m_lo < other.m_lo; + if (m_hi != other.m_hi) return m_hi < other.m_hi; + return m_flp->operatorCompare(*other.m_flp) < 0; + } + + bool operator<=(const Driver& other) const { return !(other < *this); } + }; + + // STATE + DfgGraph& m_dfg; // The graph being built + V3DfgSynthesisContext& m_ctx; // The context for stats + AstToDfgConverter m_converter; // The convert instance to use for each construct + DfgLogic* m_logicp = nullptr; // Current logic vertex we are synthesizing + + // Some debug aid: We stop after synthesizing s_dfgSynthDebugLimit vertices (if non-zero). + // This is the problematic logic (last one we synthesize), assuming a bisection search + // over s_dfgSynthDebugLimit. + DfgLogic* m_debugLogicp = nullptr; + // Source (upstream) cone of outputs of m_debugLogicp + std::unique_ptr> m_debugOSrcConep{nullptr}; + + // METHODS + + // Dump current graph for debugging ... + void debugDump(const char* name) { + // If we have the debugged logic, compute the vertices feeding its outputs + if (VL_UNLIKELY(m_debugLogicp)) { + std::vector outputs; + m_debugLogicp->forEachSink([&outputs](const DfgVertex& v) { // + outputs.emplace_back(v.singleSink()->as()); + }); + m_debugOSrcConep = m_dfg.sourceCone(outputs); + } + + if (VL_UNLIKELY(dumpDfgLevel() >= 9 || m_debugOSrcConep)) { + const auto label = m_ctx.prefix() + name; + m_dfg.dumpDotFilePrefixed(label); + if (m_debugOSrcConep) { + // Dump only the subgraph involving the inputs and outputs of the bad vertex + m_dfg.dumpDotFilePrefixed(label + "-min", [&](const DfgVertex& v) -> bool { + return m_debugOSrcConep->count(&v); + }); + } + } + } + + static AstVar* getAstVar(Variable* vp) { + // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works + if VL_CONSTEXPR_CXX17 (T_Scoped) { + return reinterpret_cast(vp)->varp(); + } else { + return reinterpret_cast(vp); + } + } + + // Allocate a new non-variable vertex, add it to the currently synthesized logic + template + Vertex* make(Args&&... args) { + static_assert(!std::is_base_of::value, "Do not use for variables"); + static_assert(std::is_base_of::value, "'Vertex' must be a 'DfgVertex'"); + Vertex* const vtxp = new Vertex{m_dfg, std::forward(args)...}; + if (m_logicp) m_logicp->synth().emplace_back(vtxp); + return vtxp; + } + + // Gather all drivers of a resolved variable + static std::pair, DfgVertex*> gatherDrivers(const DfgVertexSplice* vtxp) { + // Collect them all, check if they are sorted + std::vector drivers; + drivers.reserve(vtxp->arity()); + DfgVertex* const defaultp = vtxp->defaultp(); + bool sorted = true; + vtxp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + DfgVertex* const driverp = edge.sourcep(); + UASSERT_OBJ(driverp, vtxp, "Should not have created undriven sources"); + // Ignore default driver + if (driverp == defaultp) return; + // Collect the driver + drivers.emplace_back(vtxp->driverFileLine(i), vtxp->driverLo(i), driverp); + // Check if drivers are sorted - most often they are + const size_t n = drivers.size(); + if (n >= 2 && drivers[n - 1] < drivers[n - 2]) sorted = false; + }); + + // Sort if unsorted + if (!sorted) std::stable_sort(drivers.begin(), drivers.end()); + + // Done + return {std::move(drivers), defaultp}; + } + + // Gather all synthesized drivers of an unresolved variable + static std::vector gatherDriversUnresolved(const DfgUnresolved* vtxp) { + std::vector drivers; + drivers.reserve(vtxp->arity()); + + // For better locations in error reporting, we unpick concatenations + // which are sometimes introduced by combinint assignments in V3Const. + const std::function gather + = [&](FileLine* flp, uint32_t lo, DfgVertex* vtxp) -> void { + if (DfgConcat* const concatp = vtxp->cast()) { + DfgVertex* const rhsp = concatp->rhsp(); + gather(rhsp->fileline(), lo, rhsp); + DfgVertex* const lhsp = concatp->lhsp(); + gather(lhsp->fileline(), lo + rhsp->width(), lhsp); + return; + } + drivers.emplace_back(flp, lo, vtxp); + }; + + // Gather all synthesized drivers + vtxp->forEachSource([&](const DfgVertex& src) { + // Can ignore the original DfgLogic + if (src.is()) return; + + // Synthesized drivers must be a splice at this point + const DfgVertexSplice* const splicep = src.as(); + splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + DfgVertex* const driverp = edge.sourcep(); + UASSERT_OBJ(driverp, splicep, "Should not have created undriven sources"); + // Ignore the default driver, it's the feedback in circular logic + if (driverp == splicep->defaultp()) return; + // Collect the driver + gather(splicep->driverFileLine(i), splicep->driverLo(i), driverp); + }); + }); + + // Sort the drivers + std::stable_sort(drivers.begin(), drivers.end()); + + // Done + return drivers; + } + + // Given two drivers, combine the driven sub-ranges into the first one if + // possible. First bool returned indicates successfully combined and there + // are no multi-driven bits. Second bool returned indicates we warned + // already about multi-driven bits. + std::pair combineDrivers(DfgVertexVar& var, const std::string& sub, // + Driver& a, const Driver& b) { + // We can only combine array drivers ... + if (a.m_vtxp->isPacked()) return {false, false}; + // ... that both drive a single element ... + if (a.m_lo != b.m_lo) return {false, false}; + const DfgUnitArray* const aUap = a.m_vtxp->template cast(); + if (!aUap) return {false, false}; + const DfgUnitArray* const bUap = b.m_vtxp->template cast(); + if (!bUap) return {false, false}; + // ... and are themeselves partial + const DfgSplicePacked* const aSp = aUap->srcp()->template cast(); + if (!aSp) return {false, false}; + const DfgSplicePacked* const bSp = bUap->srcp()->template cast(); + if (!bSp) return {false, false}; + UASSERT_OBJ(aSp->dtypep()->isSame(bSp->dtypep()), &var, "DTypes should match"); + + // Gather drivers of a + std::vector aDrivers; + DfgVertex* aDefaultp = nullptr; + std::tie(aDrivers, aDefaultp) = gatherDrivers(aSp); + UASSERT_OBJ(!aDefaultp, aSp, "Should not have default driver here"); + + // Gather drivers of b + std::vector bDrivers; + DfgVertex* bDefaultp = nullptr; + std::tie(bDrivers, bDefaultp) = gatherDrivers(bSp); + UASSERT_OBJ(!bDefaultp, bSp, "Should not have default driver here"); + + // Merge them + std::vector abDrivers; + abDrivers.reserve(aDrivers.size() + bDrivers.size()); + std::merge( // + aDrivers.begin(), aDrivers.end(), // + bDrivers.begin(), bDrivers.end(), // + std::back_inserter(abDrivers) // + ); + + // Attempt to resolve them + if (!normalizeDrivers(var, abDrivers, sub + "[" + std::to_string(a.m_lo) + "]")) { + return {false, true}; + } + + // Successfully resolved. Needs a new splice and unit. + FileLine* const flp = var.fileline(); + DfgSplicePacked* const splicep = make(flp, aSp->dtypep()); + for (const Driver& d : abDrivers) splicep->addDriver(d.m_flp, d.m_lo, d.m_vtxp); + DfgUnitArray* const uap = make(flp, aUap->dtypep()); + uap->srcp(splicep); + a.m_vtxp = uap; + return {true, false}; + } + + // Combine and coalesce the given drivers. + // Returns true iff no multi-driven bits are present. + bool normalizeDrivers(DfgVertexVar& var, std::vector& drivers, + const std::string& sub = "") { + if (drivers.empty()) return true; + + // What type of values are we combining + const bool isPacked = drivers[0].m_vtxp->isPacked(); + + // Found a multidriven part ? + bool multiDriven = false; + + // Iterate through the sorted drivers. Index 'i' is the driver we are + // resolving driver 'j' agains, and if required, we merge 'j' into 'i'. + size_t i = 0; + for (size_t j = 1; j < drivers.size();) { + UASSERT_OBJ(i < j, &var, "Invalid iteration"); + Driver& iD = drivers[i]; + Driver& jD = drivers[j]; + + // If 'j' was moved, step forward + if (!jD) { + ++j; + continue; + } + // If 'i' was moved, move 'j' in it's place + if (!iD) { + iD = jD; + jD = Driver{}; + ++j; + continue; + } + + // We have 2 valid drivers now + UASSERT_OBJ(iD <= jD, &var, "Should always be sorted"); + UASSERT_OBJ(jD.m_vtxp->isPacked() == isPacked, &var, "Mixed type drivers"); + + // If no overlap, consider next pair + if (iD.m_hi < jD.m_lo) { + ++i; + if (i == j) ++j; + continue; + } + + // There is an overlap. Attempt to combine them. + bool combined = false; + bool warned = false; + std::tie(combined, warned) = combineDrivers(var, sub, iD, jD); + + // If sucessfully combined, 'j' is no longer needed, it was combined into 'i' + if (combined) { + jD = Driver{}; + ++j; + continue; + } + + // Found overlap that cannot be resolved + multiDriven = true; + // Compare next driver + ++j; + + // Do not warn again if we warned during 'combineDrivers' + if (warned) continue; + + // The variable to warn on + AstNode* const nodep = var.tmpForp() ? var.tmpForp() : var.nodep(); + Variable* const varp = reinterpret_cast(nodep); + + // Loop index often abused, so suppress + if (getAstVar(varp)->isUsedLoopIdx()) continue; + + // Warn the user now + const std::string lo = std::to_string(jD.m_lo); + const std::string hi = std::to_string(std::min(iD.m_hi, jD.m_hi)); + const std::string kind = isPacked ? "Bit" : "Element"; + const std::string part = hi == lo ? (" [" + lo + "]") : ("s [" + hi + ":" + lo + "]"); + + varp->v3warn( // + MULTIDRIVEN, // + kind << part << " of signal '" << varp->prettyName() << sub << "'" + << " have multiple combinational drivers." + << " This can cause performance degradation.\n" + << iD.m_flp->warnOther() << "... Location of offending driver\n" + << iD.m_flp->warnContextPrimary() << '\n' + << jD.m_flp->warnOther() << "... Location of offending driver\n" + << jD.m_flp->warnContextSecondary()); + } + // Rightsize vector + drivers.resize(i + 1); + + // Coalesce adjacent drivers + if (!multiDriven && isPacked) coalesceDrivers(drivers); + + return !multiDriven; + } + + // Coalesce adjacent drivers into single ones + void coalesceDrivers(std::vector& drivers) { + UASSERT(!drivers.empty(), "Can't coalesce 0 drivers"); + UASSERT_OBJ(drivers[0].m_vtxp->isPacked(), drivers[0].m_vtxp, "Can only coalesce packed"); + + size_t i = 0; + for (size_t j = 1; j < drivers.size();) { + UASSERT(i < j, "Invalid iteration"); + Driver& iD = drivers[i]; + Driver& jD = drivers[j]; + + // If 'j' was moved, step forward + if (!jD) { + ++j; + continue; + } + // If 'i' was moved, move 'j' in it's place + if (!iD) { + iD = jD; + jD = Driver{}; + ++j; + continue; + } + + // We have 2 valid drivers now + UASSERT(iD <= jD, "Should always be sorted"); + + // If not adjacent, move on + if (iD.m_hi + 1 != jD.m_lo) { + ++i; + if (i == j) ++j; + continue; + } + + // Coalesce Adjacent ranges, + const auto dtypep = DfgGraph::dtypePacked(iD.m_vtxp->width() + jD.m_vtxp->width()); + DfgConcat* const concatp = make(iD.m_flp, dtypep); + concatp->rhsp(iD.m_vtxp); + concatp->lhsp(jD.m_vtxp); + iD.m_vtxp = concatp; + iD.m_hi = jD.m_hi; + jD = Driver{}; + + // Consider next driver + ++j; + } + // Rightsize vector + drivers.resize(i + 1); + } + + // Make a new splice with the given drivers + DfgVertexSplice* makeSplice(DfgVertexVar& var, const std::vector& newDrivers) { + UASSERT_OBJ(!newDrivers.empty(), &var, "'makeSplice' called with no new drivers"); + // Create new driver + DfgVertexSplice* splicep = nullptr; + if (var.is()) { + splicep = make(var.fileline(), var.dtypep()); + } else if (var.is()) { + splicep = make(var.fileline(), var.dtypep()); + } else { + var.v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE + } + for (const Driver& d : newDrivers) splicep->addDriver(d.m_flp, d.m_lo, d.m_vtxp); + return splicep; + } + + // If any written variables are forced or otherwise udpated from outside, + // we generally cannot synthesie the construct, as we will likely need to + // introduce intermediate values that would not be updated. + static bool hasExternallyWrittenVariable(DfgLogic& vtx) { + return vtx.findSink([](const DfgVertex& sink) -> bool { + // 'sink' is a splice (for which 'vtxp' is an unresolved driver), + // which drives the target variable. + const DfgVertexVar* varp = sink.singleSink()->as(); + if (varp->hasXRefs()) return true; // Target of a hierarchical reference + const AstVar* const astVarp = varp->varp(); + if (astVarp->isForced()) return true; // Forced + if (astVarp->isSigPublic()) return true; // Public + return false; + }); + } + + // Initialzie input symbol table of entry BasicBlock + void initializeEntrySymbolTable(SymTab& iSymTab) { + m_logicp->forEachSource([&](DfgVertex& src) { + DfgVertexVar* const vvp = src.as(); + Variable* const varp = reinterpret_cast(vvp->nodep()); + iSymTab[varp] = vvp; + }); + } + + // Join variable drivers across a control flow confluence (insert muxes ...) + DfgVertexVar* joinDrivers(Variable* varp, DfgVertex* predicatep, // + DfgVertexVar* thenp, DfgVertexVar* elsep) { + UASSERT_OBJ(!predicatep->is(), predicatep, "joinDrivers with cons predicate"); + + AstNode* const thenVarp = thenp->tmpForp() ? thenp->tmpForp() : thenp->nodep(); + AstNode* const elseVarp = elsep->tmpForp() ? elsep->tmpForp() : elsep->nodep(); + UASSERT_OBJ(thenVarp == elseVarp, varp, "Attempting to join unrelated variables"); + + // If both bindings are the the same (variable not updated through either path), + // then there is nothing to do, canuse the same binding + if (thenp == elsep) return thenp; + + // We can't join the input variable just yet, so bail + if (thenp->nodep() == varp) { + ++m_ctx.m_synt.nonSynJoinInput; + return nullptr; + } + if (elsep->nodep() == varp) { + ++m_ctx.m_synt.nonSynJoinInput; + return nullptr; + } + + // Can't do arrays yet + if (VN_IS(thenp->dtypep(), UnpackArrayDType)) { + ++m_ctx.m_synt.nonSynArray; + return nullptr; + } + + // Gather drivers of 'thenp' - only if 'thenp' is not an input to the synthesized block + std::vector tDrivers; + DfgVertex* tDefaultp = nullptr; + std::tie(tDrivers, tDefaultp) = gatherDrivers(thenp->srcp()->as()); + + // Gather drivers of 'elsep' - only if 'thenp' is not an input to the synthesized block + std::vector eDrivers; + DfgVertex* eDefaultp = nullptr; + std::tie(eDrivers, eDefaultp) = gatherDrivers(elsep->srcp()->as()); + + // Default drivers should be the same or not present on either + UASSERT_OBJ(tDefaultp == eDefaultp, varp, "Different default drivers"); + + // Location to use for the join vertices + FileLine* const flp = predicatep->fileline(); + + // Create a fresh temporary for the joined value + DfgVertexVar* const joinp = m_converter.createTmp(m_dfg, *m_logicp, varp, "SynthJoin"); + DfgVertexSplice* const joinSplicep = make(flp, joinp->dtypep()); + joinp->srcp(joinSplicep); + + // If both paths are fully driven, just create a simple conditional + if (tDrivers.size() == 1 // + && tDrivers[0].m_lo == 0 // + && tDrivers[0].m_hi == thenp->width() - 1 // + && eDrivers.size() == 1 // + && eDrivers[0].m_lo == 0 // + && eDrivers[0].m_hi == elsep->width() - 1) { + UASSERT_OBJ(!tDefaultp, varp, "Fully driven variable have default driver"); + + DfgCond* const condp = make(flp, joinp->dtypep()); + condp->condp(predicatep); + condp->thenp(thenp); + condp->elsep(elsep); + joinSplicep->addDriver(tDrivers[0].m_flp, 0, condp); + + // Done + return joinp; + } + + // Otherwise we need to merge them part by part + + // If different bits are driven, then some might not have been assigned.. Latch? + if (tDrivers.size() != eDrivers.size()) { + ++m_ctx.m_synt.nonSynLatch; + return nullptr; + } + + for (size_t i = 0; i < tDrivers.size(); ++i) { + const Driver& tDriver = tDrivers[i]; + const Driver& eDriver = eDrivers[i]; + // If different bits are driven, then some might not have been assigned.. Latch? + if (tDriver.m_lo != eDriver.m_lo || tDriver.m_hi != eDriver.m_hi) { + ++m_ctx.m_synt.nonSynLatch; + return nullptr; + } + + AstNodeDType* const dtypep = DfgGraph::dtypePacked(tDriver.m_hi - tDriver.m_lo + 1); + DfgCond* const condp = make(flp, dtypep); + condp->condp(predicatep); + + // We actally need to select the bits from the joined variables, not use the drivers + DfgSel* const thenSelp = make(flp, tDriver.m_vtxp->dtypep()); + thenSelp->lsb(tDriver.m_lo); + thenSelp->fromp(thenp); + condp->thenp(thenSelp); + + // Same for the 'else' part + DfgSel* const elseSelp = make(flp, eDriver.m_vtxp->dtypep()); + elseSelp->lsb(eDriver.m_lo); + elseSelp->fromp(elsep); + condp->elsep(elseSelp); + + // Add it as a driver to the join + joinSplicep->addDriver(tDriver.m_flp, tDriver.m_lo, condp); + } + + // If there was a default driver, add it to te join + if (tDefaultp) joinSplicep->defaultp(tDefaultp); + + // Done + return joinp; + } + + // Combine the output symbol tables of the predecessors of the given + // BasicBlock to compute the input symtol table for the given block. + bool createInputSymbolTable(SymTab& joined, const BasicBlock& bb, + const BasicBlockMap& bbToOSymTab, + const ControlFlowEdgeMap& edgeToPredicatep) { + // Input symbol table of entry block was previously initialzied + if (bb.inEmpty()) return true; + + // We will fill it in here + UASSERT(joined.empty(), "Unresolved input symbol table should be empty"); + + // Fast path if there is only one predecessor + if (bb.inSize1()) { + joined = bbToOSymTab[*(bb.inEdges().frontp()->fromp()->as())]; + return true; + } + + // Gather predecessors and the path predicates + struct Predecessor final { + const BasicBlock* m_bbp; + DfgVertex* m_predicatep; + Predecessor() = delete; + Predecessor(const BasicBlock* bbp, DfgVertex* predicatep) + : m_bbp{bbp} + , m_predicatep{predicatep} {} + }; + + const std::vector predecessors = [&]() { + std::vector res; + for (const V3GraphEdge& edge : bb.inEdges()) { + const ControlFlowEdge& cfgEdge = static_cast(edge); + res.emplace_back(&cfgEdge.src(), edgeToPredicatep[cfgEdge]); + } + // Sort predecessors topologically. This way later blocks will come + // after earlier blocks, and the entry block will be first if present. + std::sort(res.begin(), res.end(), [](const Predecessor& a, const Predecessor& b) { // + return a.m_bbp->id() < b.m_bbp->id(); + }); + return res; + }(); + + // Start by copying the bindings from the oldest predecessor + joined = bbToOSymTab[*predecessors[0].m_bbp]; + // Join over all other predecessors + for (size_t i = 1; i < predecessors.size(); ++i) { + DfgVertex* const predicatep = predecessors[i].m_predicatep; + const SymTab& oSymTab = bbToOSymTab[*predecessors[i].m_bbp]; + // Give up if something is not assigned on all paths ... Latch? + if (joined.size() != oSymTab.size()) { + ++m_ctx.m_synt.nonSynLatch; + return false; + } + // Join each symbol + for (auto& pair : joined) { + Variable* const varp = pair.first; + // Find same variable on other path + auto it = oSymTab.find(varp); + // Give up if something is not assigned on all paths ... Latch? + if (it == oSymTab.end()) { + ++m_ctx.m_synt.nonSynLatch; + return false; + } + // Join paths with the block predicate + DfgVertexVar* const thenp = it->second; + DfgVertexVar* const elsep = pair.second; + DfgVertexVar* const newp = joinDrivers(varp, predicatep, thenp, elsep); + if (!newp) return false; + pair.second = newp; + } + } + + return true; + } + + // Gieven the drivers of a variable after converting a single statement + // 'newp', add drivers from 'oldp' that were not reassigned be drivers + // in newp. This computes the total result of all previous assignments. + bool incorporatePreviousValue(Variable* varp, const DfgVertexVar* newp, DfgVertexVar* oldp) { + UASSERT_OBJ(newp->srcp(), varp, "Assigned variable has no driver"); + + // Easy if there is no old value... + if (!oldp) return true; + + // New driver was not yet coalesced, so should always be a splice + DfgVertexSplice* const nSplicep = newp->srcp()->as(); + + // If the old value is the real variable we just computed the new value for, + // then it is the circular feedback into the synthesized block, add it as default driver. + if (oldp->nodep() == varp) { + if (!nSplicep->wholep()) nSplicep->defaultp(oldp); + return true; + } + + UASSERT_OBJ(oldp->srcp(), varp, "Previously assigned variable has no driver"); + + // Can't do arrays yet + if (VN_IS(newp->dtypep(), UnpackArrayDType)) { + ++m_ctx.m_synt.nonSynArray; + return false; + } + + // Gather drivers of 'newp' - they are in incresing range order with no overlaps + std::vector nDrivers; + DfgVertex* nDefaultp = nullptr; + std::tie(nDrivers, nDefaultp) = gatherDrivers(newp->srcp()->as()); + UASSERT_OBJ(!nDrivers.empty(), varp, "Should have a proper driver"); + UASSERT_OBJ(!nDefaultp, varp, "Should not have a default after conversion"); + + // Gather drivers of 'oldp' - they are in incresing range order with no overlaps + std::vector oDrivers; + DfgVertex* oDefaultp = nullptr; + std::tie(oDrivers, oDefaultp) = gatherDrivers(oldp->srcp()->as()); + UASSERT_OBJ(!oDrivers.empty(), varp, "Should have a proper driver"); + + // Additional drivers of 'newp' propagated from 'oldp' + std::vector pDrivers; + + // Add bits between 'msb' and 'lsb' from 'oldp' to 'pDrivers' + const auto addToPDriver = [&](FileLine* const flp, uint32_t msb, uint32_t lsb) { + UASSERT_OBJ(pDrivers.empty() || lsb > pDrivers.back().m_hi, flp, "Non ascending"); + DfgSel* const selp = make(flp, DfgGraph::dtypePacked(msb - lsb + 1)); + selp->lsb(lsb); + selp->fromp(oldp); + pDrivers.emplace_back(flp, lsb, selp); + }; + + // Incorporate old drivers + for (const Driver& oDriver : oDrivers) { + FileLine* const flp = oDriver.m_flp; + // Range to consider inserting, we will adjust oldLo as we process drivers + uint32_t oldLo = oDriver.m_lo; + const uint32_t oldHi = oDriver.m_hi; + + // Loop for now, can move to bisection search if this is a problem, shouldn't be ... + for (const Driver& nDriver : nDrivers) { + UASSERT_OBJ(oldHi >= oldLo, flp, "Should have stopped iteration"); + // If new driver is entirely below old driver, move on to + if (nDriver.m_hi < oldLo) continue; + // If new driver is entirely above old driver, we can stop + if (oldHi < nDriver.m_lo) break; + + // There is an overlap between 'oDriver' and 'nDriver'. + // Insert the low bits and adjust the insertion range. + // The rest will take care of itself on subsequent iterations. + if (oldLo < nDriver.m_lo) addToPDriver(flp, nDriver.m_lo - 1, oldLo); + oldLo = nDriver.m_hi + 1; + + // Stop if no more bits remaining in the old driver + if (oldLo > oldHi) break; + } + + // Insert remaining bits if any + if (oldHi >= oldLo) addToPDriver(flp, oldHi, oldLo); + } + + if (!pDrivers.empty()) { + // Need to merge propagated sources, so unlink and reset the splice + nSplicep->forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); }); + nSplicep->resetSources(); + // Merge drivers - they are both sorted and non-overlapping + std::vector drivers; + drivers.reserve(nDrivers.size() + pDrivers.size()); + std::merge(nDrivers.begin(), nDrivers.end(), pDrivers.begin(), pDrivers.end(), + std::back_inserter(drivers)); + // Coalesce adjacent ranges + coalesceDrivers(drivers); + // Reinsert drivers in order + for (const Driver& d : drivers) nSplicep->addDriver(d.m_flp, d.m_lo, d.m_vtxp); + } + + // If the old had a default, add to the new one too, unless redundant + if (oDefaultp && !nSplicep->wholep()) nSplicep->defaultp(oDefaultp); + + // Done + return true; + } + + // Synthesize the given statements with the given input symbol table. + // Returnt true if successfolly synthesized. + // Populates the given output symbol table. + // Populates the given reference with the condition of the terminator branch, if any. + bool synthesizeBasicBlock(SymTab& oSymTab, DfgVertex*& condpr, + const std::vector& stmtps, const SymTab& iSymTab) { + // Use fresh set of vertices in m_converter + const VNUser2InUse user2InUse; + + // Initialize Variable -> Vertex bindings available in this block + for (const auto& pair : iSymTab) { + Variable* const varp = pair.first; + DfgVertexVar* const vtxp = pair.second; + varp->user2p(vtxp); + oSymTab[varp] = vtxp; + } + + // Synthesize each statement one after the other + std::vector> updates; + for (AstNodeStmt* const stmtp : stmtps) { + // Regular statements + if (AstAssign* const ap = VN_CAST(stmtp, Assign)) { + // Convert this assignment + if (!m_converter.convert(updates, *m_logicp, ap)) { + ++m_ctx.m_synt.nonSynConv; + return false; + } + // Apply variable updates from this statement + for (const auto& pair : updates) { + // The target variable that was assigned to + Variable* const varp = pair.first; + // The new, potentially partially assigned value + DfgVertexVar* const newp = pair.second; + // Normalize drivers within this statement, bail if multidriven + std::vector drivers; + DfgVertex* dfltp = nullptr; + std::tie(drivers, dfltp) = gatherDrivers(newp->srcp()->as()); + UASSERT_OBJ(!dfltp, varp, "Conversion should not add default driver"); + const bool single = drivers.size() == 1; + if (!normalizeDrivers(*newp, drivers)) { + getAstVar(varp)->setDfgMultidriven(); + ++m_ctx.m_synt.nonSynMultidrive; + return false; + } + // If there were more than one driver (often not), replace in case coalesced + if (!single) newp->srcp(makeSplice(*newp, drivers)); + // The old value, if any + DfgVertexVar* const oldp = varp->user2u().template to(); + // Inncorporate old value into the new value + if (!incorporatePreviousValue(varp, newp, oldp)) return false; + // Update binding of target variable + varp->user2p(newp); + // Update output symbol table of this block + oSymTab[varp] = newp; + } + updates.clear(); + continue; + } + + // Terminator branches + if (AstIf* const ifp = VN_CAST(stmtp, If)) { + UASSERT_OBJ(ifp == stmtps.back(), ifp, "Branch should be last statement"); + // Convert condition, give up if failed + DfgVertex* const condp = m_converter.convert(*m_logicp, ifp->condp()); + if (!condp) { + ++m_ctx.m_synt.nonSynConv; + return false; + } + // + if (condp->width() == 1) { + // Single bit condition can be use directly + condpr = condp; + } else { + // Multi bit condition: use 'condp != 0' + FileLine* const flp = condp->fileline(); + DfgNeq* const neqp = make(flp, DfgGraph::dtypePacked(1)); + neqp->lhsp(make(flp, condp->width(), 0U)); + neqp->rhsp(condp); + condpr = neqp; + } + continue; + } + + // Unhandled + ++m_ctx.m_synt.nonSynStmt; + return false; + } + + return true; + } + + // Given a basic block, and the condition of the terminating branch (if any), + // assign perdicates to the block's outgoing control flow edges. + void assignSuccessorPredicates(ControlFlowEdgeMap& edgeToPredicatep, + const BasicBlock& bb, DfgVertex* condp) { + // Nothing to do for the exit block + if (bb.outEmpty()) return; + + // Get the predicate of this block + DfgVertex* const predp = [&]() -> DfgVertex* { + // Entry block has no predecessors, use constant true + if (bb.inEmpty()) return make(m_logicp->fileline(), 1U, 1U); + + // For any other block, 'or' together all the incoming predicates + const auto& inEdges = bb.inEdges(); + auto it = inEdges.begin(); + DfgVertex* resp = edgeToPredicatep[static_cast(*it)]; + while (++it != inEdges.end()) { + DfgOr* const orp = make(resp->fileline(), resp->dtypep()); + orp->rhsp(resp); + orp->lhsp(edgeToPredicatep[static_cast(*it)]); + resp = orp; + } + return resp; + }(); + + if (!condp) { + // There should be 1 successors for a block with an unconditional terminator + UASSERT_OBJ(!bb.untknEdgep(), predp, "Expecting 1 successor for BasicBlock"); + // Successor predicate edge is the same + edgeToPredicatep[*bb.takenEdgep()] = predp; + } else { + // There should be 2 successors for a block with an conditional terminator + UASSERT_OBJ(bb.untknEdgep(), predp, "Expecting 2 successors for BasicBlock"); + FileLine* const flp = condp->fileline(); + AstNodeDType* const dtypep = condp->dtypep(); // Single bit + + // Predicate for taken branch: 'predp & condp' + DfgAnd* const takenPredp = make(flp, dtypep); + takenPredp->lhsp(predp); + takenPredp->rhsp(condp); + edgeToPredicatep[*bb.takenEdgep()] = takenPredp; + + // Predicate for untaken branch: 'predp & ~condp' + DfgAnd* const untknPredp = make(flp, dtypep); + untknPredp->lhsp(predp); + DfgNot* const notp = make(flp, dtypep); + notp->srcp(condp); + untknPredp->rhsp(notp); + edgeToPredicatep[*bb.untknEdgep()] = untknPredp; + } + } + + // Add the synthesized values as drivers to the output variables of the current DfgLogic + bool addSynthesizedOutput(SymTab& oSymTab) { + // It's possible we think a variable is written by the DfgLogic when + // it actauly isn't, e.g.: '{a[0], b[0]}[1] = ...' does not write 'b'. + // These LHS forms can happen after some earlier tranforms. We + // should just run V3Const on them earleir, but we will do belt and + // braces and check here too. We can't touch any output variables if so. + const bool missing = m_logicp->findSink([&](const DfgVertex& sink) -> bool { + const DfgUnresolved* const unresolvedp = sink.as(); + AstNode* const tgtp = unresolvedp->singleSink()->as()->nodep(); + Variable* const varp = reinterpret_cast(tgtp); + return !oSymTab.count(varp); + }); + if (missing) { + ++m_ctx.m_synt.nonSynFalseWrite; + return false; + } + + // Add sinks to read the computed values for the target variables + m_logicp->forEachSink([&](DfgVertex& sink) { + DfgUnresolved* const unresolvedp = sink.as(); + AstNode* const tgtp = unresolvedp->singleSink()->as()->nodep(); + Variable* const varp = reinterpret_cast(tgtp); + DfgVertexVar* const resp = oSymTab.at(varp); + UASSERT_OBJ(resp->srcp(), resp, "Undriven result"); + unresolvedp->addDriver(resp->srcp()->as()); + }); + return true; + } + + // Synthesize the given AstAssignW. Returns true on success. + bool synthesizeAssignW(AstAssignW* nodep) { + ++m_ctx.m_synt.inputAssign; + + // Construct an equivalent AstAssign + AstNodeExpr* const lhsp = nodep->lhsp()->cloneTree(false); + AstNodeExpr* const rhsp = nodep->rhsp()->cloneTree(false); + AstAssign* const assignp = new AstAssign{nodep->fileline(), lhsp, rhsp}; + + // The input and output symbol tables + SymTab iSymTab; + SymTab oSymTab; + + // Initialzie input symbol table + initializeEntrySymbolTable(iSymTab); + + // Synthesize as if it was in a single BasicBlock CFG + DfgVertex* condp = nullptr; + const bool success = synthesizeBasicBlock(oSymTab, condp, {assignp}, iSymTab); + UASSERT_OBJ(!condp, nodep, "Conditional AstAssignW ???"); + // Delete auxiliary AstAssign + VL_DO_DANGLING(assignp->deleteTree(), assignp); + if (!success) return false; + + // Add resolved output variable drivers + return addSynthesizedOutput(oSymTab); + } + + // Synthesize the given AstAlways. Returns true on success. + bool synthesizeCfg(const ControlFlowGraph& cfg) { + ++m_ctx.m_synt.inputAlways; + + if (hasExternallyWrittenVariable(*m_logicp)) { + ++m_ctx.m_synt.nonSynExtWrite; + return false; + } + + // If there is a backward edge (loop), we can't synthesize it + if (cfg.containsLoop()) { + ++m_ctx.m_synt.nonSynLoop; + return false; + } + + // Maps from BasicBlock to its input and output symbol tables + BasicBlockMap bbToISymTab = cfg.makeBasicBlockMap(); + BasicBlockMap bbToOSymTab = cfg.makeBasicBlockMap(); + + // Map from ControlFlowGraphEdge to its predicate + ControlFlowEdgeMap edgeToPredicatep = cfg.makeEdgeMap(); + + // Initialzie input symbol table of entry block + initializeEntrySymbolTable(bbToISymTab[cfg.enter()]); + + // Synthesize all blocks + for (const V3GraphVertex& cfgVtx : cfg.vertices()) { + const BasicBlock& bb = *cfgVtx.as(); + // Symbol tables of the block + SymTab& iSymTab = bbToISymTab[bb]; + SymTab& oSymTab = bbToOSymTab[bb]; + // Join symbol tables from predecessor blocks + if (!createInputSymbolTable(iSymTab, bb, bbToOSymTab, edgeToPredicatep)) return false; + // Condition of the terminating branch, if any + DfgVertex* condp = nullptr; + // Synthesize the block + if (!synthesizeBasicBlock(oSymTab, condp, bb.stmtps(), iSymTab)) return false; + // Set the predicates on the successor edges + assignSuccessorPredicates(edgeToPredicatep, bb, condp); + } + + // Add resolved output variable drivers + return addSynthesizedOutput(bbToOSymTab[cfg.exit()]); + } + + // Synthesize a DfgLogic into regular vertices. Returns ture on success. + bool synthesize(DfgLogic& vtx) { + VL_RESTORER(m_logicp); + m_logicp = &vtx; + + if (AstAssignW* const nodep = VN_CAST(vtx.nodep(), AssignW)) { + if (!synthesizeAssignW(nodep)) return false; + ++m_ctx.m_synt.synthAssign; + return true; + } + + if (!synthesizeCfg(vtx.cfg())) return false; + ++m_ctx.m_synt.synthAlways; + return true; + } + + // Revert synthesis of the given DfgLogic + void revert(DfgLogic& vtx) { + for (DfgVertex* const p : vtx.synth()) VL_DO_DANGLING(p->unlinkDelete(m_dfg), p); + vtx.synth().clear(); + } + + // Revert all logic driving the given unresolved driver, delete it, + // and transitively the same for variables driven by the reverted logic. + void revertTransivelyAndRemove(DfgUnresolved* vtxp, VDouble0& statCountr) { + // The result variable will be driven from Ast code, mark as such + vtxp->singleSink()->as()->setHasModWrRefs(); + + // Gather all logic driving this unresolved driver + std::vector logicps; + logicps.reserve(vtxp->arity()); + vtxp->forEachSource([&](DfgVertex& src) { + if (DfgLogic* const p = src.cast()) logicps.emplace_back(p); + }); + + // Delete the unresolved driver + VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp); + + // Transitively for the rest + for (DfgLogic* const logicp : logicps) { + if (!logicp->synth().empty()) { + ++statCountr; + revert(*logicp); + } + while (DfgVertex* const sinkp = logicp->firtsSinkp()) { + revertTransivelyAndRemove(sinkp->as(), statCountr); + } + } + } + + // Synthesize all of the given vertices + void main(const std::vector& logicps) { + //------------------------------------------------------------------- + UINFO(5, "Step 1: Attempting to synthesize each of the given DfgLogic"); + for (DfgLogic* const logicp : logicps) { + // Debug aid + if (VL_UNLIKELY(s_dfgSynthDebugLimit)) { + if (s_dfgSynthDebugCount == s_dfgSynthDebugLimit) break; + ++s_dfgSynthDebugCount; + if (s_dfgSynthDebugCount == s_dfgSynthDebugLimit) { + // This is the breaking logic + m_debugLogicp = logicp; + // Dump it + UINFOTREE(0, logicp->nodep(), "Problematic DfgLogic: " << logicp, " "); + V3EmitV::debugVerilogForTree(logicp->nodep(), std::cout); + debugDump("synth-lastok"); + } + } + + // Synthesize it, revert partial construction if failed + if (!synthesize(*logicp)) revert(*logicp); + } + debugDump("synth-converted"); + + //------------------------------------------------------------------- + UINFO(5, "Step 2: Revert drivers of variables with unsynthesizeable drivers"); + // We do this as the variables might be multi-driven, we just can't know at this point + for (DfgVertexVar& var : m_dfg.varVertices()) { + if (!var.srcp()) continue; + DfgUnresolved* const unresolvedp = var.srcp()->cast(); + if (!unresolvedp) break; // Stop when reached the synthesized temporaries + + // Check if any driver have failed to synthesize + const bool failed = unresolvedp->findSourceEdge([&](const DfgEdge& e, size_t) -> bool { + DfgLogic* const driverp = e.sourcep()->cast(); + return driverp && driverp->synth().empty(); + }); + // Revert all logic involved + if (failed) revertTransivelyAndRemove(unresolvedp, m_ctx.m_synt.revertNonSyn); + } + debugDump("synth-reverted"); + + //------------------------------------------------------------------- + UINFO(5, "Step 3: Resolve synthesized drivers of original (non-temporary) variables"); + // List of multi-driven variables + std::vector multidrivenps; + // Map from variable to its resolved driver + std::unordered_map resolvedDrivers; + // Compute resolved drivers of all variablees + for (DfgVertexVar& var : m_dfg.varVertices()) { + if (!var.srcp()) continue; + const DfgUnresolved* const unresolvedp = var.srcp()->cast(); + if (!unresolvedp) break; // Stop when reached the synthesized temporaries + + // Resolve the synthesized drivers + DfgVertexSplice* const resolvedp = [&]() -> DfgVertexSplice* { + // All synthesized drivers were normalized already, + // so if there is only one, it can be used directly + if (const auto p = unresolvedp->singleSource()) return p->as(); + // Otherwise gather the synthesized drivers + std::vector drivers = gatherDriversUnresolved(unresolvedp); + // Normalize them, make resolved driver if all good + if (normalizeDrivers(var, drivers)) return makeSplice(var, drivers); + // If mutlidriven, record and ignore + multidrivenps.emplace_back(&var); + return nullptr; + }(); + // Bail if multidriven + if (!resolvedp) continue; + // Add to map for next loop + const bool newEntry = resolvedDrivers.emplace(&var, resolvedp).second; + UASSERT_OBJ(newEntry, &var, "Dupliacte driver"); + } + // Revert and remove drivers of multi-driven variables + for (DfgVertexVar* const vtxp : multidrivenps) { + // Mark as multidriven for future DFG runs - here, so we get all warning before + vtxp->varp()->setDfgMultidriven(); + // Might not have a driver if transitively removed on an earlier iteration + if (!vtxp->srcp()) continue; + // Revert all logic involved + DfgUnresolved* const unresolvedp = vtxp->srcp()->as(); + revertTransivelyAndRemove(unresolvedp, m_ctx.m_synt.revertMultidrive); + } + // Replace all DfgUnresolved with the resolved drivers + for (DfgVertexVar& var : m_dfg.varVertices()) { + if (!var.srcp()) continue; + DfgUnresolved* const srcp = var.srcp()->cast(); + if (!srcp) break; // Stop when reached the synthesized temporaries + + // Replace it + srcp->replaceWith(resolvedDrivers.at(&var)); + VL_DO_DANGLING(srcp->unlinkDelete(m_dfg), srcp); + } + debugDump("synth-resolved"); + + //------------------------------------------------------------------- + UINFO(5, "Step 4: Remove all DfgLogic and DfgUnresolved"); + for (DfgVertex* const vtxp : m_dfg.opVertices().unlinkable()) { + // Previous step should have removed all DfgUnresolved + UASSERT_OBJ(!vtxp->is(), vtxp, "DfgUnresolved remains"); + + // Process only DfgLogic + DfgLogic* const logicp = vtxp->cast(); + if (!logicp) continue; + + // Earlier pass should have removed all sinks + UASSERT_OBJ(!logicp->hasSinks(), logicp, "DfgLogic sink remains"); + + if (!logicp->synth().empty()) { + // If synthesized, delete the corresponding AstNode. It is now in Dfg. + logicp->nodep()->unlinkFrBack()->deleteTree(); + } else { + // Not synthesized. Logic stays in Ast. Mark source variables + //as read in module. Outputs already marked by revertTransivelyAndRemove. + logicp->forEachSource([](DfgVertex& src) { // + src.as()->setHasModRdRefs(); + }); + } + + // Delete this DfgLogic + VL_DO_DANGLING(logicp->unlinkDelete(m_dfg), logicp); + } + // Reset the debug pointer, we have deleted it in the loop above ... + m_debugLogicp = nullptr; + debugDump("synth-rmlogics"); + + //------------------------------------------------------------------- + UINFO(5, "Step 5: Remove unnecessary splices"); + for (DfgVertex* const vtxp : m_dfg.opVertices().unlinkable()) { + DfgVertexSplice* const splicep = vtxp->cast(); + if (!splicep) continue; + + // Might not have a sink if the driving logic was revered, remove + if (!splicep->hasSinks()) { + VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); + continue; + } + + // It should alway have drivers + UASSERT_OBJ(splicep->arity(), splicep, "Splice with no drivers"); + + // If redundant, remove it + if (DfgVertex* const wholep = splicep->wholep()) { + if (DfgVertexVar* const varp = splicep->singleSink()->cast()) { + varp->driverFileLine(splicep->driverFileLine(0)); + } + splicep->replaceWith(wholep); + VL_DO_DANGLING(splicep->unlinkDelete(m_dfg), splicep); + } + } + debugDump("synth-rmsplice"); + + //------------------------------------------------------------------- + UINFO(5, "Step 6: Remove all unused vertices"); + V3DfgPasses::removeUnused(m_dfg); + debugDump("synth-rmunused"); + } + + // CONSTRUCTOR + AstToDfgSynthesize(DfgGraph& dfg, const std::vector& logicps, + V3DfgSynthesisContext& ctx) + : m_dfg{dfg} + , m_ctx{ctx} + , m_converter{dfg, ctx} {} + +public: + static void apply(DfgGraph& dfg, const std::vector& logicps, + V3DfgSynthesisContext& ctx) { + AstToDfgSynthesize{dfg, logicps, ctx}.main(logicps); + } +}; + +void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) { + // The vertices to synthesize + std::vector logicps; + + if (v3Global.opt.fDfgSynthesizeAll()) { + // If we are told to synthesize everything, we will do so ... + for (DfgVertex& vtx : dfg.opVertices()) { + if (DfgLogic* const logicp = vtx.cast()) logicps.emplace_back(logicp); + } + } else { + // Otherwise figure out which vertices are worth synthesizing. + + // Find cycles + const auto userDataInUse = dfg.userDataInUse(); + V3DfgPasses::colorStronglyConnectedComponents(dfg); + + // First, gather variables, we will then attempt to synthesize all their drivers + std::vector varps; + for (DfgVertexVar& var : dfg.varVertices()) { + // Can ignore variables with no drivers + if (!var.srcp()) continue; + + // Circular variable - synthesize + if (var.getUser()) { + varps.emplace_back(&var); + continue; + } + + // Must be driven from a DfgUnresolved at this point, pick it up + const DfgUnresolved* const unresolvedp = var.srcp()->as(); + + // Inspect drivers to figure out if we should synthesize them + const bool doIt = unresolvedp->findSourceEdge([](const DfgEdge& edge, size_t) -> bool { + const DfgLogic* const logicp = edge.sourcep()->as(); + // Synthesize continuous assignments (this is the earlier behaviour) + if (VN_IS(logicp->nodep(), AssignW)) return true; + // Synthesize always blocks with no more than 4 basic blocks and 4 edges + // These are usually simple branches (if (rst) ... else ...), or close to it + return logicp->cfg().nBasicBlocks() <= 4 && logicp->cfg().nEdges() <= 4; + }); + if (doIt) varps.emplace_back(&var); + } + + // Gather all drivers of the selected variables + const VNUser2InUse user2InUse; // AstNode (logic) -> bool: already collected + for (const DfgVertexVar* const varp : varps) { + varp->srcp()->as()->forEachSource([&](DfgVertex& source) { + DfgLogic* const logicp = source.as(); + if (!logicp->nodep()->user2Inc()) logicps.emplace_back(logicp); + }); + } + } + + // Synthesize them - also removes un-synthesized DfgLogic, so must run even if logicps.empty() + if (dfg.modulep()) { + AstToDfgSynthesize::apply(dfg, logicps, ctx.m_synthContext); + } else { + AstToDfgSynthesize::apply(dfg, logicps, ctx.m_synthContext); + } +} diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index bb6306f26..f3ef92c65 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -43,6 +43,8 @@ class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary { AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned) // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr. FileLine* m_driverFileLine = nullptr; + // If this DfgVertexVar is a synthesized temporary, this is the Var/VarScope it stands for. + AstNode* m_tmpForp = nullptr; bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED; V3Hash selfHash() const final VL_MT_DISABLED; @@ -64,6 +66,9 @@ public: FileLine* driverFileLine() const { return m_driverFileLine; } void driverFileLine(FileLine* flp) { m_driverFileLine = flp; } + AstNode* tmpForp() const { return m_tmpForp; } + void tmpForp(AstNode* nodep) { m_tmpForp = nodep; } + bool isDrivenFullyByDfg() const { return srcp() && !srcp()->is() && !varp()->isForced(); } @@ -100,10 +105,104 @@ public: } }; class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic { +protected: + struct DriverData final { + FileLine* m_flp; // Location of this driver + uint32_t m_lo; // Low index of range driven by this driver + DriverData() = delete; + DriverData(FileLine* flp, uint32_t lo) + : m_flp{flp} + , m_lo{lo} {} + }; + std::vector m_driverData; // Additional data associated with each driver + + bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; + V3Hash selfHash() const override VL_MT_DISABLED; + public: DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep) - : DfgVertexVariadic{dfg, type, flp, dtypep, 1u} {} + : DfgVertexVariadic{dfg, type, flp, dtypep, 2u} { + // Add optional source for 'defaultp' + addSource(); + } ASTGEN_MEMBERS_DfgVertexSplice; + + std::pair sourceEdges() const override { + const std::pair pair = DfgVertexVariadic::sourceEdges(); + UASSERT_OBJ(pair.second > 0, this, "default driver edge is missing"); + // If it has a default driver that's it + if (pair.first->sourcep()) return pair; + // Otherwise there is one less source + return {pair.first + 1, pair.second - 1}; + } + std::pair sourceEdges() override { + const auto pair = const_cast(this)->sourceEdges(); + return {const_cast(pair.first), pair.second}; + } + + // Named getter/setter for optional default driver + DfgVertex* defaultp() const { return DfgVertexVariadic::source(0); } + void defaultp(DfgVertex* vtxp) { + UASSERT_OBJ(!vtxp->is(), vtxp, "default driver can't be a DfgLogic"); + const bool found = findSourceEdge([vtxp](const DfgEdge& e, size_t) -> bool { // + return e.sourcep() == vtxp; + }); + UASSERT_OBJ(!found, this, "adding existing driver as default"); + DfgVertexVariadic::sourceEdge(0)->relinkSource(vtxp); + } + + // Add resolved driver + void addDriver(FileLine* flp, uint32_t lo, DfgVertex* vtxp) { + UASSERT_OBJ(!vtxp->is(), vtxp, "addDriver called with DfgLogic"); + UASSERT_OBJ(vtxp != defaultp(), this, "adding default driver as resolved"); + m_driverData.emplace_back(flp, lo); + DfgVertexVariadic::addSource()->relinkSource(vtxp); + } + + FileLine* driverFileLine(size_t idx) const { + UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverFileLine' called on default driver"); + if (defaultp()) --idx; + return m_driverData.at(idx).m_flp; + } + + uint32_t driverLo(size_t idx) const { + UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverLo' called on default driver"); + if (defaultp()) --idx; + const DriverData& dd = m_driverData.at(idx); + return dd.m_lo; + } + + DfgVertex* driverAt(size_t idx) const { + const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge& e, size_t i) { // + // Don't pick the default driver + if (i == 0 && defaultp()) return false; + return driverLo(i) == idx; + }); + return edgep ? edgep->sourcep() : nullptr; + } + + // If drives the whole result explicitly (not through defaultp), this is + // the actual driver this DfgVertexSplice can be replaced with. + inline DfgVertex* wholep() const; + + void resetSources() { + m_driverData.clear(); + // Unlink default driver + DfgVertex* const dp = defaultp(); + DfgVertexVariadic::sourceEdge(0)->unlinkSource(); + // Reset DfgVertexVariadic sources + DfgVertexVariadic::resetSources(); + // Add back the default driver if present + DfgEdge* const edgep = DfgVertexVariadic::addSource(); + if (dp) edgep->relinkSource(dp); + } + + const std::string srcName(size_t idx) const override { + if (idx == 0 && defaultp()) return "default"; + const uint32_t lo = driverLo(idx); + const uint32_t hi = lo + DfgVertexVariadic::source(idx + !defaultp())->size() - 1; + return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']'; + } }; // === Concrete node types ===================================================== @@ -119,12 +218,8 @@ class DfgConst final : public DfgVertex { V3Hash selfHash() const override VL_MT_DISABLED; public: - DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num) - : DfgVertex{dfg, dfgType(), flp, dtypeForWidth(num.width())} - , m_num{num} {} - DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value = 0) - : DfgVertex{dfg, dfgType(), flp, dtypeForWidth(width)} - , m_num{flp, static_cast(width), value} {} + inline DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num); + inline DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value = 0); ASTGEN_MEMBERS_DfgConst; V3Number& num() { return m_num; } @@ -174,6 +269,7 @@ public: }; // === DfgVertexUnary === + class DfgSel final : public DfgVertexUnary { // AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and // 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for @@ -196,6 +292,20 @@ public: const string srcName(size_t) const override { return "fromp"; } }; +class DfgUnitArray final : public DfgVertexUnary { + // This is a type adapter for modeling arrays. It's a single element array, + // with the value of the single element being the source operand. +public: + DfgUnitArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexUnary{dfg, dfgType(), flp, dtypep} { + UASSERT_OBJ(this->dtypep(), flp, "Non array DfgUnitArray"); + UASSERT_OBJ(this->size() == 1, flp, "DfgUnitArray must have a single element"); + } + ASTGEN_MEMBERS_DfgUnitArray; + + const std::string srcName(size_t) const override { return ""; } +}; + // === DfgVertexVar === class DfgVarArray final : public DfgVertexVar { friend class DfgVertex; @@ -228,94 +338,78 @@ public: ASTGEN_MEMBERS_DfgVarPacked; }; +// === DfgVertexVariadic === +class DfgLogic final : public DfgVertexVariadic { + // Generic vertex representing a whole combinational process + AstNode* const m_nodep; // The Ast logic represented by this vertex + const std::unique_ptr m_cfgp; + // Vertices this logic was synthesized into. Excluding variables + std::vector m_synth; + +public: + DfgLogic(DfgGraph& dfg, AstAssignW* nodep) + : DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u} + , m_nodep{nodep} + , m_cfgp{nullptr} {} + + DfgLogic(DfgGraph& dfg, AstAlways* nodep, std::unique_ptr cfgp) + : DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u} + , m_nodep{nodep} + , m_cfgp{std::move(cfgp)} {} + + ASTGEN_MEMBERS_DfgLogic; + + void addInput(DfgVertexVar* varp) { addSource()->relinkSource(varp); } + + AstNode* nodep() const { return m_nodep; } + const ControlFlowGraph& cfg() const { return *m_cfgp; } + std::vector& synth() { return m_synth; } + const std::vector& synth() const { return m_synth; } + + const std::string srcName(size_t) const override { return ""; } +}; + +class DfgUnresolved final : public DfgVertexVariadic { + // Represents a collection of unresolved variable drivers before synthesis + +public: + DfgUnresolved(DfgGraph& dfg, DfgVertexVar* vtxp) + : DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep(), 1u} {} + ASTGEN_MEMBERS_DfgUnresolved; + + // Can only be driven by DfgLogic or DfgVertexSplice + void addDriver(DfgLogic* vtxp) { addSource()->relinkSource(vtxp); } + void addDriver(DfgVertexSplice* vtxp) { addSource()->relinkSource(vtxp); } + + void clearSources() { DfgVertexVariadic::clearSources(); } + + DfgVertex* singleSource() const { return arity() == 1 ? source(0) : nullptr; } + + const std::string srcName(size_t) const override { return ""; } +}; + // === DfgVertexSplice === class DfgSpliceArray final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; - struct DriverData final { - FileLine* m_flp; // Location of this driver - uint32_t m_index; // Array index driven by this driver (or low index of range) - DriverData() = delete; - DriverData(FileLine* flp, uint32_t index) - : m_flp{flp} - , m_index{index} {} - }; - - std::vector m_driverData; // Additional data associated with each driver - - bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; - V3Hash selfHash() const override VL_MT_DISABLED; - public: DfgSpliceArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { UASSERT_OBJ(VN_IS(dtypep, UnpackArrayDType), flp, "Non array DfgSpliceArray"); } ASTGEN_MEMBERS_DfgSpliceArray; - - void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) { - m_driverData.emplace_back(flp, index); - DfgVertexVariadic::addSource()->relinkSource(vtxp); - } - - void resetSources() { - m_driverData.clear(); - DfgVertexVariadic::resetSources(); - } - - FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; } - uint32_t driverIndex(size_t i) const { return m_driverData.at(i).m_index; } - - DfgVertex* driverAt(size_t idx) const { - const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge&, size_t i) { // - return driverIndex(i) == idx; - }); - return edgep ? edgep->sourcep() : nullptr; - } - - const std::string srcName(size_t idx) const override { - return std::to_string(driverIndex(idx)); - } }; class DfgSplicePacked final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; - struct DriverData final { - FileLine* m_flp; // Location of this driver - uint32_t m_lsb; // LSB of range driven by this driver - DriverData() = delete; - DriverData(FileLine* flp, uint32_t lsb) - : m_flp{flp} - , m_lsb{lsb} {} - }; - std::vector m_driverData; // Additional data associated with each driver - - bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; - V3Hash selfHash() const override VL_MT_DISABLED; - public: DfgSplicePacked(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { UASSERT_OBJ(!VN_IS(dtypep, UnpackArrayDType), flp, "Array DfgSplicePacked"); } ASTGEN_MEMBERS_DfgSplicePacked; - - void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) { - m_driverData.emplace_back(flp, lsb); - DfgVertexVariadic::addSource()->relinkSource(vtxp); - } - - void resetSources() { - m_driverData.clear(); - DfgVertexVariadic::resetSources(); - } - - FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; } - uint32_t driverLsb(size_t i) const { return m_driverData.at(i).m_lsb; } - - const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); } }; #endif diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 5608c19d9..2765564b0 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -1018,8 +1018,8 @@ class EmitVStreamVisitor final : public EmitVBaseVisitorConst { void putqs(AstNode*, const string& str) override { putbs(str); } public: - EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking) - : EmitVBaseVisitorConst{false, false} + EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking, bool suppressUnknown) + : EmitVBaseVisitorConst{false, suppressUnknown} , m_os{os, V3OutFormatter::LA_VERILOG} , m_tracking{tracking} { iterateConst(const_cast(nodep)); @@ -1031,7 +1031,11 @@ public: // EmitV class functions void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) { - { EmitVStreamVisitor{nodep, os, /* tracking: */ false}; } + { EmitVStreamVisitor{nodep, os, /* tracking: */ false, false}; } +} + +void V3EmitV::debugVerilogForTree(const AstNode* nodep, std::ostream& os) { + { EmitVStreamVisitor{nodep, os, /* tracking: */ true, true}; } } void V3EmitV::emitvFiles() { diff --git a/src/V3EmitV.h b/src/V3EmitV.h index 1da5bc2e8..d284cfa80 100644 --- a/src/V3EmitV.h +++ b/src/V3EmitV.h @@ -28,6 +28,7 @@ class AstSenTree; class V3EmitV final { public: static void verilogForTree(const AstNode* nodep, std::ostream& os = std::cout); + static void debugVerilogForTree(const AstNode* nodep, std::ostream& os); static void emitvFiles(); static void debugEmitV(const string& filename); }; diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 0d2bafc62..2aac40cc3 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1347,6 +1347,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline); DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline); DECL_OPTION("-fdfg-scoped", FOnOff, &m_fDfgScoped); + DECL_OPTION("-fdfg-synthesize-all", FOnOff, &m_fDfgSynthesizeAll); DECL_OPTION("-fexpand", FOnOff, &m_fExpand); DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { // m_fFuncSplitCat = flag; diff --git a/src/V3Options.h b/src/V3Options.h index 39f8e9a4b..12dcdd295 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -425,6 +425,7 @@ private: bool m_fDfgPreInline; // main switch: -fno-dfg-pre-inline and -fno-dfg bool m_fDfgPostInline; // main switch: -fno-dfg-post-inline and -fno-dfg bool m_fDfgScoped; // main switch: -fno-dfg-scoped and -fno-dfg + bool m_fDfgSynthesizeAll = false; // main switch: -fdfg-synthesize-all bool m_fDeadAssigns; // main switch: -fno-dead-assigns: remove dead assigns bool m_fDeadCells; // main switch: -fno-dead-cells: remove dead cells bool m_fExpand; // main switch: -fno-expand: expansion of C macros @@ -741,6 +742,7 @@ public: bool fDfgPreInline() const { return m_fDfgPreInline; } bool fDfgPostInline() const { return m_fDfgPostInline; } bool fDfgScoped() const { return m_fDfgScoped; } + bool fDfgSynthesizeAll() const { return m_fDfgSynthesizeAll; } bool fDfgPeepholeEnabled(const std::string& name) const { return !m_fDfgPeepholeDisabled.count(name); } diff --git a/src/astgen b/src/astgen index 9af3a1746..f59e84865 100755 --- a/src/astgen +++ b/src/astgen @@ -1266,9 +1266,10 @@ def write_dfg_ast_to_dfg(filename): " Dfg{t}* const vtxp = makeVertex(nodep, m_dfg);\n".format(t=node.name)) fh.write(" if (!vtxp) {\n") fh.write(" m_foundUnhandled = true;\n") - fh.write(" ++m_ctx.m_nonRepNode;\n") + fh.write(" ++m_ctx.m_conv.nonRepNode;\n") fh.write(" return;\n") fh.write(" }\n\n") + fh.write(" m_logicp->synth().emplace_back(vtxp);") for i in range(node.arity): fh.write( " vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to());\n". diff --git a/test_regress/t/t_balance_cats.v b/test_regress/t/t_balance_cats.v index 4562ca1e5..7befe35ce 100644 --- a/test_regress/t/t_balance_cats.v +++ b/test_regress/t/t_balance_cats.v @@ -4,8 +4,6 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -// verilator lint_off UNOPTFLAT - module t(i, o); localparam N = 2000; // Deliberately not multiple of 32 diff --git a/test_regress/t/t_clk_condflop.v b/test_regress/t/t_clk_condflop.v index a28335121..5563b1b82 100644 --- a/test_regress/t/t_clk_condflop.v +++ b/test_regress/t/t_clk_condflop.v @@ -15,9 +15,7 @@ module t (clk); wire [2:0] q3; wire [7:0] q8; - // verilator lint_off UNOPTFLAT reg ena; - // verilator lint_on UNOPTFLAT condff #(12) condff (.clk(clk), .sen(1'b0), .ena(ena), diff --git a/test_regress/t/t_comb_input_1.py b/test_regress/t/t_comb_input_1.py index 74e183dab..4f9f6115e 100755 --- a/test_regress/t/t_comb_input_1.py +++ b/test_regress/t/t_comb_input_1.py @@ -14,7 +14,7 @@ test.scenarios('vlt_all') test.compile( #make_top_shell = False, make_main=False, - v_flags2=["--exe", test.pli_filename]) + v_flags2=["--exe", test.pli_filename, "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_comb_input_2.py b/test_regress/t/t_comb_input_2.py index 74e183dab..4f9f6115e 100755 --- a/test_regress/t/t_comb_input_2.py +++ b/test_regress/t/t_comb_input_2.py @@ -14,7 +14,7 @@ test.scenarios('vlt_all') test.compile( #make_top_shell = False, make_main=False, - v_flags2=["--exe", test.pli_filename]) + v_flags2=["--exe", test.pli_filename, "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_comb_loop_through_unpacked_array.py b/test_regress/t/t_comb_loop_through_unpacked_array.py index 41b8d5138..d16f85d79 100755 --- a/test_regress/t/t_comb_loop_through_unpacked_array.py +++ b/test_regress/t/t_comb_loop_through_unpacked_array.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile(verilator_flags2=["-Wno-UNOPTFLAT"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "-fno-dfg"]) test.passes() diff --git a/test_regress/t/t_detectarray_1.py b/test_regress/t/t_detectarray_1.py index c37bc018e..51e61aa39 100755 --- a/test_regress/t/t_detectarray_1.py +++ b/test_regress/t/t_detectarray_1.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-Wno-UNOPTFLAT"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_detectarray_2.py b/test_regress/t/t_detectarray_2.py index c37bc018e..51e61aa39 100755 --- a/test_regress/t/t_detectarray_2.py +++ b/test_regress/t/t_detectarray_2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-Wno-UNOPTFLAT"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_detectarray_3.py b/test_regress/t/t_detectarray_3.py index bf411aade..979bfb85d 100755 --- a/test_regress/t/t_detectarray_3.py +++ b/test_regress/t/t_detectarray_3.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-Wno-UNOPTFLAT -Wno-WIDTH"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "-Wno-WIDTH", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index 198c4cd48..48fe8d28f 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -9,6 +9,8 @@ import vltest_bootstrap +import os + test.scenarios('vlt_all') test.sim_time = 2000000 @@ -65,8 +67,6 @@ test.compile(verilator_flags2=[ "--stats", "--build", "-fno-dfg-break-cycles", - "-fno-dfg-post-inline", - "-fno-dfg-scoped", "+incdir+" + test.obj_dir, "-Mdir", test.obj_dir + "/obj_ref", "--prefix", "Vref", @@ -100,6 +100,9 @@ coveredLines = set() def readCovered(fileName): + if not os.path.exists(fileName): + test.error_keep_going("Missing coverage file: " + fileName) + return with open(fileName, 'r', encoding="utf8") as fd: for line in fd: coveredLines.add(int(line.strip())) diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index 538e4abdc..ad794c052 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -194,4 +194,37 @@ module t ( `signal(COND_COND, 3); // UNOPTFLAT assign COND_COND = {rand_a[0], (COND_COND >> 2) == 3'b001 ? rand_b[3:2] : rand_b[1:0]}; + // verilator lint_off ALWCOMBORDER + logic [3:0] always_0; + always_comb begin + always_0[3] = ~always_0[1]; + always_0[2] = always_0[1]; + always_0[0] = rand_a[0]; + end + assign always_0[1] = ~always_0[0]; + `signal(ALWAYS_0, 4); // UNOPTFLAT + assign ALWAYS_0 = always_0; + // verilator lint_on ALWCOMBORDER + + // verilator lint_off ALWCOMBORDER + logic [4:0] always_1; + always_comb begin + always_1[4] = always_1[0]; + always_1[0] = rand_a[0]; + always_1[3:2] = always_1[1:0]; + end + assign always_1[1] = always_1[0]; + `signal(ALWAYS_1, 5); // UNOPTFLAT + assign ALWAYS_1 = always_1; + // verilator lint_on ALWCOMBORDER + + // verilator lint_off ALWCOMBORDER + logic [3:0] always_2; + always_comb begin + always_2[2:0] = 3'((always_2 << 1) | 4'(rand_a[0])); + always_2[3] = rand_a[0]; + end + `signal(ALWAYS_2, 4); // UNOPTFLAT + assign ALWAYS_2 = always_2; + // verilator lint_on ALWCOMBORDER endmodule diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.out b/test_regress/t/t_dfg_multidriver_dfg_bad.out index 07a2fa37e..92a73f8a3 100644 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.out +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.out @@ -1,75 +1,120 @@ -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:1] of signal 'a' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:45:18: Bit [1] of signal 'y' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver - 17 | assign a[3:0] = i[3:0]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:18:19: ... Location of other driver - 18 | assign a[4:1] = ~i[4:1]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected + t/t_dfg_multidriver_dfg_bad.v:48:24: ... Location of offending driver + 48 | {y[1:0], y[2:1]} = i[3:0] + 4'd5; + | ^ + t/t_dfg_multidriver_dfg_bad.v:48:24: ... Location of offending driver + 48 | {y[1:0], y[2:1]} = i[3:0] + 4'd5; + | ^ ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest ... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message. -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:3] of signal 'a' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:1] of signal 'a' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of offending driver 17 | assign a[3:0] = i[3:0]; | ^ - t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:18:19: ... Location of offending driver + 18 | assign a[4:1] = ~i[4:1]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bit [3] of signal 'a' have multiple combinational drivers. This can cause performance degradation. + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of offending driver + 17 | assign a[3:0] = i[3:0]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of offending driver 19 | assign a[3] = ~i[3]; | ^ - t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [7:6] of signal 'a' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [7:6] of signal 'a' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of offending driver 20 | assign a[8:5] = i[8:5]; | ^ - t/t_dfg_multidriver_dfg_bad.v:21:19: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:21:19: ... Location of offending driver 21 | assign a[7:6] = ~i[7:6]; | ^ - t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [9:9] of signal 'a' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bit [9] of signal 'a' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:22:17: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:22:17: ... Location of offending driver 22 | assign a[9] = i[9]; | ^ - t/t_dfg_multidriver_dfg_bad.v:23:19: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:23:17: ... Location of offending driver 23 | assign a[9] = ~i[9]; - | ^ - t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:18: Elements [3:0] of signal 'u' have multiple combinational drivers + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:18: Elements [3:0] of signal 'u' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:27:14: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:27:14: ... Location of offending driver 27 | assign u = j; | ^ - t/t_dfg_multidriver_dfg_bad.v:28:14: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:28:14: ... Location of offending driver 28 | assign u = k; | ^ - t/t_dfg_multidriver_dfg_bad.v:26:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:18: Elements [1:1] of signal 'v' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:18: Element [1] of signal 'v' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:31:14: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:31:14: ... Location of offending driver 31 | assign v = j; | ^ - t/t_dfg_multidriver_dfg_bad.v:32:13: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:32:13: ... Location of offending driver 32 | assign v[1] = i; | ^ - t/t_dfg_multidriver_dfg_bad.v:30:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:18: Elements [0:0] of signal 'w' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:18: Element [0] of signal 'w' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:35:13: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:35:13: ... Location of offending driver 35 | assign w[0] = i; | ^ - t/t_dfg_multidriver_dfg_bad.v:36:14: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:36:14: ... Location of offending driver 36 | assign w = j; | ^ - t/t_dfg_multidriver_dfg_bad.v:34:18: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:18: Bits [3:2] of signal 'x[3]' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:18: Bits [3:2] of signal 'x[3]' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_dfg_multidriver_dfg_bad.v:39:17: ... Location of first driver + t/t_dfg_multidriver_dfg_bad.v:39:17: ... Location of offending driver 39 | assign x[3] = i; | ^ - t/t_dfg_multidriver_dfg_bad.v:40:22: ... Location of other driver + t/t_dfg_multidriver_dfg_bad.v:40:22: ... Location of offending driver 40 | assign x[3][3:2] = ~i[1:0]; | ^ - t/t_dfg_multidriver_dfg_bad.v:38:18: ... Only the first driver will be respected +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:18: Bits [2:1] of signal 'z' have multiple combinational drivers. This can cause performance degradation. + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:53:14: ... Location of offending driver + 53 | z[2:0] = i[2:0]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:58:17: ... Location of offending driver + 58 | z[3:1] = i[3:1]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:18: Bits [6:5] of signal 'z' have multiple combinational drivers. This can cause performance degradation. + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:57:14: ... Location of offending driver + 57 | z[6:4] = i[6:4]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:54:14: ... Location of offending driver + 54 | z[7:5] = i[7:5]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:18: Bit [7] of signal 'z' have multiple combinational drivers. This can cause performance degradation. + : ... note: In instance 't' + t/t_dfg_multidriver_dfg_bad.v:54:14: ... Location of offending driver + 54 | z[7:5] = i[7:5]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:60:20: ... Location of offending driver + 60 | assign z[10:7] = i[10:7]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:74:18: Bits [5:2] of signal 't.sub_1.a' have multiple combinational drivers. This can cause performance degradation. + t/t_dfg_multidriver_dfg_bad.v:63:20: ... Location of offending driver + 63 | assign sub_1.a = i; + | ^ + t/t_dfg_multidriver_dfg_bad.v:75:19: ... Location of offending driver + 75 | assign a[5:2] = i[5:2]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:74:18: Bits [3:2] of signal 't.sub_2.a' have multiple combinational drivers. This can cause performance degradation. + t/t_dfg_multidriver_dfg_bad.v:67:25: ... Location of offending driver + 67 | assign sub_2.a[3:0] = i[3:0]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:75:19: ... Location of offending driver + 75 | assign a[5:2] = i[5:2]; + | ^ +%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:74:18: Bit [5] of signal 't.sub_2.a' have multiple combinational drivers. This can cause performance degradation. + t/t_dfg_multidriver_dfg_bad.v:75:19: ... Location of offending driver + 75 | assign a[5:2] = i[5:2]; + | ^ + t/t_dfg_multidriver_dfg_bad.v:66:26: ... Location of offending driver + 66 | assign sub_2.a[10:5] = i[10:5]; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.py b/test_regress/t/t_dfg_multidriver_dfg_bad.py index e33e10acf..cdd7fb345 100755 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.py +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint(fails=True, expect_filename=test.golden_filename) +test.lint(verilator_flags2=["-fdfg-synthesize-all", "-fno-const-before-dfg"], + fails=True, + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_dfg_multidriver_dfg_bad.v b/test_regress/t/t_dfg_multidriver_dfg_bad.v index 84659c923..d42e66fa1 100644 --- a/test_regress/t/t_dfg_multidriver_dfg_bad.v +++ b/test_regress/t/t_dfg_multidriver_dfg_bad.v @@ -42,6 +42,35 @@ module t( assign x[2][3:2] = ~i[1:0]; assign x[2][1:0] = ~i[1:0]; - assign o = a ^ u[3] ^ v[3] ^ w[3] ^ x[3]; + logic [10:0] y; + always_comb begin + y = i; + {y[1:0], y[2:1]} = i[3:0] + 4'd5; + end + + logic [10:0] z; + always_comb begin + z[2:0] = i[2:0]; + z[7:5] = i[7:5]; + end + always_comb begin + z[6:4] = i[6:4]; + z[3:1] = i[3:1]; + end + assign z[10:7] = i[10:7]; + + sub sub_1(i); + assign sub_1.a = i; + + sub sub_2(i); + assign sub_2.a[10:5] = i[10:5]; + assign sub_2.a[3:0] = i[3:0]; + + assign o = a ^ u[3] ^ v[3] ^ w[3] ^ x[3] ^ y ^ z ^ sub_1.a ^ sub_2.a; endmodule + +module sub(input wire [10:0] i); + logic [10:0] a; + assign a[5:2] = i[5:2]; +endmodule diff --git a/test_regress/t/t_dfg_peephole.py b/test_regress/t/t_dfg_peephole.py index ffb1819e9..204b59116 100755 --- a/test_regress/t/t_dfg_peephole.py +++ b/test_regress/t/t_dfg_peephole.py @@ -82,6 +82,7 @@ test.compile(verilator_flags2=[ "-Mdir", test.obj_dir + "/obj_opt", "--prefix", "Vopt", "-fno-const-before-dfg", # Otherwise V3Const makes testing painful + "-fdfg-synthesize-all", "--dump-dfg", # To fill code coverage "-CFLAGS \"-I .. -I ../obj_ref\"", "../obj_ref/Vref__ALL.a", diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index 6392ded77..73d3bd635 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -36,12 +36,17 @@ module t ( wire logic signed [63:0] sconst_a; wire logic signed [63:0] sconst_b; logic [63:0] array [3:0]; + logic [63:0] unitArrayWhole [0:0]; + logic [63:0] unitArrayParts [0:0]; assign array[0] = (rand_a << 32) | (rand_a >> 32); assign array[1] = (rand_a << 16) | (rand_a >> 48); assign array[2][3:0] = rand_a[3:0]; always @(rand_b) begin // Intentional non-combinational partial driver array[2][7:4] = rand_a[7:4]; end + assign unitArrayWhole[0] = rand_a; + assign unitArrayParts[0][1] = rand_a[1]; + assign unitArrayParts[0][9] = rand_a[9]; `signal(FOLD_UNARY_LogNot, !const_a[0]); `signal(FOLD_UNARY_Negate, -const_a); @@ -188,8 +193,10 @@ module t ( `signal(REPLACE_COND_WITH_THEN_BRANCH_ONES, rand_a[0] ? 1'd1 : rand_a[1]); `signal(REPLACE_COND_WITH_ELSE_BRANCH_ZERO, rand_a[0] ? rand_a[1] : 1'd0); `signal(REPLACE_COND_WITH_ELSE_BRANCH_ONES, rand_a[0] ? rand_a[1] : 1'd1); - `signal(INLINE_ARRAYSEL, array[0]); - `signal(NO_INLINE_ARRAYSEL_PARTIAL, array[2]); + `signal(INLINE_ARRAYSEL_SPLICE, array[0]); + `signal(NO_INLINE_ARRAYSEL_SPLICE_PARTIAL, array[2]); + `signal(INLINE_ARRAYSEL_UNIT, unitArrayWhole[0]); + `signal(NO_INLINE_ARRAYSEL_UNIT_PARTIAL, unitArrayParts[0]); `signal(PUSH_BITWISE_THROUGH_REDUCTION_AND, (&(rand_a + 64'd105)) & (&(rand_b + 64'd108))); `signal(PUSH_BITWISE_THROUGH_REDUCTION_OR, (|(rand_a + 64'd106)) | (|(rand_b + 64'd109))); `signal(PUSH_BITWISE_THROUGH_REDUCTION_XOR, (^(rand_a + 64'd107)) ^ (^(rand_b + 64'd110))); @@ -229,6 +236,15 @@ module t ( `signal(PUSH_SEL_THROUGH_SHIFTL, sel_from_shiftl[20:0]); `signal(REPLACE_SEL_FROM_SEL, sel_from_sel[4:3]); + logic [2:0] sel_from_partial_tmp;; + always_comb begin + sel_from_partial_tmp[1:0] = 2'd0; + if (rand_a[0]) begin + sel_from_partial_tmp[0] = rand_b[0]; + end + end + `signal(PUSH_SEL_THROUGH_SPLICE, sel_from_partial_tmp[1:0]); + // Asscending ranges `signal(ASCENDNG_SEL, arand_a[0:4]); // verilator lint_off ASCRANGE diff --git a/test_regress/t/t_dfg_synthesis.cpp b/test_regress/t/t_dfg_synthesis.cpp new file mode 100644 index 000000000..0664397e1 --- /dev/null +++ b/test_regress/t/t_dfg_synthesis.cpp @@ -0,0 +1,60 @@ +// +// DESCRIPTION: Verilator: DFG optimizer equivalence testing +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 +// + +#include +#include + +#include +#include +#include + +void rngUpdate(uint64_t& x) { + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; +} + +int main(int, char**) { + // Create contexts + VerilatedContext ctx; + + // Create models + Vref ref{&ctx}; + Vopt opt{&ctx}; + + uint64_t rand_a = 0x5aef0c8dd70a4497; + uint64_t rand_b = 0xf0c0a8dd75ae4497; + uint64_t srand_a = 0x00fa8dcc7ae4957; + uint64_t srand_b = 0x0fa8dc7ae3c9574; + + for (size_t n = 0; n < 200000; ++n) { + // Update rngs + rngUpdate(rand_a); + rngUpdate(rand_b); + rngUpdate(srand_a); + rngUpdate(srand_b); + + // Assign inputs + ref.rand_a = opt.rand_a = rand_a; + ref.rand_b = opt.rand_b = rand_b; + ref.srand_a = opt.srand_a = srand_a; + ref.srand_b = opt.srand_b = srand_b; + + // Evaluate both models + ref.eval(); + opt.eval(); + + // Check equivalence +#include "checks.h" + + // increment time + ctx.timeInc(1); + } + + std::cout << "*-* All Finished *-*\n"; +} diff --git a/test_regress/t/t_dfg_synthesis.py b/test_regress/t/t_dfg_synthesis.py new file mode 100755 index 000000000..bd229b216 --- /dev/null +++ b/test_regress/t/t_dfg_synthesis.py @@ -0,0 +1,99 @@ +#!/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_all') +test.sim_time = 2000000 + +root = ".." + +if not os.path.exists(root + "/.git"): + test.skip("Not in a git repository") + +# Generate the equivalence checks and declaration boilerplate +rdFile = test.top_filename +plistFile = test.obj_dir + "/portlist.vh" +pdeclFile = test.obj_dir + "/portdecl.vh" +checkFile = test.obj_dir + "/checks.h" +nAlwaysSynthesized = 0 +nAlwaysNotSynthesized = 0 +nAlwaysReverted = 0 +with open(rdFile, 'r', encoding="utf8") as rdFh, \ + open(plistFile, 'w', encoding="utf8") as plistFh, \ + open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \ + open(checkFile, 'w', encoding="utf8") as checkFh: + for line in rdFh: + if re.search(r'^\s*always.*//\s*nosynth$', line): + nAlwaysNotSynthesized += 1 + elif re.search(r'^\s*always.*//\s*revert$', line): + nAlwaysReverted += 1 + elif re.search(r'^\s*always', line): + nAlwaysSynthesized += 1 + line = line.split("//")[0] + m = re.search(r'`signal\((\w+),', line) + if not m: + continue + sig = m.group(1) + plistFh.write(sig + ",\n") + pdeclFh.write("output " + sig + ";\n") + checkFh.write("if (ref." + sig + " != opt." + sig + ") {\n") + checkFh.write(" std::cout << \"Mismatched " + sig + "\" << std::endl;\n") + checkFh.write(" std::cout << \"Ref: 0x\" << std::hex << (ref." + sig + + " + 0) << std::endl;\n") + checkFh.write(" std::cout << \"Opt: 0x\" << std::hex << (opt." + sig + + " + 0) << std::endl;\n") + checkFh.write(" std::exit(1);\n") + checkFh.write("}\n") + +# Compile un-optimized +test.compile(verilator_flags2=[ + "--stats", + "--build", + "-fno-dfg", + "+incdir+" + test.obj_dir, + "-Mdir", test.obj_dir + "/obj_ref", + "--prefix", "Vref", + "-Wno-UNOPTFLAT" +]) # yapf:disable + +test.file_grep_not(test.obj_dir + "/obj_ref/Vref__stats.txt", r'DFG.*Synthesis') + +# Compile optimized - also builds executable +test.compile(verilator_flags2=[ + "--stats", + "--build", + "--fdfg-synthesize-all", + "-fno-dfg-post-inline", + "-fno-dfg-scoped", + "--exe", + "+incdir+" + test.obj_dir, + "-Mdir", test.obj_dir + "/obj_opt", + "--prefix", "Vopt", + "-fno-const-before-dfg", # Otherwise V3Const makes testing painful + "--debug", "--debugi", "0", "--dumpi-tree", "0", + "-CFLAGS \"-I .. -I ../obj_ref\"", + "../obj_ref/Vref__ALL.a", + "../../t/" + test.name + ".cpp" +]) # yapf:disable + +test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", + r'DFG pre inline Synthesis, synt / always blocks considered\s+(\d+)$', + nAlwaysSynthesized + nAlwaysReverted + nAlwaysNotSynthesized) +test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", + r'DFG pre inline Synthesis, synt / always blocks synthesized\s+(\d+)$', + nAlwaysSynthesized + nAlwaysReverted) +test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", + r'DFG pre inline Synthesis, synt / reverted \(multidrive\)\s+(\d)$', + nAlwaysReverted) + +# Execute test to check equivalence +test.execute(executable=test.obj_dir + "/obj_opt/Vopt") + +test.passes() diff --git a/test_regress/t/t_dfg_synthesis.v b/test_regress/t/t_dfg_synthesis.v new file mode 100644 index 000000000..46f3e298b --- /dev/null +++ b/test_regress/t/t_dfg_synthesis.v @@ -0,0 +1,228 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +`define signal(name, expr) wire [$bits(expr)-1:0] ``name = expr + +module t ( +`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py + rand_a, rand_b, srand_a, srand_b +); + +`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py + + input rand_a; + input rand_b; + input srand_a; + input srand_b; + wire logic [63:0] rand_a; + wire logic [63:0] rand_b; + wire logic signed [63:0] srand_a; + wire logic signed [63:0] srand_b; + + ////////////////////////////////////////////////////////////////////////// + + logic [2:0] simple; + always_comb begin + simple[0] = rand_a[0]; + simple[1] = rand_a[1]; + simple[2] = rand_a[2]; + end + `signal(SIMPLE, simple); + + logic [1:0] reassign; + always_comb begin + reassign[0] = rand_a[0]; + reassign[0] = ~rand_a[0]; + reassign[1] = rand_a[1]; + reassign[1] = ~rand_a[1]; + end + `signal(REASSIGN, reassign); + + logic [1:0] use_intermediate_a; + logic [1:0] use_intermediate_b; + always_comb begin + use_intermediate_a[0] = rand_a[0]; + use_intermediate_b[0] = ~use_intermediate_a[0]; + use_intermediate_a[1] = rand_a[1]; + use_intermediate_a[0] = ~rand_a[0]; + use_intermediate_b[1] = ~use_intermediate_a[1]; + use_intermediate_a[1] = ~rand_a[1]; + end + `signal(USE_INTERMEDIATE, {use_intermediate_a, use_intermediate_b}); + + logic [2:0] self_circular; + always_comb begin + self_circular[0] = rand_a[0]; + self_circular[1] = ~self_circular[0]; + self_circular[2] = ~self_circular[1]; + end + `signal(SELF_CIRCULAR, self_circular); + + logic [2:0] part_circular; + always_comb begin + part_circular[0] = rand_a[0]; + part_circular[1] = ~part_circular[0]; + end + // part_circular[2] deliberately undriven! + `signal(PART_CIRCULAR, part_circular); + + logic [3:0] split_circular; + always_comb begin + split_circular[0] = rand_a[0]; + split_circular[2] = rand_a[1]; + end + always_comb begin + split_circular[1] = ~split_circular[0]; + split_circular[3] = ~split_circular[2]; + end + `signal(SPLIT_CIRCULAR, split_circular); + + logic [3:0] conditional_a; + always_comb begin + conditional_a = 4'd0; + if (rand_a[0]) begin + conditional_a = rand_b[3:0]; + end else begin + conditional_a = ~rand_b[3:0]; + end + end + `signal(CONDITONAL_A, conditional_a); + + logic [3:0] conditional_b; + always_comb begin + conditional_b = 4'd0; + if (rand_a[0]) begin + conditional_b = rand_b[3:0]; + end + end + `signal(CONDITONAL_B, conditional_b); + + // verilator lint_off LATCH + logic [3:0] conditional_c; + always_comb begin // nosynth + if (rand_a[0]) begin + conditional_c = rand_b[3:0]; + end + if (~rand_a[0]) begin + conditional_c = ~rand_b[3:0]; + end + end + `signal(CONDITONAL_C, conditional_c); + // verilator lint_on LATCH + + logic [3:0] conditional_d; + always_comb begin + if (rand_a[0]) begin + conditional_d = rand_b[3:0]; + end else if (rand_a[1]) begin + conditional_d = ~rand_b[3:0]; + end else begin + conditional_d = rand_b[7:4]; + end + end + `signal(CONDITONAL_D, conditional_d); + + logic [3:0] conditional_e; + always_comb begin + conditional_e = 4'd0; + if (rand_a[0]) begin + conditional_e = rand_b[3:0]; + end else begin + if (rand_a[1]) begin + conditional_e = rand_b[3:0]; + end else begin + conditional_e = rand_b[7:4]; + end + conditional_e = ~conditional_e; + end + end + `signal(CONDITONAL_E, conditional_e); + + logic condigional_f; + always_comb begin + if (rand_b[0]) begin + condigional_f = 1'h1; + if (rand_b[1]) begin + condigional_f = rand_a[0]; + end + end else begin + condigional_f = 1'b0; + end + end + `signal(CONDITONAL_F, condigional_f); + + logic [7:0] partial_conditional_a; + always_comb begin + partial_conditional_a[1:0] = 2'd0; + if (rand_a[0]) begin + partial_conditional_a[0] = rand_b[0]; + end else begin + partial_conditional_a[1] = rand_b[1]; + end + partial_conditional_a[4:3] = rand_b[4:3]; + end + `signal(PARTIAL_CONDITONAL_A, partial_conditional_a); + + logic [3:0] partial_conditional_b; + always_comb begin + partial_conditional_b[1:0] = 2'd0; + if (rand_a[0]) begin + partial_conditional_b[0] = rand_b[0]; + end + if (rand_a[1]) begin + partial_conditional_b[1] = rand_b[1]; + end + end + `signal(PARTIAL_CONDITONAL_B, partial_conditional_b); + + logic [3:0] becomes_full; + always_comb begin + becomes_full[2:0] = rand_a[2:0]; + becomes_full[3] = ~rand_a[3]; + if (rand_b[0]) begin + becomes_full = ~becomes_full; + end + end + `signal(BECOMES_FULL, becomes_full); + + // verilator lint_off LATCH + logic [3:0] latch_a; + logic [3:0] latch_b; + always_comb begin // nosynth + if (rand_b[0]) begin + latch_a[3:1] = ~rand_a[3:1]; + end + latch_b = latch_a; + end + assign latch_a[0] = rand_a[0]; + `signal(LATCH, latch_b); + // verilator lint_on LATCH + + // verilator lint_off MULTIDRIVEN + logic static_temporary_a; + logic static_temporary_b; + logic static_temporary_tmp; + always_comb begin // revert + static_temporary_tmp = rand_a[0]; + static_temporary_a = ~static_temporary_tmp; + end + always_comb begin // revert + static_temporary_tmp = static_temporary_a; + static_temporary_b = ~static_temporary_tmp; + end + // verilator lint_on MULTIDRIVEN + `signal(STATIC_TEMPORARY, {static_temporary_tmp, static_temporary_b, static_temporary_a, rand_a[0]}); + + logic [2:0] partial_temporary_a; + logic [2:0] partial_temporary_tmp; + always_comb begin + partial_temporary_tmp[2] = rand_a[3]; + partial_temporary_tmp[1] = rand_a[2]; + partial_temporary_tmp[0] = rand_a[1]; + partial_temporary_a = partial_temporary_tmp; + end + `signal(PARTIAL_TEMPORARY, partial_temporary_a); +endmodule diff --git a/test_regress/t/t_dist_docs_summary.py b/test_regress/t/t_dist_docs_summary.py index d75cc5ce2..c8576c6f5 100755 --- a/test_regress/t/t_dist_docs_summary.py +++ b/test_regress/t/t_dist_docs_summary.py @@ -17,6 +17,7 @@ Waivers = [ '+verilator+prof+threads+file+', # Deprecated '+verilator+prof+threads+start+', # Deprecated '+verilator+prof+threads+window+', # Deprecated + '-fdfg-synthesize-all', # Mostly used for testing '-fno-', # Documented differently '-no-lineno', # Deprecated '-no-order-clock-delay', # Deprecated diff --git a/test_regress/t/t_dpi_qw.py b/test_regress/t/t_dpi_qw.py index 6e3f18c06..b9e84b1c1 100755 --- a/test_regress/t/t_dpi_qw.py +++ b/test_regress/t/t_dpi_qw.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.compile(v_flags2=["t/t_dpi_qw_c.cpp"], - verilator_flags2=["-Wall -Wno-DECLFILENAME -Wno-UNOPTFLAT -no-l2name"]) + verilator_flags2=["-Wall -Wno-DECLFILENAME -no-l2name"]) test.execute() diff --git a/test_regress/t/t_func_crc.py b/test_regress/t/t_func_crc.py index 0304e7ed5..d6276df18 100755 --- a/test_regress/t/t_func_crc.py +++ b/test_regress/t/t_func_crc.py @@ -18,6 +18,6 @@ test.compile( test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 3888) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 3434) test.passes() diff --git a/test_regress/t/t_hier_block.v b/test_regress/t/t_hier_block.v index aaf0016f3..d21be731f 100644 --- a/test_regress/t/t_hier_block.v +++ b/test_regress/t/t_hier_block.v @@ -35,12 +35,10 @@ module t (/*AUTOARG*/ `ifdef PROTLIB_TOP secret i_secred(.clk(clk)); `else - /* verilator lint_off UNOPTFLAT */ wire [7:0] out0; wire [7:0] out1; wire [7:0] out2; wire [7:0] out3; - /* verilator lint_on UNOPTFLAT */ wire [7:0] out3_2; wire [7:0] out5; wire [7:0] out6; diff --git a/test_regress/t/t_hier_block_chained.py b/test_regress/t/t_hier_block_chained.py index 553bf8ea4..b443056a4 100755 --- a/test_regress/t/t_hier_block_chained.py +++ b/test_regress/t/t_hier_block_chained.py @@ -24,7 +24,6 @@ test.compile( benchmarksim=1, v_flags2=[ config_file, "+define+SIM_CYCLES=" + str(test.cycles), "--hierarchical", "--stats", - "-Wno-UNOPTFLAT", (f"-DWORKERS={HIER_BLOCK_THREADS}" if test.vltmt and HIER_BLOCK_THREADS > 1 else ""), (f"--hierarchical-threads {HIER_THREADS}" if test.vltmt and HIER_THREADS > 1 else "") ], @@ -33,9 +32,9 @@ test.compile( 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+)', 2) + r'Optimizations, Thread schedule count\s+(\d+)', 3) test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", - r'Optimizations, Thread schedule total tasks\s+(\d+)', 3) + r'Optimizations, Thread schedule total tasks\s+(\d+)', 4) test.execute() diff --git a/test_regress/t/t_lint_always_comb_multidriven_bad.out b/test_regress/t/t_lint_always_comb_multidriven_bad.out index 83d6cb364..25b66bffc 100644 --- a/test_regress/t/t_lint_always_comb_multidriven_bad.out +++ b/test_regress/t/t_lint_always_comb_multidriven_bad.out @@ -48,40 +48,36 @@ t/t_lint_always_comb_multidriven_bad.v:40:16: ... Location of other write 40 | always_comb out6 = d; | ^~~~ -%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:15: Bits [0:0] of signal 'out2' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:15: Bit [0] of signal 'out2' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_lint_always_comb_multidriven_bad.v:28:16: ... Location of first driver + t/t_lint_always_comb_multidriven_bad.v:28:16: ... Location of offending driver 28 | assign out2 = d; | ^ - t/t_lint_always_comb_multidriven_bad.v:29:21: ... Location of other driver + t/t_lint_always_comb_multidriven_bad.v:29:21: ... Location of offending driver 29 | always_comb out2 = 1'b0; | ^ - t/t_lint_always_comb_multidriven_bad.v:17:15: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:15: Bits [0:0] of signal 'out4' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:15: Bit [0] of signal 'out4' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_lint_always_comb_multidriven_bad.v:34:21: ... Location of first driver + t/t_lint_always_comb_multidriven_bad.v:34:21: ... Location of offending driver 34 | always_comb out4 = 1'b0; | ^ - t/t_lint_always_comb_multidriven_bad.v:35:16: ... Location of other driver + t/t_lint_always_comb_multidriven_bad.v:35:16: ... Location of offending driver 35 | assign out4 = d; | ^ - t/t_lint_always_comb_multidriven_bad.v:19:15: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:15: Bits [0:0] of signal 'out5' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:15: Bit [0] of signal 'out5' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_lint_always_comb_multidriven_bad.v:37:21: ... Location of first driver + t/t_lint_always_comb_multidriven_bad.v:37:21: ... Location of offending driver 37 | always_comb out5 = 1'b0; | ^ - t/t_lint_always_comb_multidriven_bad.v:38:21: ... Location of other driver + t/t_lint_always_comb_multidriven_bad.v:38:21: ... Location of offending driver 38 | always_comb out5 = d; | ^ - t/t_lint_always_comb_multidriven_bad.v:20:15: ... Only the first driver will be respected -%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:15: Bits [0:0] of signal 'out6' have multiple combinational drivers +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:15: Bit [0] of signal 'out6' have multiple combinational drivers. This can cause performance degradation. : ... note: In instance 't' - t/t_lint_always_comb_multidriven_bad.v:40:21: ... Location of first driver + t/t_lint_always_comb_multidriven_bad.v:40:21: ... Location of offending driver 40 | always_comb out6 = d; | ^ - t/t_lint_always_comb_multidriven_bad.v:41:21: ... Location of other driver + t/t_lint_always_comb_multidriven_bad.v:41:21: ... Location of offending driver 41 | always_comb out6 = 1'b0; | ^ - t/t_lint_always_comb_multidriven_bad.v:21:15: ... Only the first driver will be respected %Error: Exiting due to diff --git a/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out new file mode 100644 index 000000000..0a5648357 --- /dev/null +++ b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out @@ -0,0 +1,51 @@ +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:26:16: Variable written to in always_comb also written by other process (IEEE 1800-2023 9.2.2.2): 'out1' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:26:16: + 26 | always_comb out1 = d; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:25:11: ... Location of other write + 25 | assign out1 = 1'b0; + | ^~~~ + ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest + ... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message. +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:29:16: Variable written to in always_comb also written by other process (IEEE 1800-2023 9.2.2.2): 'out2' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:29:16: + 29 | always_comb out2 = 1'b0; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:28:11: ... Location of other write + 28 | assign out2 = d; + | ^~~~ +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:32:11: Variable also written to in always_comb (IEEE 1800-2023 9.2.2.2): 'out3' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:32:11: + 32 | assign out3 = 1'b0; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:31:16: ... Location of always_comb write + 31 | always_comb out3 = d; + | ^~~~ +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:35:11: Variable also written to in always_comb (IEEE 1800-2023 9.2.2.2): 'out4' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:35:11: + 35 | assign out4 = d; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:34:16: ... Location of always_comb write + 34 | always_comb out4 = 1'b0; + | ^~~~ +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:38:16: Variable written to in always_comb also written by other process (IEEE 1800-2023 9.2.2.2): 'out5' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:38:16: + 38 | always_comb out5 = d; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:37:16: ... Location of other write + 37 | always_comb out5 = 1'b0; + | ^~~~ +%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:41:16: Variable written to in always_comb also written by other process (IEEE 1800-2023 9.2.2.2): 'out6' + : ... note: In instance 't' + t/t_lint_always_comb_multidriven_bad.v:41:16: + 41 | always_comb out6 = 1'b0; + | ^~~~ + t/t_lint_always_comb_multidriven_bad.v:40:16: ... Location of other write + 40 | always_comb out6 = d; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.py b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.py index 58bab8994..07d3c7396 100755 --- a/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.py +++ b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.py @@ -14,6 +14,6 @@ test.top_filename = "t/t_lint_always_comb_multidriven_bad.v" test.lint(verilator_flags2=['--public-flat-rw --lint-only'], fails=True, - expect_filename="t/t_lint_always_comb_multidriven_bad.out") + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_lint_didnotconverge_bad.out b/test_regress/t/t_lint_didnotconverge_bad.out index f72c08997..526a34911 100644 --- a/test_regress/t/t_lint_didnotconverge_bad.out +++ b/test_regress/t/t_lint_didnotconverge_bad.out @@ -1,3 +1,3 @@ --V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a) +-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] b) %Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge. Aborting... diff --git a/test_regress/t/t_lint_inherit.py b/test_regress/t/t_lint_inherit.py index c3bd7274f..5484b6a2d 100755 --- a/test_regress/t/t_lint_inherit.py +++ b/test_regress/t/t_lint_inherit.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["-fno-dfg"]) test.passes() diff --git a/test_regress/t/t_order.v b/test_regress/t/t_order.v index 9a54fa524..14276f6ba 100644 --- a/test_regress/t/t_order.v +++ b/test_regress/t/t_order.v @@ -62,9 +62,7 @@ module t (/*AUTOARG*/ end reg sepassign_in; - // verilator lint_off UNOPTFLAT wire [3:0] sepassign; - // verilator lint_on UNOPTFLAT // verilator lint_off UNOPT assign #0.1 sepassign[0] = 0, diff --git a/test_regress/t/t_order_clkinst.py b/test_regress/t/t_order_clkinst.py index 3f7b706e5..9e12d32c2 100755 --- a/test_regress/t/t_order_clkinst.py +++ b/test_regress/t/t_order_clkinst.py @@ -17,7 +17,7 @@ test.scenarios('simulator') # closely enough to pass the same test? # If not -- probably we should switch this to be vlt-only. -test.compile(verilator_flags2=["--trace-vcd"]) +test.compile(verilator_flags2=["--trace-vcd", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_order_comboclkloop.py b/test_regress/t/t_order_comboclkloop.py index d4f986441..dc6cab445 100755 --- a/test_regress/t/t_order_comboclkloop.py +++ b/test_regress/t/t_order_comboclkloop.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_order_comboloop.py b/test_regress/t/t_order_comboloop.py index d4f986441..dc6cab445 100755 --- a/test_regress/t/t_order_comboloop.py +++ b/test_regress/t/t_order_comboloop.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_order_doubleloop.py b/test_regress/t/t_order_doubleloop.py index d4f986441..dc6cab445 100755 --- a/test_regress/t/t_order_doubleloop.py +++ b/test_regress/t/t_order_doubleloop.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_order_first.v b/test_regress/t/t_order_first.v index 79c3516e8..eaf1403ee 100644 --- a/test_regress/t/t_order_first.v +++ b/test_regress/t/t_order_first.v @@ -31,9 +31,7 @@ module t_netlist (/*AUTOARG*/ // This entire module should optimize to nearly nothing... - // verilator lint_off UNOPTFLAT reg [4:0] a,a2,b,c,d,e; - // verilator lint_on UNOPTFLAT initial a=5'd1; @@ -42,13 +40,11 @@ module t_netlist (/*AUTOARG*/ c <= b+5'd1; // Better for ordering if this moves before previous statement end - // verilator lint_off UNOPT always @ (d or /*AS*/a or c) begin e = d+5'd1; a2 = a+5'd1; // This can be pulled out of the middle of the always d = c+5'd1; // Better for ordering if this moves before previous statement end - // verilator lint_on UNOPT always @ (posedge also_fastclk) begin if (_mode==5) begin diff --git a/test_regress/t/t_order_multialways.v b/test_regress/t/t_order_multialways.v index f38590db8..46c67d672 100644 --- a/test_regress/t/t_order_multialways.v +++ b/test_regress/t/t_order_multialways.v @@ -23,7 +23,6 @@ module t (/*AUTOARG*/ h = {g[15:0], g[31:16]}; end - // verilator lint_off UNOPTFLAT reg [31:0] e2,f2,g2,h2; always @ (/*AS*/f2, g2) begin h2 = {g2[15:0], g2[31:16]}; @@ -33,7 +32,6 @@ module t (/*AUTOARG*/ f2 = {e2[15:0], e2[31:16]}; e2 = in_a; end - // verilator lint_on UNOPTFLAT integer cyc; initial cyc=1; always @ (posedge clk) begin diff --git a/test_regress/t/t_order_quad.py b/test_regress/t/t_order_quad.py index f37ad07c8..94dfdc72f 100755 --- a/test_regress/t/t_order_quad.py +++ b/test_regress/t/t_order_quad.py @@ -11,7 +11,9 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(make_top_shell=False, make_main=False, verilator_flags2=["--exe", test.pli_filename]) +test.compile(make_top_shell=False, + make_main=False, + verilator_flags2=["--exe", test.pli_filename, "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_order_wireloop.py b/test_regress/t/t_order_wireloop.py index 2a9b7aa62..a96c62185 100755 --- a/test_regress/t/t_order_wireloop.py +++ b/test_regress/t/t_order_wireloop.py @@ -11,14 +11,14 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(fails=test.vlt_all) +test.compile(fails=test.vlt_all, verilator_flags2=["-fno-dfg"]) # Used to be %Error: t/t_order_wireloop.v:\d+: Wire inputs its own output, creating circular logic .wire x=x. # However we no longer gate optimize this # Can't use expect_filename here as unstable output test.file_grep( test.compile_log_filename, - r"%Warning-UNOPTFLAT: t/t_order_wireloop.v:\d+:\d+: Signal unoptimizable: Circular combinational logic: \'bar\'" + r"%Warning-UNOPTFLAT: t/t_order_wireloop.v:\d+:\d+: Signal unoptimizable: Circular combinational logic: \'t.foo\'" ) test.passes() diff --git a/test_regress/t/t_split_var_0.py b/test_regress/t/t_split_var_0.py index 24ed1b20d..d9c2c6068 100755 --- a/test_regress/t/t_split_var_0.py +++ b/test_regress/t/t_split_var_0.py @@ -14,7 +14,7 @@ test.scenarios('simulator') # CI environment offers 2 VCPUs, 2 thread setting causes the following warning. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # So use 6 threads here though it's not optimal in performance, but ok. -test.compile(verilator_flags2=['--stats', test.t_dir + "/t_split_var_0.vlt"], +test.compile(verilator_flags2=['--stats', test.t_dir + "/t_split_var_0.vlt", "-fno-dfg"], threads=(6 if test.vltmt else 1)) test.execute() diff --git a/test_regress/t/t_split_var_0.v b/test_regress/t/t_split_var_0.v index c9efe9e76..65fd4cde6 100644 --- a/test_regress/t/t_split_var_0.v +++ b/test_regress/t/t_split_var_0.v @@ -351,13 +351,10 @@ module unpack2pack #(parameter WIDTH = 8) return tmp; endfunction - /* verilator lint_off UNOPTFLAT*/ task automatic to_packed1(input logic in[1:0] /*verilator split_var*/, output logic [1:0] out /*verilator split_var*/); out[1] = in[1]; out[0] = in[0]; endtask - /* verilator lint_on UNOPTFLAT*/ - generate for (genvar i = 4; i < WIDTH; i += 4) begin diff --git a/test_regress/t/t_split_var_1_bad.py b/test_regress/t/t_split_var_1_bad.py index 699eafaf5..fed583da0 100755 --- a/test_regress/t/t_split_var_1_bad.py +++ b/test_regress/t/t_split_var_1_bad.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('linter') -test.lint(fails=True, verilator_flags2=['--stats'], expect_filename=test.golden_filename) +test.lint(fails=True, + verilator_flags2=['--stats', "-fno-dfg"], + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_split_var_2_trace.py b/test_regress/t/t_split_var_2_trace.py index 7a9eac173..0fcaf3d8c 100755 --- a/test_regress/t/t_split_var_2_trace.py +++ b/test_regress/t/t_split_var_2_trace.py @@ -15,8 +15,9 @@ test.top_filename = "t/t_split_var_0.v" # CI environment offers 2 VCPUs, 2 thread setting causes the following warning. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # So use 6 threads here though it's not optimal in performance, but ok. -test.compile(verilator_flags2=['--cc --trace-vcd --stats +define+TEST_ATTRIBUTES'], - threads=(6 if test.vltmt else 1)) +test.compile( + verilator_flags2=['--cc', '--trace-vcd', '--stats', '+define+TEST_ATTRIBUTES', "-fno-dfg"], + threads=(6 if test.vltmt else 1)) test.execute() diff --git a/test_regress/t/t_split_var_3_wreal.py b/test_regress/t/t_split_var_3_wreal.py index 524a7b18d..8860c1644 100755 --- a/test_regress/t/t_split_var_3_wreal.py +++ b/test_regress/t/t_split_var_3_wreal.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=['--stats']) +test.compile(verilator_flags2=['--stats', "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_split_var_4.py b/test_regress/t/t_split_var_4.py index 57b43d1a0..4a461e645 100755 --- a/test_regress/t/t_split_var_4.py +++ b/test_regress/t/t_split_var_4.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=['--stats', '-DENABLE_SPLIT_VAR=1']) +test.compile(verilator_flags2=['--stats', '-DENABLE_SPLIT_VAR=1', "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_split_var_5.py b/test_regress/t/t_split_var_5.py index 1cfb7e245..12d5eaabe 100755 --- a/test_regress/t/t_split_var_5.py +++ b/test_regress/t/t_split_var_5.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_split_var_4.v" -test.compile(verilator_flags2=['--stats']) +test.compile(verilator_flags2=['--stats', "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_timing_debug1.out b/test_regress/t/t_timing_debug1.out index eefec27ca..4a8b8f770 100644 --- a/test_regress/t/t_timing_debug1.out +++ b/test_regress/t/t_timing_debug1.out @@ -11,7 +11,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_initial__TOP__Vtiming__1 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 -V{t#,#}+ Vt_timing_debug1___024root___eval_initial__TOP__Vtiming__2 --V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:50 +-V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:48 -V{t#,#}+ Vt_timing_debug1___024root___eval_initial__TOP__Vtiming__3 -V{t#,#}+ Vt_timing_debug1___024root___eval_settle -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__stl @@ -24,14 +24,12 @@ -V{t#,#} 'stl' region trigger index 4 is active: @([hybrid] t.clk2) -V{t#,#} 'stl' region trigger index 5 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'stl' region trigger index 6 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'stl' region trigger index 7 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___eval_stl -V{t#,#}+ Vt_timing_debug1___024root___stl_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1____Vfork_2__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__stl -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__stl -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__stl @@ -57,18 +55,16 @@ -V{t#,#} 'act' region trigger index 3 is active: @([hybrid] t.clk2) -V{t#,#} 'act' region trigger index 4 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'act' region trigger index 5 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'act' region trigger index 6 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} - Process waiting at t/t_timing_sched.v:17 -V{t#,#} Committing processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 +-V{t#,#} - Process waiting at t/t_timing_sched.v:48 -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 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -90,13 +86,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 3: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 11: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -105,7 +101,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -121,7 +117,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -141,6 +136,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -156,14 +152,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 6: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 7: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 11: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -208,14 +204,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 7: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 9: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 11: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -245,13 +241,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 9: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 11: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -260,7 +256,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -272,7 +268,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -290,6 +285,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -305,14 +301,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 11: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 12: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 13: Process waiting at t/t_timing_sched.v:17 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -321,35 +317,32 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 3 is active: @([hybrid] t.clk2) --V{t#,#} 'act' region trigger index 9 is active: @(posedge t.clk2) +-V{t#,#} 'act' region trigger index 8 is active: @(posedge t.clk2) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 +-V{t#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#} Ready processes waiting for @(posedge t.clk2): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} Resuming processes waiting for @(posedge t.clk2) --V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:18 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:18 -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1____Vfork_2__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 4 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'act' region trigger index 5 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'act' region trigger index 6 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -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__1 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -357,6 +350,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -372,19 +366,19 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 12: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 12: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Awaiting time 12: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Awaiting time 13: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -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#,#} Resuming: Process waiting at t/t_timing_sched.v:48 +-V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:48 -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 @@ -393,7 +387,7 @@ -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#,#} - Process waiting at t/t_timing_sched.v:48 -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 @@ -429,14 +423,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 13: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 15: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -466,13 +460,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 15: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -481,7 +475,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -497,7 +491,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -517,6 +510,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -532,14 +526,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 18: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 19: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -584,14 +578,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 19: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 21: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -621,13 +615,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 21: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -636,7 +630,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -648,7 +642,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -666,6 +659,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -681,14 +675,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 22: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 24: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 25: Process waiting at t/t_timing_sched.v:17 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -733,14 +727,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 24: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 25: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -785,14 +779,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 25: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 27: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -822,13 +816,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 27: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -837,7 +831,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -849,7 +843,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -867,6 +860,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -882,14 +876,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 30: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 31: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -934,14 +928,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 31: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -971,13 +965,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 33: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 @@ -988,8 +982,8 @@ -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) -V{t#,#} 'act' region trigger index 3 is active: @([hybrid] t.clk2) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) --V{t#,#} 'act' region trigger index 9 is active: @(posedge t.clk2) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 8 is active: @(posedge t.clk2) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -997,11 +991,11 @@ -V{t#,#} Resuming processes waiting for @(posedge t.clk1) -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Ready processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 +-V{t#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#} Ready processes waiting for @(posedge t.clk2): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} Resuming processes waiting for @(posedge t.clk2) --V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:18 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:18 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1011,7 +1005,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1____Vfork_2__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1019,7 +1012,6 @@ -V{t#,#} 'act' region trigger index 2 is active: @([hybrid] __VassignWgen_h########__0) -V{t#,#} 'act' region trigger index 4 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'act' region trigger index 5 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'act' region trigger index 6 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 @@ -1027,7 +1019,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1036,6 +1027,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1051,18 +1043,18 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 34: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Awaiting time 34: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Awaiting time 36: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 37: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes --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#,#} Resuming: Process waiting at t/t_timing_sched.v:48 +-V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:48 -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 @@ -1071,7 +1063,7 @@ -V{t#,#} No triggers active -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#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 @@ -1089,14 +1081,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 36: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 37: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1141,14 +1133,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 37: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 39: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -1178,13 +1170,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 39: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1193,7 +1185,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -1209,7 +1201,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1229,6 +1220,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1244,14 +1236,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 42: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 43: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1296,14 +1288,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 43: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 45: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -1333,13 +1325,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 44: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 45: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1384,13 +1376,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 45: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1399,7 +1391,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -1411,7 +1403,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1429,6 +1420,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1444,14 +1436,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 48: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 49: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1496,14 +1488,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 49: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 51: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -1533,13 +1525,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 51: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1548,7 +1540,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -1560,7 +1552,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1578,6 +1569,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1593,14 +1585,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 54: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:17 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1645,14 +1637,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 55: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 57: Process waiting at t/t_timing_sched.v:10 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 @@ -1663,37 +1655,34 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 3 is active: @([hybrid] t.clk2) --V{t#,#} 'act' region trigger index 9 is active: @(posedge t.clk2) +-V{t#,#} 'act' region trigger index 8 is active: @(posedge t.clk2) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:17 -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 +-V{t#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#} Ready processes waiting for @(posedge t.clk2): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} Resuming processes waiting for @(posedge t.clk2) --V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:18 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:18 -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1____Vfork_2__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 4 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'act' region trigger index 5 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'act' region trigger index 6 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -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__1 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1701,6 +1690,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1716,17 +1706,17 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 56: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Awaiting time 56: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Awaiting time 57: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes --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#,#} Resuming: Process waiting at t/t_timing_sched.v:48 +-V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:48 -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 @@ -1735,7 +1725,7 @@ -V{t#,#} No triggers active -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#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 @@ -1753,13 +1743,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 57: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1768,7 +1758,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -1784,7 +1774,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1804,6 +1793,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1819,14 +1809,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 60: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 61: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1871,14 +1861,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 61: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 63: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -1908,13 +1898,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 63: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -1923,7 +1913,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -1935,7 +1925,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -1953,6 +1942,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -1968,14 +1958,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Awaiting time 66: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 67: Process waiting at t/t_timing_sched.v:17 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 @@ -2027,14 +2017,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 67: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 69: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -2064,13 +2054,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 69: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -2079,7 +2069,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -2091,7 +2081,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -2109,6 +2098,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -2124,14 +2114,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 72: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 73: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -2176,14 +2166,14 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 73: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 75: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:17 @@ -2213,13 +2203,13 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 75: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#}+ Vt_timing_debug1___024root___eval_act @@ -2228,7 +2218,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) --V{t#,#} 'act' region trigger index 7 is active: @(posedge t.clk1) +-V{t#,#} 'act' region trigger index 6 is active: @(posedge t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk1): @@ -2240,7 +2230,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0____Vfork_1__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -2258,6 +2247,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba -V{t#,#}+ Vt_timing_debug1___024root___nba_sequent__TOP__0 +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -2273,12 +2263,12 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: -V{t#,#} Awaiting time 77: Process waiting at t/t_timing_sched.v:13 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 -V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 79: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Resuming delayed processes @@ -2289,35 +2279,32 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 3 is active: @([hybrid] t.clk2) --V{t#,#} 'act' region trigger index 9 is active: @(posedge t.clk2) +-V{t#,#} 'act' region trigger index 8 is active: @(posedge t.clk2) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Ready processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 +-V{t#,#} - Process waiting at t/t_timing_sched.v:48 -V{t#,#} Ready processes waiting for @(posedge t.clk2): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} Resuming processes waiting for @(posedge t.clk2) --V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:18 -V{t#,#} Suspending process waiting for @(posedge t.clk1) at t/t_timing_sched.v:18 -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__1____Vfork_2__0 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 4 is active: @([hybrid] __VassignWtmp_h########__0) -V{t#,#} 'act' region trigger index 5 is active: @([hybrid] __VassignWgen_h########__0) --V{t#,#} 'act' region trigger index 6 is active: @([hybrid] t.c1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#} Committing processes waiting for @(posedge t.clk1): -V{t#,#} - Process waiting at t/t_timing_sched.v:18 -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__1 --V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__2 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act @@ -2325,6 +2312,7 @@ -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__nba -V{t#,#}+ Vt_timing_debug1___024root___eval_nba +-V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__1 -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act @@ -2340,20 +2328,20 @@ -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug1___024root___eval_triggers__act -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act --V{t#,#} 'act' region trigger index 8 is active: @([true] __VdlySched.awaitingCurrentTime()) +-V{t#,#} 'act' region trigger index 7 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:52 --V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:50 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:10 +-V{t#,#} Awaiting time 78: Process waiting at t/t_timing_sched.v:48 -V{t#,#} Awaiting time 79: Process waiting at t/t_timing_sched.v:17 -V{t#,#} Awaiting time 88: Process waiting at t/t_timing_sched.v:13 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:52 +-V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 *-* 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#,#} Resuming: Process waiting at t/t_timing_sched.v:48 -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 diff --git a/test_regress/t/t_timing_sched.v b/test_regress/t/t_timing_sched.v index 6bb015fb9..596616a26 100644 --- a/test_regress/t/t_timing_sched.v +++ b/test_regress/t/t_timing_sched.v @@ -37,14 +37,12 @@ module t; `endif end - // verilator lint_off UNOPTFLAT int c1 = 0; int c2 = 0; always @(b2, c1) begin c2 = c1 >> 3; c1 = b2 << 3; end - // verilator lint_on UNOPTFLAT always @(posedge clk1) if (a2 != a1 << 1) $stop; always @(posedge clk2) #1 if (b2 != b1 << 2) $stop; diff --git a/test_regress/t/t_unopt_array.py b/test_regress/t/t_unopt_array.py index c37bc018e..51e61aa39 100755 --- a/test_regress/t/t_unopt_array.py +++ b/test_regress/t/t_unopt_array.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-Wno-UNOPTFLAT"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_unopt_array_csplit.py b/test_regress/t/t_unopt_array_csplit.py index ce0e4bae6..ec16097b5 100755 --- a/test_regress/t/t_unopt_array_csplit.py +++ b/test_regress/t/t_unopt_array_csplit.py @@ -12,7 +12,10 @@ import vltest_bootstrap test.scenarios('vlt_all') test.top_filename = "t/t_unopt_array.v" -test.compile(v_flags2=["--trace-vcd --output-split 1 --output-split-cfuncs 1 -Wno-UNOPTFLAT"]) +test.compile(v_flags2=[ + "--trace-vcd", "--output-split", "1", "--output-split-cfuncs", "1", "-Wno-UNOPTFLAT", + "-fno-dfg" +]) test.execute() diff --git a/test_regress/t/t_unopt_array_typedef.py b/test_regress/t/t_unopt_array_typedef.py index 809e92873..fb7da0974 100755 --- a/test_regress/t/t_unopt_array_typedef.py +++ b/test_regress/t/t_unopt_array_typedef.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_unopt_array.v" -test.compile(verilator_flags2=["-Wno-UNOPTFLAT +define+USE_TYPEDEF"]) +test.compile(verilator_flags2=["-Wno-UNOPTFLAT", "+define+USE_TYPEDEF", "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_unopt_bound.py b/test_regress/t/t_unopt_bound.py index d4f986441..dc6cab445 100755 --- a/test_regress/t/t_unopt_bound.py +++ b/test_regress/t/t_unopt_bound.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_unopt_combo.py b/test_regress/t/t_unopt_combo.py index 64265ff66..9027b0470 100755 --- a/test_regress/t/t_unopt_combo.py +++ b/test_regress/t/t_unopt_combo.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(v_flags2=['+define+ALLOW_UNOPT']) +test.compile(v_flags2=['+define+ALLOW_UNOPT', "-fno-dfg"]) test.execute() diff --git a/test_regress/t/t_unopt_combo_bad.out b/test_regress/t/t_unopt_combo_bad.out index bee8bc003..8cd58f59f 100644 --- a/test_regress/t/t_unopt_combo_bad.out +++ b/test_regress/t/t_unopt_combo_bad.out @@ -4,7 +4,7 @@ ... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest ... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message. t/t_unopt_combo.v:23:25: Example path: t.b - t/t_unopt_combo.v:137:14: Example path: ASSIGNW + t/t_unopt_combo.v:124:4: Example path: ALWAYS t/t_unopt_combo.v:24:25: Example path: t.c t/t_unopt_combo.v:81:4: Example path: ALWAYS t/t_unopt_combo.v:23:25: Example path: t.b diff --git a/test_regress/t/t_unopt_combo_bad.py b/test_regress/t/t_unopt_combo_bad.py index 1592b6d48..c659a17b7 100755 --- a/test_regress/t/t_unopt_combo_bad.py +++ b/test_regress/t/t_unopt_combo_bad.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_unopt_combo.v" -test.compile(v_flags2=['+define+ATTRIBUTES'], +test.compile(v_flags2=['+define+ATTRIBUTES', "-fno-dfg"], fails=test.vlt_all, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unopt_combo_isolate.py b/test_regress/t/t_unopt_combo_isolate.py index 641974899..fa5b040b2 100755 --- a/test_regress/t/t_unopt_combo_isolate.py +++ b/test_regress/t/t_unopt_combo_isolate.py @@ -14,7 +14,7 @@ test.top_filename = "t/t_unopt_combo.v" out_filename = test.obj_dir + "/V" + test.name + ".tree.json" -test.compile(verilator_flags2=["--no-json-edit-nums +define+ISOLATE --stats"]) +test.compile(verilator_flags2=["--no-json-edit-nums", "+define+ISOLATE", "--stats", "-fno-dfg"]) if test.vlt_all: test.file_grep(test.stats, r'Optimizations, isolate_assignments blocks\s+3') diff --git a/test_regress/t/t_unopt_combo_isolate_vlt.py b/test_regress/t/t_unopt_combo_isolate_vlt.py index 4f3a4988c..70a35c8c2 100755 --- a/test_regress/t/t_unopt_combo_isolate_vlt.py +++ b/test_regress/t/t_unopt_combo_isolate_vlt.py @@ -14,8 +14,9 @@ test.top_filename = "t/t_unopt_combo.v" out_filename = test.obj_dir + "/V" + test.name + ".tree.json" -test.compile( - verilator_flags2=["--no-json-edit-nums --stats", test.t_dir + "/t_unopt_combo_isolate.vlt"]) +test.compile(verilator_flags2=[ + "--no-json-edit-nums", "--stats", test.t_dir + "/t_unopt_combo_isolate.vlt", "-fno-dfg" +]) if test.vlt_all: test.file_grep(test.stats, r'Optimizations, isolate_assignments blocks\s+3') diff --git a/test_regress/t/t_unopt_combo_waive.py b/test_regress/t/t_unopt_combo_waive.py index 508a70141..34a421ec1 100755 --- a/test_regress/t/t_unopt_combo_waive.py +++ b/test_regress/t/t_unopt_combo_waive.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('vlt_all') test.top_filename = "t/t_unopt_combo.v" -test.compile(v_flags2=['+define+ATTRIBUTES', "t/t_unopt_combo.vlt"], +test.compile(v_flags2=['+define+ATTRIBUTES', "t/t_unopt_combo.vlt", "-fno-dfg"], # Passes, as we waived ) diff --git a/test_regress/t/t_unopt_converge_initial_run_bad.py b/test_regress/t/t_unopt_converge_initial_run_bad.py index ebc9607d7..eaa85d927 100755 --- a/test_regress/t/t_unopt_converge_initial_run_bad.py +++ b/test_regress/t/t_unopt_converge_initial_run_bad.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_unopt_converge_initial.v" -test.compile(v_flags2=['+define+ALLOW_UNOPT --output-split 0']) +test.compile(v_flags2=['+define+ALLOW_UNOPT', '--output-split 0', "-fno-dfg"]) if test.vlt_all: test.execute(fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unopt_converge_ndbg_bad.py b/test_regress/t/t_unopt_converge_ndbg_bad.py index 65b34b60c..fbc3822fe 100755 --- a/test_regress/t/t_unopt_converge_ndbg_bad.py +++ b/test_regress/t/t_unopt_converge_ndbg_bad.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_unopt_converge.v" -test.compile(v_flags2=['+define+ALLOW_UNOPT'], make_flags=['CPPFLAGS_ADD=-UVL_DEBUG']) +test.compile(v_flags2=['+define+ALLOW_UNOPT', "-fno-dfg"], make_flags=['CPPFLAGS_ADD=-UVL_DEBUG']) if test.vlt_all: test.execute(fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unopt_converge_print_bad.py b/test_regress/t/t_unopt_converge_print_bad.py index d38fcc4a0..ac1bfd49d 100755 --- a/test_regress/t/t_unopt_converge_print_bad.py +++ b/test_regress/t/t_unopt_converge_print_bad.py @@ -13,7 +13,7 @@ test.scenarios('simulator') test.top_filename = "t/t_unopt_converge.v" #test.verilated_debug = 1 -test.compile(v_flags2=['+define+ALLOW_UNOPT --output-split 0'], +test.compile(v_flags2=['+define+ALLOW_UNOPT', '--output-split 0', "-fno-dfg"], make_flags=['CPPFLAGS_ADD=-DVL_DEBUG']) if test.vlt_all: diff --git a/test_regress/t/t_unopt_converge_run_bad.py b/test_regress/t/t_unopt_converge_run_bad.py index 846df0093..4d3251a15 100755 --- a/test_regress/t/t_unopt_converge_run_bad.py +++ b/test_regress/t/t_unopt_converge_run_bad.py @@ -12,7 +12,7 @@ import vltest_bootstrap test.scenarios('simulator') test.top_filename = "t/t_unopt_converge.v" -test.compile(v_flags2=['+define+ALLOW_UNOPT --output-split 0']) +test.compile(v_flags2=['+define+ALLOW_UNOPT', '--output-split 0', "-fno-dfg"]) if test.vlt_all: test.execute(fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unopt_converge_unopt_bad.out b/test_regress/t/t_unopt_converge_unopt_bad.out index 7aa0e9039..ad410cbb3 100644 --- a/test_regress/t/t_unopt_converge_unopt_bad.out +++ b/test_regress/t/t_unopt_converge_unopt_bad.out @@ -4,6 +4,6 @@ ... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest ... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message. t/t_unopt_converge.v:19:11: Example path: x - t/t_unopt_converge.v:23:9: Example path: ASSIGNW + t/t_unopt_converge.v:22:4: Example path: ALWAYS t/t_unopt_converge.v:19:11: Example path: x %Error: Exiting due to diff --git a/test_regress/t/t_unopt_converge_unopt_bad.py b/test_regress/t/t_unopt_converge_unopt_bad.py index 23a3f47d2..75751ea27 100755 --- a/test_regress/t/t_unopt_converge_unopt_bad.py +++ b/test_regress/t/t_unopt_converge_unopt_bad.py @@ -12,6 +12,6 @@ import vltest_bootstrap test.scenarios('vlt_all') test.top_filename = "t/t_unopt_converge.v" -test.compile(fails=True, expect_filename=test.golden_filename) +test.compile(verilator_flags2=["-fno-dfg"], fails=True, expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_unoptflat_simple_2_bad.out b/test_regress/t/t_unoptflat_simple_2_bad.out index 49c85ef34..5bcb5a581 100644 --- a/test_regress/t/t_unoptflat_simple_2_bad.out +++ b/test_regress/t/t_unoptflat_simple_2_bad.out @@ -4,11 +4,11 @@ ... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest ... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message. t/t_unoptflat_simple_2.v:16:15: Example path: t.x - t/t_unoptflat_simple_2.v:13:10: Example path: ASSIGNW + t/t_unoptflat_simple_2.v:19:18: Example path: ASSIGNW t/t_unoptflat_simple_2.v:16:15: Example path: t.x ... Widest variables candidate to splitting: - t/t_unoptflat_simple_2.v:16:15: t.x, width 3, circular fanout 1, can split_var + t/t_unoptflat_simple_2.v:16:15: t.x, width 3, circular fanout 2, can split_var ... Candidates with the highest fanout: - t/t_unoptflat_simple_2.v:16:15: t.x, width 3, circular fanout 1, can split_var + t/t_unoptflat_simple_2.v:16:15: t.x, width 3, circular fanout 2, can split_var ... Suggest add /*verilator split_var*/ or /*verilator isolate_assignments*/ to appropriate variables above. %Error: Exiting due to diff --git a/test_regress/t/t_unoptflat_simple_2_bad.py b/test_regress/t/t_unoptflat_simple_2_bad.py index a87556b80..6ed146135 100755 --- a/test_regress/t/t_unoptflat_simple_2_bad.py +++ b/test_regress/t/t_unoptflat_simple_2_bad.py @@ -14,7 +14,7 @@ test.top_filename = "t/t_unoptflat_simple_2.v" # Compile only test.compile(verilator_flags3=[], - verilator_flags2=["--report-unoptflat", "-fno-dfg-break-cycles"], + verilator_flags2=["--report-unoptflat", "-fno-dfg"], fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_unoptflat_simple_3_bad.py b/test_regress/t/t_unoptflat_simple_3_bad.py index 7c3da3bf6..02fe09491 100755 --- a/test_regress/t/t_unoptflat_simple_3_bad.py +++ b/test_regress/t/t_unoptflat_simple_3_bad.py @@ -13,6 +13,6 @@ test.scenarios('simulator') test.top_filename = "t/t_unoptflat_simple_3.v" # Compile only -test.compile(fails=True) +test.compile(verilator_flags2=["-fno-dfg"], fails=True) test.passes() diff --git a/test_regress/t/t_unoptflat_simple_bad.py b/test_regress/t/t_unoptflat_simple_bad.py index 85da89728..6440259f5 100755 --- a/test_regress/t/t_unoptflat_simple_bad.py +++ b/test_regress/t/t_unoptflat_simple_bad.py @@ -13,6 +13,6 @@ test.scenarios('simulator') test.top_filename = "t/t_unoptflat_simple.v" # Compile only -test.compile(fails=True) +test.compile(verilator_flags2=["-fno-dfg"], fails=True) test.passes() From a0edd4e907da8d9fdafdfe999c542fb5b44e7ff0 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 19 Aug 2025 22:02:10 +0100 Subject: [PATCH 194/252] Internals: Improve cppcheck flow and fix up issues (#6311) Added cppcheck-suppressions.txt in the repo root. You can add new patterns in there instead of having to parse the XML output. Also configure to add the -D__GNUC__ preprocessor macro, which makes it understand UASSERT (it understands the 'noreturn' function attribute). Added some case by case specific suppressions and fixed up other code, especially in V3Ast*h and V3Dfg*.h, including code generated by astgen that had some no-ops that irks cppcheck. One thing it does not seem to like is `const` class members with default initializers in the class. It will assume that's always the value, even if overridden in the constructor. We had few so removed them. With that a lot of files in `src/` are now clean or only have a handful of issues. Therefore, I have also deleted cppcheck_filtered, and made it produce human readable output straight to the terminal. Regarding cleaning up the reported nits, I kind of got bored after V3[A-E] so pausing here. Apologies for the merge conflicts. Tested with cppcheck 2.13.0 --- Makefile.in | 58 +++++----- include/verilatedos.h | 6 +- src/V3Active.cpp | 2 +- src/V3Assert.cpp | 10 +- src/V3AssertPre.cpp | 6 +- src/V3Ast.cpp | 27 +++-- src/V3Ast.h | 21 ++-- src/V3AstInlines.h | 8 +- src/V3AstNodeDType.h | 2 +- src/V3AstNodeExpr.h | 47 +++++--- src/V3AstNodeOther.h | 28 ++--- src/V3AstNodeStmt.h | 1 + src/V3AstNodes.cpp | 63 ++++++----- src/V3Begin.cpp | 2 +- src/V3Branch.cpp | 2 +- src/V3CUse.cpp | 7 +- src/V3Case.cpp | 8 +- src/V3Cast.cpp | 2 +- src/V3CfgLiveVariables.cpp | 2 +- src/V3Class.cpp | 5 +- src/V3Clean.cpp | 6 +- src/V3Const.cpp | 84 +++++++-------- src/V3Control.cpp | 7 +- src/V3Control.h | 5 +- src/V3Coverage.cpp | 24 ++--- src/V3CoverageJoin.cpp | 2 +- src/V3Dead.cpp | 6 +- src/V3Delayed.cpp | 12 +-- src/V3Descope.cpp | 4 +- src/V3Dfg.h | 4 +- src/V3DfgPasses.cpp | 4 +- src/V3DfgVertices.h | 2 + src/V3DiagSarif.cpp | 10 +- src/V3EmitCBase.h | 4 +- src/V3EmitCConstInit.h | 2 +- src/V3EmitCConstPool.cpp | 2 +- src/V3EmitCFunc.h | 14 +-- src/V3EmitCHeaders.cpp | 4 +- src/V3EmitCImp.cpp | 16 +-- src/V3EmitCMake.cpp | 12 ++- src/V3Simulate.h | 26 +++-- src/astgen | 39 ++++--- src/cppcheck-suppressions.txt | 33 ++++++ src/cppcheck_filtered | 196 ---------------------------------- 44 files changed, 357 insertions(+), 468 deletions(-) create mode 100644 src/cppcheck-suppressions.txt delete mode 100755 src/cppcheck_filtered diff --git a/Makefile.in b/Makefile.in index f98a03a49..763b18f94 100644 --- a/Makefile.in +++ b/Makefile.in @@ -409,36 +409,39 @@ CHECK_H = $(wildcard \ CHECK_YL = $(wildcard \ $(srcdir)/src/*.y \ $(srcdir)/src/*.l ) -CPPCHECK = src/cppcheck_filtered cppcheck -CPPCHECK_FLAGS = --enable=all --inline-suppr \ - --suppress=cstyleCast --suppress=ctunullpointer \ - --suppress=derefInvalidIteratorRedundantCheck \ - --suppress=nullPointer --suppress=nullPointerRedundantCheck \ - --suppress=templateRecursion \ - --suppress=unusedFunction --suppress=unusedScopedObject \ - --suppress=useInitializationList --suppress=useStlAlgorithm \ +CPPCHECK = cppcheck +CPPCHECK_CACHE = $(srcdir)/src/obj_dbg/cppcheck-cache +CPPCHECK_FLAGS = --enable=all +CPPCHECK_FLAGS += --inline-suppr +CPPCHECK_FLAGS += --suppressions-list=$(srcdir)/src/cppcheck-suppressions.txt +CPPCHECK_FLAGS += --cppcheck-build-dir=$(CPPCHECK_CACHE) +CPPCHECK_FLAGS += -DVL_DEBUG=1 -DVL_CPPCHECK=1 -D__GNUC__=1 +CPPCHECK_INC = -I$(srcdir)/include +CPPCHECK_INC += -I$(srcdir)/include/gtkwave +CPPCHECK_INC += -I$(srcdir)/include/vltstd +CPPCHECK_INC += -I$(srcdir)/src/obj_dbg +CPPCHECK_INC += -I$(srcdir)/src -CPPCHECK_FLAGS += --xml -CPPCHECK_DEP = $(subst .cpp,.cppcheck,$(CHECK_CPP)) -CPPCHECK_INC = -I$(srcdir)/include -I$(srcdir)/include/gtkwave -I$(srcdir)/include/vltstd -I$(srcdir)/src/obj_dbg -I$(srcdir)/src +$(CPPCHECK_CACHE): + mkdir -p $@ cppcheck: cppcheck-1 cppcheck-2 cppcheck-3 cppcheck-4 cppcheck-5 cppcheck-6 cppcheck-7 cppcheck-8 -cppcheck-1: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK1_CPP) -cppcheck-2: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK2_CPP) -cppcheck-3: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK3_CPP) -cppcheck-4: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK4_CPP) -cppcheck-5: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK5_CPP) -cppcheck-6: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK6_CPP) -cppcheck-7: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK7_CPP) -cppcheck-8: - $(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $(CPPCHECK8_CPP) +cppcheck-1: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK1_CPP) +cppcheck-2: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK2_CPP) +cppcheck-3: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK3_CPP) +cppcheck-4: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK4_CPP) +cppcheck-5: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK5_CPP) +cppcheck-6: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK6_CPP) +cppcheck-7: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK7_CPP) +cppcheck-8: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK8_CPP) CLANGTIDY = clang-tidy CLANGTIDY_FLAGS = -config='' \ @@ -512,7 +515,6 @@ PY_PROGRAMS = \ src/astgen \ src/bisonpre \ src/config_rev \ - src/cppcheck_filtered \ src/flexfix \ src/vlcovgen \ src/.gdbinit.py \ diff --git a/include/verilatedos.h b/include/verilatedos.h index b24b7a58c..a4c03c46d 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -52,15 +52,13 @@ // clang and gcc-8.0+ support no_sanitize("string") style attribute # if defined(__clang__) || (__GNUC__ >= 8) # define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment"))) -#else // The entire undefined sanitizer has to be disabled for older gcc +# else // The entire undefined sanitizer has to be disabled for older gcc # define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined)) -#endif +# endif # define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1))) # define VL_ATTR_PURE __attribute__((pure)) # define VL_ATTR_UNUSED __attribute__((unused)) -#ifndef VL_ATTR_WARN_UNUSED_RESULT # define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#endif # if !defined(_WIN32) && !defined(__MINGW32__) // All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in # define VL_ATTR_WEAK __attribute__((weak)) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index dc2367144..3497ede5e 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -166,7 +166,7 @@ public: // paths make an assignment. Detected latches are flagged in the variables AstVar void latchCheck(AstNode* nodep, bool latch_expected) { bool latch_detected = false; - for (const auto& vrp : m_outputs) { + for (const AstVarRef* const vrp : m_outputs) { LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p()); vertp->user(true); // Identify the output vertex we are checking paths _to_ if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true; diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 57ca6092b..72279d547 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -86,7 +86,7 @@ class AssertVisitor final : public VNVisitor { } VL_UNREACHABLE; } - string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message, + string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message, VDisplayType severity) { if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) { return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":" @@ -148,7 +148,7 @@ class AssertVisitor final : public VNVisitor { sampledp->dtypeFrom(nodep); return sampledp; } - AstVarRef* newMonitorNumVarRefp(AstNode* nodep, VAccess access) { + AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) { if (!m_monitorNumVarp) { m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum", nodep->findUInt64DType()}; @@ -158,7 +158,7 @@ class AssertVisitor final : public VNVisitor { varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); return varrefp; } - AstVarRef* newMonitorOffVarRefp(AstNode* nodep, VAccess access) { + AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) { if (!m_monitorOffVarp) { m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff", nodep->findBitDType()}; @@ -181,7 +181,7 @@ class AssertVisitor final : public VNVisitor { return newp; } - AstNodeStmt* newFireAssertUnchecked(AstNodeStmt* nodep, const string& message, + AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message, AstNodeExpr* exprsp = nullptr) { // Like newFireAssert() but omits the asserts-on check AstDisplay* const dispp @@ -194,7 +194,7 @@ class AssertVisitor final : public VNVisitor { return bodysp; } - AstNodeStmt* newFireAssert(AstNodeStmt* nodep, VAssertDirectiveType directiveType, + AstNodeStmt* newFireAssert(const AstNodeStmt* nodep, VAssertDirectiveType directiveType, VAssertType assertType, const string& message, AstNodeExpr* exprsp = nullptr) { AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp); diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 032c2855b..bcaf91c1e 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -102,7 +102,7 @@ private: } AstPropSpec* substitutePropertyCall(AstPropSpec* nodep) { if (AstFuncRef* const funcrefp = VN_CAST(nodep->propp(), FuncRef)) { - if (AstProperty* const propp = VN_CAST(funcrefp->taskp(), Property)) { + if (const AstProperty* const propp = VN_CAST(funcrefp->taskp(), Property)) { AstPropSpec* propExprp = getPropertyExprp(propp); // Substitute inner property call before copying in order to not doing the same for // each call of outer property call. @@ -183,7 +183,7 @@ private: m_clockingp->addNextHere(varp->unlinkFrBack()); varp->user1p(nodep); if (nodep->direction() == VDirection::OUTPUT) { - exprp->foreach([](AstNodeVarRef* varrefp) { + exprp->foreach([](const AstNodeVarRef* varrefp) { // Prevent confusing BLKANDNBLK warnings on clockvars due to generated assignments varrefp->fileline()->warnOff(V3ErrorCode::BLKANDNBLK, true); }); @@ -318,7 +318,7 @@ private: if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext()); FileLine* const flp = nodep->fileline(); AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack()); - AstConst* const constp = VN_CAST(valuep, Const); + const AstConst* const constp = VN_CAST(valuep, Const); if (constp->isZero()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index b0d8f7cfe..dea6e32f5 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -255,14 +255,13 @@ string AstNode::vpiName(const string& namein) { // This is slightly different from prettyName, in that when we encounter escaped characters, // we change that identifier to an escaped identifier, wrapping it with '\' and ' ' // as specified in LRM 23.6 - string name = namein; - if (0 == namein.substr(0, 7).compare("__SYM__")) name = namein.substr(7); + const size_t offset = VString::startsWith(namein, "__SYM__") ? 7 : 0; string pretty; - pretty.reserve(name.length()); + pretty.reserve(namein.length()); bool inEscapedIdent = false; int lastIdent = 0; - for (const char* pos = name.c_str(); *pos;) { + for (const char* pos = namein.c_str() + offset; *pos;) { char specialChar = 0; if (pos[0] == '-' && pos[1] == '>') { // -> specialChar = '.'; @@ -280,7 +279,7 @@ string AstNode::vpiName(const string& namein) { } else if (0 == std::strncmp(pos, "__PVT__", 7)) { pos += 7; continue; - } else if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && std::isxdigit(pos[3]) + } else if (0 == std::strncmp(pos, "__0", 3) && std::isxdigit(pos[3]) && std::isxdigit(pos[4])) { char value = 0; value += 16 @@ -567,6 +566,7 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) { AstNode* const oldp = this; UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?"); oldp->editCountInc(); + // cppcheck-suppress shadowFunction AstNode* const backp = oldp->m_backp; if (linkerp) { linkerp->m_oldp = oldp; @@ -629,6 +629,7 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) { AstNode* const oldp = this; UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?"); oldp->editCountInc(); + // cppcheck-suppress shadowFunction AstNode* const backp = oldp->m_backp; if (linkerp) { linkerp->m_oldp = oldp; @@ -710,6 +711,7 @@ void AstNode::relink(VNRelinker* linkerp) { cout << endl; } + // cppcheck-suppress shadowFunction AstNode* const backp = linkerp->m_backp; debugTreeChange(this, "-relinkNew: ", __LINE__, true); debugTreeChange(backp, "-relinkTre: ", __LINE__, true); @@ -773,6 +775,7 @@ void AstNode::addHereThisAsNext(AstNode* newp) { UASSERT_OBJ(this->m_backp, this, "'this' node has no back, already unlinked?"); UASSERT_OBJ(newp->m_headtailp, newp, "m_headtailp not set on new node"); // + // cppcheck-suppress shadowFunction AstNode* const backp = this->m_backp; AstNode* const newLastp = newp->m_headtailp; // @@ -1185,6 +1188,7 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE { break; case VNTypeInfo::OP_LIST: if (const AstNode* const headp = nodep) { + // cppcheck-suppress shadowFunction const AstNode* backp = this; const AstNode* tailp; const AstNode* opp = headp; @@ -1457,15 +1461,16 @@ string AstNode::instanceStr() const { // in case we have some circular reference bug. constexpr unsigned maxIterations = 10000; unsigned iterCount = 0; - for (const AstNode* backp = this; backp; backp = backp->backp(), ++iterCount) { + // Walk 'backp' chain + for (const AstNode* currp = this; currp; currp = currp->backp(), ++iterCount) { if (VL_UNCOVERABLE(iterCount >= maxIterations)) return ""; // LCOV_EXCL_LINE // Prefer the enclosing scope, if there is one. This is always under the enclosing module, // so just pick it up when encountered - if (const AstScope* const scopep = VN_CAST(backp, Scope)) { + if (const AstScope* const scopep = VN_CAST(currp, Scope)) { return scopep->isTop() ? "" : "... note: In instance " + scopep->prettyNameQ(); } // If scopes don't exist, report an example instance of the enclosing module - if (const AstModule* const modp = VN_CAST(backp, Module)) { + if (const AstModule* const modp = VN_CAST(currp, Module)) { const string instanceName = modp->someInstanceName(); return instanceName.empty() ? "" : "... note: In instance '" + instanceName + "'"; } @@ -1619,8 +1624,8 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { if (fromConstp->isNull()) return VCastable::COMPATIBLE; } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { - const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp(); - const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp(); + const AstClass* const toClassp = VN_AS(toDtp, ClassRefDType)->classp(); + const AstClass* const fromClassp = VN_AS(fromDtp, ClassRefDType)->classp(); const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp); const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp); if (upcast) { @@ -1664,7 +1669,7 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) { while (classDtypep1) { const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p); if (castable == VCastable::COMPATIBLE) return classDtypep1; - AstClassExtends* const extendsp = classDtypep1->classp()->extendsp(); + const AstClassExtends* const extendsp = classDtypep1->classp()->extendsp(); classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr; } return nullptr; diff --git a/src/V3Ast.h b/src/V3Ast.h index 32b1ceb99..1e5403b96 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -116,9 +116,8 @@ public: // Above include has: // enum en {...}; // const char* ascii() const {...}; - enum en m_e; - // cppcheck-suppress uninitVar // responsibility of each subclass - VNType() = default; + const enum en m_e; + VNType() = delete; // cppcheck-suppress noExplicitConstructor constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {} explicit VNType(int _e) @@ -2138,7 +2137,7 @@ protected: AstNode(VNType t, FileLine* fl); virtual AstNode* clone() = 0; // Generally, cloneTree is what you want instead virtual void cloneRelink() { cloneRelinkGen(); } - virtual void cloneRelinkGen() = 0; // Generated by 'astgen' + virtual void cloneRelinkGen(){}; // Overrides generated by 'astgen' void cloneRelinkTree(); // METHODS @@ -2662,20 +2661,24 @@ public: // For use via privateAs or the VN_DBG_AS macro only template static T* unsafePrivateAs(AstNode* nodep) VL_PURE { - static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); - static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); + static_assert(!uselessCast(), + "Unnecessary VN_DBG_AS, node known to have target type."); + static_assert(!impossibleCast(), "Unnecessary VN_DBG_AS, node cannot be this type."); return reinterpret_cast(nodep); } template static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE { - static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); - static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); + static_assert(!uselessCast(), + "Unnecessary VN_DBG_AS, node known to have target type."); + static_assert(!impossibleCast(), "Unnecessary VN_DBG_AS, node cannot be this type."); return reinterpret_cast(nodep); } // For use via the VN_AS macro only template static T* privateAs(AstNode* nodep) VL_PURE { + static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); + static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); UASSERT_OBJ(!nodep || privateTypeTest(nodep), nodep, "AstNode is not of expected type, but instead has type '" << nodep->typeName() << "'"); @@ -2683,6 +2686,8 @@ public: } template static const T* privateAs(const AstNode* nodep) VL_PURE { + static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); + static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); UASSERT_OBJ(!nodep || privateTypeTest(nodep), nodep, "AstNode is not of expected type, but instead has type '" << nodep->typeName() << "'"); diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 3ec6c1917..d6f3444d7 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -94,11 +94,11 @@ AstRange::AstRange(FileLine* fl, const VNumRange& range) rightp(new AstConst{fl, static_cast(range.right())}); } int AstRange::leftConst() const VL_MT_STABLE { - AstConst* const constp = VN_CAST(leftp(), Const); + const AstConst* const constp = VN_CAST(leftp(), Const); return (constp ? constp->toSInt() : 0); } int AstRange::rightConst() const VL_MT_STABLE { - AstConst* const constp = VN_CAST(rightp(), Const); + const AstConst* const constp = VN_CAST(rightp(), Const); return (constp ? constp->toSInt() : 0); } @@ -129,7 +129,7 @@ AstPackArrayDType::AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* } int AstQueueDType::boundConst() const VL_MT_STABLE { - AstConst* const constp = VN_CAST(boundp(), Const); + const AstConst* const constp = VN_CAST(boundp(), Const); return (constp ? constp->toSInt() : 0); } @@ -193,7 +193,7 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const { && (varp() && samep->varp() && varp()->name() == samep->varp()->name())); } } -bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { +bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const { if (varScopep()) { return (varScopep() == samep->varScopep()); } else { diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index f16fc7fa2..861a1254d 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -792,7 +792,7 @@ public: private: string m_name; // Name from upper typedef, if any - const int m_uniqueNum = 0; + const int m_uniqueNum; TableMap m_tableMap; // Created table for V3Width only to remove duplicates public: diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 71a97d67a..ce7241113 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -656,6 +656,7 @@ public: childDTypep(dtp); dtypeFrom(dtp); } + // cppcheck-suppress constParameterPointer AstCast(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) : ASTGEN_SUPER_Cast(fl) { this->fromp(fromp); @@ -791,14 +792,17 @@ class AstConsDynArray final : public AstNodeExpr { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // @astgen op1 := lhsp : Optional[AstNode] // @astgen op2 := rhsp : Optional[AstNode] - const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat - const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat + const bool m_lhsIsValue; // LHS constructs value inside the queue, not concat + const bool m_rhsIsValue; // RHS constructs value inside the queue, not concat public: explicit AstConsDynArray(FileLine* fl) - : ASTGEN_SUPER_ConsDynArray(fl) {} + : ASTGEN_SUPER_ConsDynArray(fl) + , m_lhsIsValue{false} + , m_rhsIsValue{false} {} explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp) : ASTGEN_SUPER_ConsDynArray(fl) - , m_lhsIsValue{lhsIsValue} { + , m_lhsIsValue{lhsIsValue} + , m_rhsIsValue{false} { this->lhsp(lhsp); } explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue, @@ -873,14 +877,17 @@ class AstConsQueue final : public AstNodeExpr { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // @astgen op1 := lhsp : Optional[AstNode] // @astgen op2 := rhsp : Optional[AstNode] - const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat - const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat + const bool m_lhsIsValue; // LHS constructs value inside the queue, not concat + const bool m_rhsIsValue; // RHS constructs value inside the queue, not concat public: explicit AstConsQueue(FileLine* fl) - : ASTGEN_SUPER_ConsQueue(fl) {} + : ASTGEN_SUPER_ConsQueue(fl) + , m_lhsIsValue{false} + , m_rhsIsValue{false} {} explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp) : ASTGEN_SUPER_ConsQueue(fl) - , m_lhsIsValue{lhsIsValue} { + , m_lhsIsValue{lhsIsValue} + , m_rhsIsValue{false} { this->lhsp(lhsp); } explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue, @@ -1116,6 +1123,8 @@ private: const int m_srcElementBits; // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue) public: + // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse, int blockSize, int dstElementBits, int srcElementBits) : ASTGEN_SUPER_CvtArrayToArray(fl) @@ -1141,6 +1150,8 @@ class AstCvtArrayToPacked final : public AstNodeExpr { // Cast from dynamic queue data type to packed array // @astgen op1 := fromp : AstNodeExpr public: + // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCvtArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) : ASTGEN_SUPER_CvtArrayToPacked(fl) { this->fromp(fromp); @@ -1155,6 +1166,8 @@ class AstCvtPackedToArray final : public AstNodeExpr { // Cast from packed array to dynamic/unpacked queue data type // @astgen op1 := fromp : AstNodeExpr public: + // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCvtPackedToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) : ASTGEN_SUPER_CvtPackedToArray(fl) { this->fromp(fromp); @@ -1169,6 +1182,8 @@ class AstCvtUnpackedToQueue final : public AstNodeExpr { // Cast from unpacked array to dynamic/unpacked queue data type // @astgen op1 := fromp : AstNodeExpr public: + // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCvtUnpackedToQueue(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) : ASTGEN_SUPER_CvtUnpackedToQueue(fl) { this->fromp(fromp); @@ -1604,7 +1619,6 @@ class AstLambdaArgRef final : public AstNodeExpr { // Lambda argument usage // These are not AstVarRefs because we need to be able to delete/clone lambdas during // optimizations and AstVar's are painful to remove. - ASTGEN_MEMBERS_AstLambdaArgRef; private: string m_name; // Name of variable @@ -1615,6 +1629,7 @@ public: : ASTGEN_SUPER_LambdaArgRef(fl) , m_name{name} , m_index{index} {} + ASTGEN_MEMBERS_AstLambdaArgRef; bool sameNode(const AstNode* /*samep*/) const override { return true; } string emitVerilog() override { return name(); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1812,7 +1827,7 @@ class AstRand final : public AstNodeExpr { // $random/$random(seed) or $urandom/$urandom(seed) // Return a random number, based upon width() // @astgen op1 := seedp : Optional[AstNode] - const bool m_urandom = false; // $urandom vs $random + const bool m_urandom; // $urandom vs $random public: class Reset {}; AstRand(FileLine* fl, AstNode* seedp, bool urandom) @@ -1994,7 +2009,7 @@ class AstScopeName final : public AstNodeExpr { // @astgen op1 := scopeAttrp : List[AstText] // @astgen op2 := scopeEntrp : List[AstText] bool m_dpiExport = false; // Is for dpiExport - const bool m_forFormat = false; // Is for a format %m + const bool m_forFormat; // Is for a format %m string scopeNameFormatter(AstText* scopeTextp) const; string scopePrettyNameFormatter(AstText* scopeTextp) const; @@ -4211,7 +4226,7 @@ public: // === AstNodeSel === class AstArraySel final : public AstNodeSel { - void init(AstNode* fromp) { + void init(const AstNode* fromp) { if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) { // Strip off array to find what array references dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep()); @@ -4251,7 +4266,7 @@ public: static AstNode* baseFromp(AstNode* nodep, bool overMembers); }; class AstAssocSel final : public AstNodeSel { - void init(AstNode* fromp) { + void init(const AstNode* fromp) { if (fromp && VN_IS(fromp->dtypep()->skipRefp(), AssocArrayDType)) { // Strip off array to find what array references dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), AssocArrayDType)->subDTypep()); @@ -4285,7 +4300,7 @@ public: int instrCount() const override { return widthInstrs(); } }; class AstWildcardSel final : public AstNodeSel { - void init(AstNode* fromp) { + void init(const AstNode* fromp) { if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) { // Strip off array to find what array references dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep()); @@ -4976,6 +4991,8 @@ public: dtypeSetLogicUnsized(setwidth, minwidth, VSigning::UNSIGNED); } } + // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCCast(FileLine* fl, AstNodeExpr* lhsp, AstNode* typeFromp) : ASTGEN_SUPER_CCast(fl, lhsp) { dtypeFrom(typeFromp); @@ -5768,7 +5785,7 @@ public: const char* broken() const override; bool sameNode(const AstNode* samep) const override; inline bool sameNode(const AstVarRef* samep) const; - inline bool sameNoLvalue(AstVarRef* samep) const; + inline bool sameNoLvalue(const AstVarRef* samep) const; int instrCount() const override; string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index bf18a7049..aaaf9698f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -784,7 +784,7 @@ class AstClassExtends final : public AstNode { // @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op2 := classOrPkgsp : Optional[AstNode] // @astgen op3 := argsp : List[AstNodeExpr] - const bool m_isImplements = false; // class implements + const bool m_isImplements; // class implements bool m_parameterized = false; // has parameters in its statement public: @@ -812,8 +812,8 @@ class AstClocking final : public AstNode { // @astgen op2 := itemsp : List[AstClockingItem] // @astgen op3 := eventp : Optional[AstVar] std::string m_name; // Clocking block name - const bool m_isDefault = false; // True if default clocking - const bool m_isGlobal = false; // True if global clocking + const bool m_isDefault; // True if default clocking + const bool m_isGlobal; // True if global clocking public: AstClocking(FileLine* fl, const std::string& name, AstSenItem* sensesp, @@ -1531,7 +1531,7 @@ public: // Create new MODULETEMP variable under this scope AstVarScope* createTemp(const string& name, unsigned width); AstVarScope* createTemp(const string& name, AstNodeDType* dtypep); - AstVarScope* createTempLike(const string& name, AstVarScope* vscp); + AstVarScope* createTempLike(const string& name, const AstVarScope* vscp); }; class AstSenItem final : public AstNode { // Parents: SENTREE @@ -1961,7 +1961,7 @@ public: combineType(type); dtypeSetBitSized(wantwidth, VSigning::UNSIGNED); } - AstVar(FileLine* fl, VVarType type, const string& name, AstVar* examplep) + AstVar(FileLine* fl, VVarType type, const string& name, const AstVar* examplep) : ASTGEN_SUPER_Var(fl) , m_name{name} , m_origName{name} { @@ -2133,7 +2133,7 @@ public: bool isGParam() const { return varType() == VVarType::GPARAM; } bool isGenVar() const { return varType() == VVarType::GENVAR; } bool isBitLogic() const { - AstBasicDType* bdtypep = basicp(); + const AstBasicDType* const bdtypep = basicp(); return bdtypep && bdtypep->isBitLogic(); } bool isUsedClock() const VL_MT_SAFE { return m_usedClock; } @@ -2486,7 +2486,7 @@ public: && std::is_base_of::value, "T_Callable 'f' must have a signature compatible with 'void(AstClass*, T_Node*)', " "with 'T_Node' being a subtype of 'AstNode'"); - if (AstClassExtends* const cextendsp = this->extendsp()) { + if (const AstClassExtends* const cextendsp = this->extendsp()) { cextendsp->classp()->foreachMember(f); } for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) { @@ -2502,7 +2502,7 @@ public: && std::is_base_of::value, "Predicate 'p' must have a signature compatible with 'bool(const AstClass*, " "const T_Node*)', with 'T_Node' being a subtype of 'AstNode'"); - if (AstClassExtends* const cextendsp = this->extendsp()) { + if (const AstClassExtends* const cextendsp = this->extendsp()) { if (cextendsp->classp()->existsMember(p)) return true; } for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) { @@ -2540,18 +2540,22 @@ public: }; class AstModule final : public AstNodeModule { // A module declaration - const bool m_isChecker = false; // Module represents a checker - const bool m_isProgram = false; // Module represents a program + const bool m_isChecker; // Module represents a checker + const bool m_isProgram; // Module represents a program public: class Checker {}; // for constructor type-overload selection class Program {}; // for constructor type-overload selection AstModule(FileLine* fl, const string& name, const string& libname) - : ASTGEN_SUPER_Module(fl, name, libname) {} + : ASTGEN_SUPER_Module(fl, name, libname) + , m_isChecker{false} + , m_isProgram{false} {} AstModule(FileLine* fl, const string& name, const string& libname, Checker) : ASTGEN_SUPER_Module(fl, name, libname) - , m_isChecker{true} {} + , m_isChecker{true} + , m_isProgram{false} {} AstModule(FileLine* fl, const string& name, const string& libname, Program) : ASTGEN_SUPER_Module(fl, name, libname) + , m_isChecker{false} , m_isProgram{true} {} ASTGEN_MEMBERS_AstModule; string verilogKwd() const override { diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 7d47b230c..447441684 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -845,6 +845,7 @@ public: this->exprp(exprp); } ASTGEN_MEMBERS_AstStmtExpr; + // cppcheck-suppress uselessOverride bool isPure() override { return exprp()->isPure(); } }; class AstStop final : public AstNodeStmt { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 60a87ef90..2cea4e39b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -542,18 +542,16 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& string ostatic; if (isStatic() && namespc.empty()) ostatic = "static "; - const bool isRef = isDpiOpenArray() - || (forFunc && (isWritable() || this->isRef() || this->isConstRef())) - || asRef; + asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef())); - if (forFunc && isReadOnly() && isRef) ostatic = ostatic + "const "; + if (forFunc && isReadOnly() && asRef) ostatic = ostatic + "const "; string oname; if (named) { if (!namespc.empty()) oname += namespc + "::"; oname += VIdProtect::protectIf(name(), protect()); } - return ostatic + dtypep()->cType(oname, forFunc, isRef); + return ostatic + dtypep()->cType(oname, forFunc, asRef); } string AstVar::vlEnumType() const { @@ -687,7 +685,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const { if (forReturn) named = false; string arg; if (isWide() && isReadOnly()) arg += "const "; - const bool isRef = !forReturn && (isWritable() || this->isRef() || this->isConstRef()); + const bool asRef = !forReturn && (isWritable() || this->isRef() || this->isConstRef()); if (VN_IS(dtypeSkipRefp(), BasicDType) && !dtypeSkipRefp()->isDouble() && !dtypeSkipRefp()->isString()) { // Backward compatible type declaration @@ -708,12 +706,12 @@ string AstVar::cPubArgType(bool named, bool forReturn) const { arg += " (& " + name(); arg += ")[" + cvtToStr(widthWords()) + "]"; } else { - if (isRef) arg += "&"; + if (asRef) arg += "&"; if (named) arg += " " + name(); } } else { // Newer internal-compatible types - arg += dtypep()->cType((named ? name() : std::string{}), true, isRef); + arg += dtypep()->cType((named ? name() : std::string{}), true, asRef); } return arg; } @@ -775,7 +773,7 @@ string AstVar::dpiTmpVarType(const string& varName) const { class converter final : public dpiTypesToStringConverter { const string m_name; string arraySuffix(const AstVar* varp, size_t n) const { - if (AstUnpackArrayDType* const unpackp + if (const AstUnpackArrayDType* const unpackp = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType)) { // Convert multi dimensional unpacked array to 1D array if (n == 0) n = 1; @@ -849,13 +847,13 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) { } else { return nullptr; } - } else if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { + } else if (const AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { if (vrefp->varp()->isSc()) { return vrefp->varp(); } else { return nullptr; } - } else if (AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) { + } else if (const AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) { if (AstVar* const p = scVarRecurse(arraySelp->fromp())) return p; } return nullptr; @@ -1194,7 +1192,7 @@ AstVarScope* AstScope::createTemp(const string& name, AstNodeDType* dtypep) { return vscp; } -AstVarScope* AstScope::createTempLike(const string& name, AstVarScope* vscp) { +AstVarScope* AstScope::createTempLike(const string& name, const AstVarScope* vscp) { return createTemp(name, vscp->dtypep()); } @@ -1568,7 +1566,7 @@ void AstNode::dump(std::ostream& str) const { } else { str << " @dt=" << nodeAddr(dtypep()) << "@"; } - if (AstNodeDType* const dtp = dtypep()) dtp->dumpSmall(str); + if (const AstNodeDType* const dtp = dtypep()) dtp->dumpSmall(str); } else { // V3Broken will throw an error if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep()); } @@ -2217,10 +2215,10 @@ void AstRefDType::dump(std::ostream& str) const { if (!s_recursing) { // Prevent infinite dump if circular typedefs s_recursing = true; str << " -> "; - if (const auto subp = subDTypep()) { + if (const AstNodeDType* const subp = subDTypep()) { if (typedefp()) str << "typedef=" << static_cast(typedefp()) << " -> "; subp->dump(str); - } else if (const auto subp = typedefp()) { + } else if (const AstTypedef* const subp = typedefp()) { subp->dump(str); } s_recursing = false; @@ -2264,7 +2262,7 @@ string AstNodeUOrStructDType::prettyDTypeName(bool full) const { void AstNodeDType::dump(std::ostream& str) const { this->AstNode::dump(str); if (generic()) str << " [GENERIC]"; - if (AstNodeDType* const dtp = virtRefDTypep()) { + if (const AstNodeDType* const dtp = virtRefDTypep()) { str << " refdt=" << nodeAddr(dtp); dtp->dumpSmall(str); } @@ -2476,7 +2474,7 @@ void AstMTaskBody::dumpJson(std::ostream& str) const { void AstTypeTable::dump(std::ostream& str) const { this->AstNode::dump(str); for (int i = 0; i < static_cast(VBasicDTypeKwd::_ENUM_MAX); ++i) { - if (AstBasicDType* const subnodep = m_basicps[i]) { + if (const AstBasicDType* const subnodep = m_basicps[i]) { str << '\n'; // Newline from caller, so newline first str << "\t\t" << std::setw(8) << VBasicDTypeKwd{i}.ascii(); str << " -> "; @@ -2486,7 +2484,7 @@ void AstTypeTable::dump(std::ostream& str) const { { const DetailedMap& mapr = m_detailedMap; for (const auto& itr : mapr) { - AstBasicDType* const dtypep = itr.second; + const AstBasicDType* const dtypep = itr.second; str << '\n'; // Newline from caller, so newline first str << "\t\tdetailed -> "; dtypep->dump(str); @@ -2591,15 +2589,21 @@ void AstNodeVarRef::dumpJson(std::ostream& str) const { AstNodeVarRef* AstNodeVarRef::varRefLValueRecurse(AstNode* nodep) { // Given a (possible) lvalue expression, recurse to find the being-set NodeVarRef, else nullptr if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) return anodep; - if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) + if (const AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) { return varRefLValueRecurse(anodep->fromp()); - if (AstSel* const anodep = VN_CAST(nodep, Sel)) return varRefLValueRecurse(anodep->fromp()); - if (AstArraySel* const anodep = VN_CAST(nodep, ArraySel)) + } + if (const AstSel* const anodep = VN_CAST(nodep, Sel)) { return varRefLValueRecurse(anodep->fromp()); - if (AstMemberSel* const anodep = VN_CAST(nodep, MemberSel)) + } + if (const AstArraySel* const anodep = VN_CAST(nodep, ArraySel)) { return varRefLValueRecurse(anodep->fromp()); - if (AstStructSel* const anodep = VN_CAST(nodep, StructSel)) + } + if (const AstMemberSel* const anodep = VN_CAST(nodep, MemberSel)) { return varRefLValueRecurse(anodep->fromp()); + } + if (const AstStructSel* const anodep = VN_CAST(nodep, StructSel)) { + return varRefLValueRecurse(anodep->fromp()); + } return nullptr; } @@ -2796,9 +2800,12 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const { if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) { foundp = anodep->skipRefOrNullp(); } - if (AstTypedef* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep(); - if (AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) + if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) { + foundp = anodep->subDTypep(); + } + if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) { foundp = anodep->classp(); + } } return VN_CAST(foundp, NodeModule); } @@ -3076,7 +3083,7 @@ void AstCAwait::dump(std::ostream& str) const { } void AstCAwait::dumpJson(std::ostream& str) const { dumpJsonGen(str); } int AstCMethodHard::instrCount() const { - if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) { + if (const AstBasicDType* const basicp = fromp()->dtypep()->basicp()) { // TODO: add a more structured description of library methods, rather than using string // matching. See issue #3715. if (basicp->isTriggerVec() && m_name == "word") { @@ -3165,8 +3172,8 @@ void AstCMethodHard::setPurity() { if (name() == "atWriteAppend" || name() == "atWriteAppendBack") { m_pure = false; // Treat atWriteAppend as pure if the argument is a loop iterator - if (AstNodeExpr* const argp = pinsp()) { - if (AstVarRef* const varrefp = VN_CAST(argp, VarRef)) { + if (const AstNodeExpr* const argp = pinsp()) { + if (const AstVarRef* const varrefp = VN_CAST(argp, VarRef)) { if (varrefp->varp()->isUsedLoopIdx()) m_pure = true; } } diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index cd6cd7fe9..d90ac3aa0 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -455,7 +455,7 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) { lastp->unlinkFrBack(&handle); if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) { loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); - } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { + } else if (const AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { if (adtypep->isString()) { AstConst* const leftp = new AstConst{fl, 0}; AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)}; diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index 72203bd8e..f4defb43c 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -51,7 +51,7 @@ class BranchVisitor final : public VNVisitorConst { m_likely = false; m_unlikely = false; } - void checkUnlikely(AstNode* nodep) { + void checkUnlikely(const AstNode* nodep) { if (nodep->isUnlikely()) { UINFO(4, " UNLIKELY: " << nodep); m_unlikely++; diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index e51507c39..19687d603 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -43,7 +43,7 @@ class CUseVisitor final : public VNVisitorConst { std::map> m_didUse; // What we already used // METHODS - void addNewUse(AstNode* nodep, VUseType useType, const string& name) { + void addNewUse(const AstNode* nodep, VUseType useType, const string& name) { auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType)); if (e.second || ((e.first->second.second & useType) != useType)) { e.first->second.second = e.first->second.second | useType; @@ -73,7 +73,8 @@ class CUseVisitor final : public VNVisitorConst { if (stypep && stypep->classOrPackagep()) { addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name()); iterateChildrenConst(stypep); - } else if (AstClassRefDType* const classp = VN_CAST(nodep->skipRefp(), ClassRefDType)) { + } else if (const AstClassRefDType* const classp + = VN_CAST(nodep->skipRefp(), ClassRefDType)) { addNewUse(nodep, VUseType::INT_FWD_CLASS, classp->name()); } } @@ -95,7 +96,7 @@ public: : m_modp{modp} { iterateConst(modp); - for (auto& used : m_didUse) { + for (const auto& used : m_didUse) { AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first}; m_modp->addStmtsp(newp); UINFO(8, "Insert " << newp); diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 1c13b744a..7af727731 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -216,7 +216,7 @@ class CaseVisitor final : public VNVisitor { const uint32_t val = numval.toUInt(); uint32_t firstOverlap = 0; - AstNode* overlappedCondp = nullptr; + const AstNode* overlappedCondp = nullptr; bool foundHit = false; for (uint32_t i = 0; i < numCases; ++i) { if ((i & mask) == val) { @@ -307,7 +307,7 @@ class CaseVisitor final : public VNVisitor { // Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch for (uint32_t i = 0; i < numCases; ++i) { if (AstNode* const condp = m_valueItem[i]) { - AstCaseItem* caseItemp = caseItemMap[condp]; + const AstCaseItem* const caseItemp = caseItemMap[condp]; UASSERT(caseItemp, "caseItemp should exist"); m_valueItem[i] = caseItemp->stmtsp(); } @@ -325,7 +325,7 @@ class CaseVisitor final : public VNVisitor { } else { // Make left and right subtrees // cexpr[msb:lsb] == 1 - AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb - 1, upperValue | 0); + AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb - 1, upperValue); AstNode* tree1p = replaceCaseFastRecurse( cexprp, msb - 1, upperValue | (1UL << static_cast(msb))); @@ -548,7 +548,7 @@ class CaseVisitor final : public VNVisitor { } } - bool neverItem(AstCase* casep, AstConst* itemp) { + bool neverItem(const AstCase* casep, const AstConst* itemp) { // Xs in case or casez are impossible due to two state simulations if (casep->casex()) { } else if (casep->casez() || casep->caseInside()) { diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 24e20d3e1..4839b8d04 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -69,7 +69,7 @@ class CastVisitor final : public VNVisitor { ensureLower32Cast(castp); nodep->user1(1); // Now must be of known size } - static int castSize(AstNode* nodep) { + static int castSize(const AstNode* nodep) { if (nodep->isQuad()) { return VL_QUADSIZE; } else if (nodep->width() <= 8) { diff --git a/src/V3CfgLiveVariables.cpp b/src/V3CfgLiveVariables.cpp index 790ee7fee..b0db3aa76 100644 --- a/src/V3CfgLiveVariables.cpp +++ b/src/V3CfgLiveVariables.cpp @@ -83,7 +83,7 @@ class CfgLiveVariables final : VNVisitorConst { // Check and return if variable is incompatible bool incompatible(Variable* varp) { if (!isSupportedPackedDType(varp->dtypep())) return true; - AstVar* astVarp = nullptr; + const AstVar* astVarp = nullptr; // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works if VL_CONSTEXPR_CXX17 (T_Scoped) { astVarp = reinterpret_cast(varp)->varp(); diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 6cd5d9b1e..fbf384d6a 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -257,7 +257,7 @@ public: modp->addStmtsp(nodep); } // BFS to mark public typedefs. - std::set pubStrDtypeps; + std::set pubStrDtypeps; while (!m_pubStrDtypeps.empty()) { AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front(); m_pubStrDtypeps.pop(); @@ -270,7 +270,8 @@ public: } } for (AstTypedef* typedefp : m_typedefps) { - AstNodeUOrStructDType* const sdtypep = VN_AS(typedefp->dtypep(), NodeUOrStructDType); + const AstNodeUOrStructDType* const sdtypep + = VN_AS(typedefp->dtypep(), NodeUOrStructDType); if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true); } // Clear package pointer of non-public packed struct / union type, which will never be diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 0fe863e4d..ed8134b9a 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -51,7 +51,7 @@ class CleanVisitor final : public VNVisitor { // METHODS // Width resetting - int cppWidth(AstNode* nodep) { + int cppWidth(const AstNode* nodep) { if (nodep->width() <= VL_IDATASIZE) { return VL_IDATASIZE; } else if (nodep->width() <= VL_QUADSIZE) { @@ -101,7 +101,9 @@ class CleanVisitor final : public VNVisitor { // Store the clean state in the userp on each node void setCleanState(AstNode* nodep, CleanState clean) { nodep->user1(clean); } - CleanState getCleanState(AstNode* nodep) { return static_cast(nodep->user1()); } + CleanState getCleanState(const AstNode* nodep) { + return static_cast(nodep->user1()); + } bool isClean(AstNode* nodep) { const CleanState clstate = getCleanState(nodep); if (clstate == CS_CLEAN) return true; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 0846a3126..b2afb50c5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -170,8 +170,8 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { struct BitPolarityEntry final { // Found bit polarity during iterate() LeafInfo m_info; - bool m_polarity; - int m_bit; + bool m_polarity = false; + int m_bit = 0; BitPolarityEntry(const LeafInfo& info, bool pol, int bit) : m_info{info} , m_polarity{pol} @@ -392,7 +392,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { #define CONST_BITOP_SET_FAILED(reason, nodep) setFailed(true, reason, nodep, __LINE__) - bool setFailed(bool fail, const char* reason, AstNode* nodep, int line) { + bool setFailed(bool fail, const char* reason, const AstNode* nodep, int line) { if (fail && !m_failed) { UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:" << line << " when checking:" << nodep); @@ -460,7 +460,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { } void visit(AstShiftR* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); - AstConst* const constp = VN_CAST(nodep->rhsp(), Const); + const AstConst* const constp = VN_CAST(nodep->rhsp(), Const); CONST_BITOP_RETURN_IF(!constp, nodep->rhsp()); m_lsb += constp->toUInt(); incrOps(nodep, __LINE__); @@ -484,7 +484,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { } void visit(AstWordSel* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); - AstConst* const constp = VN_CAST(nodep->bitp(), Const); + const AstConst* const constp = VN_CAST(nodep->bitp(), Const); CONST_BITOP_RETURN_IF(!constp, nodep->bitp()); UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel"); m_leafp->wordIdx(constp->toSInt()); @@ -796,7 +796,7 @@ public: if (debug() >= 9) { // LCOV_EXCL_START cout << "- Bitop tree considered:\n"; - for (AstNodeExpr* const termp : termps) termp->dumpTree("- Reduced term: "); + for (const AstNodeExpr* const termp : termps) termp->dumpTree("- Reduced term: "); for (const std::pair& termp : visitor.m_frozenNodes) { termp.first->dumpTree("- Frozen term with lsb " + std::to_string(termp.second.m_lsb) + " polarity " @@ -916,7 +916,6 @@ class ConstVisitor final : public VNVisitor { // AstEnum::user4 -> bool. Recursing. // STATE - static constexpr bool m_doShort = true; // Remove expressions that short circuit bool m_params = false; // If true, propagate parameterized and true numbers only bool m_required = false; // If true, must become a constant bool m_wremove = true; // Inside scope, no assignw removal @@ -949,12 +948,12 @@ class ConstVisitor final : public VNVisitor { const V3Number& numc = VN_AS(nodep, Const)->num(); return !numc.isNumber() ? numc : V3Number{nodep, nodep->widthMinV(), numc}; } - V3Number toNumC(AstNode* nodep, V3Number& numv) { + V3Number toNumC(AstNode* nodep, const 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 operandConst(const AstNode* nodep) { return VN_IS(nodep, Const); } bool operandAsvConst(const AstNode* nodep) { // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) const AstNodeBiComAsv* const bnodep = VN_CAST(nodep, NodeBiComAsv); @@ -1022,8 +1021,9 @@ class ConstVisitor final : public VNVisitor { UASSERT_OBJ(constp && constp->isOne(), andp->lhsp(), "TRREEOPC must meet this condition"); AstNodeExpr* const rhsp = andp->rhsp(); AstCCast* ccastp = nullptr; - const auto isEqOrNeq - = [](AstNode* nodep) -> bool { return VN_IS(nodep, Eq) || VN_IS(nodep, Neq); }; + const auto isEqOrNeq = [](const AstNode* nodep) -> bool { // + return VN_IS(nodep, Eq) || VN_IS(nodep, Neq); + }; if (isEqOrNeq(rhsp)) { ccastp = new AstCCast{andp->fileline(), rhsp->unlinkFrBack(), andp}; } else if (AstCCast* const tmpp = VN_CAST(rhsp, CCast)) { @@ -1352,8 +1352,8 @@ class ConstVisitor final : public VNVisitor { return false; } AstNodeExpr* const ap = shiftp->lhsp(); - AstConst* const bp = VN_AS(shiftp->rhsp(), Const); - AstConst* const lp = VN_AS(nodep->lsbp(), Const); + const AstConst* const bp = VN_AS(shiftp->rhsp(), Const); + const AstConst* const lp = VN_AS(nodep->lsbp(), Const); if (bp->isWide() || bp->num().isFourState() || bp->num().isNegative() || lp->isWide() || lp->num().isFourState() || lp->num().isNegative()) { return false; @@ -1402,7 +1402,7 @@ class ConstVisitor final : public VNVisitor { // Avoids compiler warning const AstExtend* const extendp = VN_CAST(nodep->rhsp(), Extend); if (!extendp) return false; - AstNode* const smallerp = extendp->lhsp(); + const AstNode* const smallerp = extendp->lhsp(); const int subsize = smallerp->width(); const AstConst* const constp = VN_CAST(nodep->lhsp(), Const); if (!constp) return false; @@ -1459,7 +1459,7 @@ class ConstVisitor final : public VNVisitor { return false; // Not a transform, so NOP } - static bool operandsSame(AstNode* node1p, AstNode* node2p) { + static bool operandsSame(const AstNode* node1p, const AstNode* node2p) { // For now we just detect constants & simple vars, though it could be more generic if (VN_IS(node1p, Const) && VN_IS(node2p, Const)) return node1p->sameGateTree(node2p); if (VN_IS(node1p, VarRef) && VN_IS(node2p, VarRef)) { @@ -1469,8 +1469,8 @@ class ConstVisitor final : public VNVisitor { return node1p->isSame(node2p); } // Pattern created by coverage-line; avoid compiler tautological-compare warning - if (AstAnd* const and1p = VN_CAST(node1p, And)) { - if (AstAnd* const and2p = VN_CAST(node2p, And)) { + if (const AstAnd* const and1p = VN_CAST(node1p, And)) { + if (const AstAnd* const and2p = VN_CAST(node2p, And)) { if (VN_IS(and1p->lhsp(), Const) && VN_IS(and1p->rhsp(), NodeVarRef) && VN_IS(and2p->lhsp(), Const) && VN_IS(and2p->rhsp(), NodeVarRef)) return node1p->sameGateTree(node2p); @@ -1535,11 +1535,11 @@ class ConstVisitor final : public VNVisitor { if (!lselp || !rselp) return false; // a[a:b] a[b-1:c] are adjacent - AstNode* const lfromp = lselp->fromp(); - AstNode* const rfromp = rselp->fromp(); + const AstNode* const lfromp = lselp->fromp(); + const AstNode* const rfromp = rselp->fromp(); if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false; - AstConst* const lstart = VN_CAST(lselp->lsbp(), Const); - AstConst* const rstart = VN_CAST(rselp->lsbp(), Const); + const AstConst* const lstart = VN_CAST(lselp->lsbp(), Const); + const AstConst* const rstart = VN_CAST(rselp->lsbp(), Const); if (!lstart || !rstart) return false; // too complicated const int rend = (rstart->toSInt() + rselp->widthConst()); // a[i:j] a[j-1:k] @@ -2209,11 +2209,11 @@ class ConstVisitor final : public VNVisitor { if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { srcElementBits = elemDtp->width(); } int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { dstElementBits = elemDtp->width(); } srcp = new AstCvtArrayToArray{ @@ -2243,7 +2243,7 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack(); AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp(); AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack(); - AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); const int sWidth = srcp->width(); const int dWidth = dstp->width(); // Connect the rhs to the stream operator and update its width @@ -2306,7 +2306,7 @@ class ConstVisitor final : public VNVisitor { } else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) { AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL); AstNodeExpr* srcp = streamp->lhsp(); - AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp(); if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) || VN_IS(srcDTypep, UnpackArrayDType))) { @@ -2330,11 +2330,11 @@ class ConstVisitor final : public VNVisitor { // streamp->rhsp()->prettyTypeName() << ")"); // } int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { srcElementBits = elemDtp->width(); } int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { dstElementBits = elemDtp->width(); } streamp->unlinkFrBack(); @@ -2500,7 +2500,7 @@ class ConstVisitor final : public VNVisitor { bool matchConcatRand(AstConcat* nodep) { // CONCAT(RAND, RAND) - created by Chisel code AstRand* const aRandp = VN_CAST(nodep->lhsp(), Rand); - AstRand* const bRandp = VN_CAST(nodep->rhsp(), Rand); + const AstRand* const bRandp = VN_CAST(nodep->rhsp(), Rand); if (!aRandp || !bRandp) return false; if (!aRandp->combinable(bRandp)) return false; UINFO(4, "Concat(Rand,Rand) => Rand: " << nodep); @@ -2518,15 +2518,15 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } - int operandConcatMove(AstConcat* nodep) { + int operandConcatMove(const AstConcat* nodep) { // CONCAT under concat (See moveConcat) // Return value: true indicates to do it; 2 means move to LHS const AstConcat* const abConcp = VN_CAST(nodep->lhsp(), Concat); const AstConcat* const bcConcp = VN_CAST(nodep->rhsp(), Concat); if (!abConcp && !bcConcp) return 0; if (bcConcp) { - AstNodeExpr* const ap = nodep->lhsp(); - AstNodeExpr* const bp = bcConcp->lhsp(); + const AstNodeExpr* const ap = nodep->lhsp(); + const AstNodeExpr* const bp = bcConcp->lhsp(); // If a+b == 32,64,96 etc, then we want to have a+b together on LHS if (VL_BITBIT_I(ap->width() + bp->width()) == 0) return 2; // Transform 2: to abConc } else { // abConcp @@ -2617,7 +2617,7 @@ class ConstVisitor final : public VNVisitor { // potentially smaller lsb1p's width, but don't insert a redundant AstExtend. // Note that due to some sloppiness in earlier passes, lsb1p might actually be wider, // so extend to the wider type. - AstNodeExpr* const widep = lsb1p->width() > lsb2p->width() ? lsb1p : lsb2p; + const AstNodeExpr* const widep = lsb1p->width() > lsb2p->width() ? lsb1p : lsb2p; AstNodeExpr* const lhsp = widep->width() > lsb2p->width() ? new AstExtend{lsb2p->fileline(), lsb2p} : lsb2p; @@ -2895,7 +2895,7 @@ class ConstVisitor final : public VNVisitor { // Not constant propagated (for today) because AstNodeExpr::isOpaque is set // Someday if lower is constant, convert to quoted "string". - bool onlySenItemInSenTree(AstSenItem* nodep) { + bool onlySenItemInSenTree(const AstSenItem* nodep) { // Only one if it's not in a list return (!nodep->nextp() && nodep->backp()->nextp() != nodep); } @@ -2981,8 +2981,8 @@ class ConstVisitor final : public VNVisitor { if (aCallp->name() < bCallp->name()) return -1; if (aCallp->name() > bCallp->name()) return 1; if (const int c = cmp(aCallp->fromp(), bCallp->fromp())) return c; - AstNodeExpr* aPinsp = aCallp->pinsp(); - AstNodeExpr* bPinsp = bCallp->pinsp(); + const AstNodeExpr* aPinsp = aCallp->pinsp(); + const AstNodeExpr* bPinsp = bCallp->pinsp(); while (aPinsp && bPinsp) { if (const int c = cmp(aPinsp, bPinsp)) return c; aPinsp = VN_AS(aPinsp->nextp(), NodeExpr); @@ -2996,8 +2996,8 @@ class ConstVisitor final : public VNVisitor { public: bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const { - AstNodeExpr* const lSensp = lhsp->sensp(); - AstNodeExpr* const rSensp = rhsp->sensp(); + const AstNodeExpr* const lSensp = lhsp->sensp(); + const AstNodeExpr* const rSensp = rhsp->sensp(); if (lSensp && rSensp) { // If both terms have sensitivity expressions, recursively compare them if (const int c = cmp(lSensp, rSensp)) return c < 0; @@ -3163,11 +3163,11 @@ class ConstVisitor final : public VNVisitor { } if (streamp) { AstNodeExpr* srcp = streamp->lhsp(); - AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp(); if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) { int blockSize = 1; - if (AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { + if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { blockSize = constp->toSInt(); if (VL_UNLIKELY(blockSize <= 0)) { // Not reachable due to higher level checks when parsing stream @@ -3184,11 +3184,11 @@ class ConstVisitor final : public VNVisitor { // streamp->rhsp()->prettyTypeName() << ")"); // } int srcElementBits = 0; - if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { srcElementBits = elemDtp->width(); } int dstElementBits = 0; - if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { dstElementBits = elemDtp->width(); } streamp->unlinkFrBack(); @@ -3330,9 +3330,7 @@ class ConstVisitor final : public VNVisitor { if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp()) return false; AstSFormatF* const pformatp = prevp->fmtp(); - if (!pformatp) return false; AstSFormatF* const nformatp = nodep->fmtp(); - if (!nformatp) return false; // We don't merge scopeNames as can have only one and might be different scopes (late in // process) Also rare for real code to print %m multiple times in same message if (nformatp->scopeNamep() && pformatp->scopeNamep()) return false; diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 36402417b..9e745a1fb 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -441,7 +441,7 @@ using V3ControlFileResolver = V3ControlWildcardResolver; class V3ControlScopeTraceEntry final { public: const string m_scope; // Scope or regexp to match - const bool m_on = false; // True to enable message + const bool m_on; // True to enable message int m_levels = 0; // # levels, 0 = all, 1 = only this, ... // CONSTRUCTORS V3ControlScopeTraceEntry(const string& scope, bool on, int levels) @@ -757,7 +757,8 @@ void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) { if (ftp) ftp->apply(ftaskp); } -void V3Control::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp) { +void V3Control::applyVarAttr(const AstNodeModule* modulep, const AstNodeFTask* ftaskp, + AstVar* varp) { V3ControlVar* vp; V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modulep->prettyOrigOrName()); @@ -797,7 +798,7 @@ bool V3Control::containsMTaskProfileData() { return V3ControlResolver::s().containsMTaskProfileData(); } -bool V3Control::waive(FileLine* filelinep, V3ErrorCode code, const string& message) { +bool V3Control::waive(const FileLine* filelinep, V3ErrorCode code, const string& message) { V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filelinep->filename()); if (!filep) return false; return filep->waive(code, message); diff --git a/src/V3Control.h b/src/V3Control.h index ff26e5ab6..3b3142158 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -51,7 +51,8 @@ public: static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp); static void applyIgnores(FileLine* filelinep); static void applyModule(AstNodeModule* modulep); - static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp); + static void applyVarAttr(const AstNodeModule* modulep, const AstNodeFTask* ftaskp, + AstVar* varp); static int getHierWorkers(const string& model); static FileLine* getHierWorkersFileLine(const string& model); @@ -64,7 +65,7 @@ public: static bool containsMTaskProfileData(); - static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message); + static bool waive(const FileLine* filelinep, V3ErrorCode code, const string& message); }; #endif // Guard diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 2c142ac0a..718a0a0d2 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -35,30 +35,30 @@ VL_DEFINE_DEBUG_FUNCTIONS; -class ExprCoverageEligibleVisitor final : public VNVisitor { +class ExprCoverageEligibleVisitor final : public VNVisitorConst { // STATE bool m_eligible = true; static bool elemDTypeEligible(const AstNodeDType* dtypep) { dtypep = dtypep->skipRefp(); - if (AstNodeDType* const dtp = dtypep->virtRefDTypep()) { + if (const AstNodeDType* const dtp = dtypep->virtRefDTypep()) { if (!elemDTypeEligible(dtp)) return false; } - if (AstNodeDType* const dtp = dtypep->virtRefDType2p()) { + if (const AstNodeDType* const dtp = dtypep->virtRefDType2p()) { if (!elemDTypeEligible(dtp)) return false; } return !VN_IS(dtypep, ClassRefDType); } void visit(AstNodeVarRef* nodep) override { - AstNodeDType* dtypep = nodep->varp()->dtypep(); + const AstNodeDType* const dtypep = nodep->varp()->dtypep(); // Class objects and references not supported for expression coverage // because the object may not persist until the point at which // coverage data is gathered // This could be resolved in the future by protecting against dereferrencing // null pointers when cloning the expression for expression coverage if (dtypep && elemDTypeEligible(dtypep)) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { m_eligible = false; } @@ -68,13 +68,13 @@ class ExprCoverageEligibleVisitor final : public VNVisitor { if (!nodep->isExprCoverageEligible()) { m_eligible = false; } else { - iterateChildren(nodep); + iterateChildrenConst(nodep); } } public: // CONSTRUCTORS - explicit ExprCoverageEligibleVisitor(AstNode* nodep) { iterateChildren(nodep); } + explicit ExprCoverageEligibleVisitor(AstNode* nodep) { iterateChildrenConst(nodep); } ~ExprCoverageEligibleVisitor() override = default; bool eligible() { return m_eligible; } @@ -162,7 +162,7 @@ class CoverageVisitor final : public VNVisitor { // METHODS - const char* varIgnoreToggle(AstVar* nodep) { + const char* varIgnoreToggle(const AstVar* nodep) { // Return true if this shouldn't be traced // See also similar rule in V3TraceDecl::varIgnoreTrace if (!nodep->isToggleCoverable()) return "Not relevant signal type"; @@ -203,7 +203,7 @@ class CoverageVisitor final : public VNVisitor { } return incp; } - string traceNameForLine(AstNode* nodep, const string& type) { + string traceNameForLine(const AstNode* nodep, const string& type) { string name = "vlCoverageLineTrace_" + nodep->fileline()->filebasenameNoExt() + "__" + cvtToStr(nodep->fileline()->lineno()) + "_" + type; if (const uint32_t suffix = m_varnames[name]++) name += "_" + cvtToStr(suffix); @@ -438,7 +438,7 @@ class CoverageVisitor final : public VNVisitor { if (adtypep->packed()) { for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp(); + const AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp(); const int index_code = itemp->lsb(); ToggleEnt newent{ above.m_comment + "."s + itemp->name(), @@ -855,7 +855,7 @@ class CoverageVisitor final : public VNVisitor { // not be flagged as redundant or impossible, however the results will // still be valid, albeit messier for (CoverTerm& term : l) { - if (AstVarRef* const refp = VN_CAST(term.m_exprp, VarRef)) { + if (const AstVarRef* const refp = VN_CAST(term.m_exprp, VarRef)) { varps[term.m_objective].insert(refp->varp()); } else { strs[term.m_objective].insert(term.m_emitV); @@ -865,7 +865,7 @@ class CoverageVisitor final : public VNVisitor { bool impossible = false; for (CoverTerm& term : r) { bool redundant = false; - if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + if (const AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { if (varps[term.m_objective].find(refp->varp()) != varps[term.m_objective].end()) redundant = true; diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index 39d73bd10..9b2c8f25a 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -48,7 +48,7 @@ class CoverageJoinVisitor final : public VNVisitor { // Note uses user4 V3DupFinder dupFinder; // Duplicate code detection // Hash all of the original signals we toggle cover - for (AstCoverToggle* nodep : m_toggleps) dupFinder.insert(nodep->origp()); + for (const AstCoverToggle* const nodep : m_toggleps) dupFinder.insert(nodep->origp()); if (dumpLevel() || debug() >= 9) dupFinder.dumpFile(v3Global.debugFilename("coveragejoin") + ".hash", false); // Find if there are any duplicates diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 8cbf22db3..b36758ed2 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -334,8 +334,8 @@ class DeadVisitor final : public VNVisitor { checkAll(typedefp); } } - bool shouldDeleteTypedef(AstTypedef* typedefp) { - if (auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) { + bool shouldDeleteTypedef(const AstTypedef* typedefp) { + if (const auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) { if (structp->user1() && !structp->packed()) return false; } return m_elimCells && !typedefp->attrPublic(); @@ -367,7 +367,7 @@ class DeadVisitor final : public VNVisitor { } } } - bool mightElimVar(AstVar* nodep) const { + bool mightElimVar(const AstVar* nodep) const { if (nodep->isSigPublic()) return false; // Can't elim publics! if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false; if (nodep->isTemp() && !nodep->isTrace()) return true; diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index a11d01410..072e73f29 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -540,7 +540,7 @@ class DelayedVisitor final : public VNVisitor { } // Create a unique temporary variable name - std::string uniqueTmpName(AstScope* scopep, AstVarScope* vscp, VarScopeInfo& vscpInfo) { + std::string uniqueTmpName(AstScope* scopep, const AstVarScope* vscp, VarScopeInfo& vscpInfo) { std::stringstream ss; ss << "__" << vscp->varp()->shortName() + "__v"; // If the assignment is in the same scope as the variable, just @@ -900,7 +900,7 @@ class DelayedVisitor final : public VNVisitor { return dtypep; }(); - if (AstSel* const lSelp = VN_CAST(lhsNodep, Sel)) { + if (const AstSel* const lSelp = VN_CAST(lhsNodep, Sel)) { // This is a partial assignment. // Need to create a mask and widen the value to element size. lhsNodep = lSelp->fromp(); @@ -910,7 +910,7 @@ class DelayedVisitor final : public VNVisitor { // Create mask value maskp = [&]() -> AstNodeExpr* { // Constant mask we can compute here - if (AstConst* const cLsbp = VN_CAST(sLsbp, Const)) { + if (const AstConst* const cLsbp = VN_CAST(sLsbp, Const)) { AstConst* const cp = new AstConst{flp, AstConst::DTyped{}, eDTypep}; cp->num().setMask(sWidth, cLsbp->toSInt()); return cp; @@ -927,7 +927,7 @@ class DelayedVisitor final : public VNVisitor { valuep = [&]() -> AstNodeExpr* { // Constant value with constant select we can compute here if (AstConst* const cValuep = VN_CAST(valuep, Const)) { - if (AstConst* const cLsbp = VN_CAST(sLsbp, Const)) { + if (const AstConst* const cLsbp = VN_CAST(sLsbp, Const)) { AstConst* const cp = new AstConst{flp, AstConst::DTyped{}, eDTypep}; cp->num().setAllBits0(); cp->num().opSelInto(cValuep->num(), cLsbp->toSInt(), sWidth); @@ -1097,7 +1097,7 @@ class DelayedVisitor final : public VNVisitor { VL_RESTORER(m_ignoreBlkAndNBlk); VL_RESTORER(m_inNonCombLogic); m_activep = nodep; - AstSenTree* const senTreep = nodep->sentreep(); + const AstSenTree* const senTreep = nodep->sentreep(); m_ignoreBlkAndNBlk = senTreep->hasStatic() || senTreep->hasInitial(); m_inNonCombLogic = senTreep->hasClocked(); iterateChildren(nodep); @@ -1160,7 +1160,7 @@ class DelayedVisitor final : public VNVisitor { AstNode* newp = new AstCStmt{flp, blockp}; if (nodep->isDelayed()) { - AstVarRef* const vrefp = VN_AS(eventp, VarRef); + const AstVarRef* const vrefp = VN_AS(eventp, VarRef); const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName(); AstVarScope* const dlyvscp = createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1); diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 9a9fa9e5b..03ecabf2f 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -55,11 +55,11 @@ class DescopeVisitor final : public VNVisitor { // METHODS - static bool modIsSingleton(AstNodeModule* modp) { + static bool modIsSingleton(const AstNodeModule* modp) { // True iff there's exactly one instance of this module in the design (including top). if (modp->isTop()) return true; int instances = 0; - for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + for (const AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (VN_IS(stmtp, Scope)) { if (++instances > 1) return false; } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 2936dffa8..46a9b26b5 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -79,8 +79,8 @@ struct std::hash> final { class VDfgType final { public: #include "V3Dfg__gen_type_enum.h" // From ./astgen - enum en m_e; - VDfgType() = default; + const enum en m_e; + VDfgType() = delete; // cppcheck-suppress noExplicitConstructor constexpr VDfgType(en _e) : m_e{_e} {} diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 0ea612634..a2d47394c 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -181,8 +181,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // Structure to keep track of comparison details struct Term final { - DfgVertex* m_vtxp; // Vertex to replace - bool m_inv; // '!=', instead of '==' + DfgVertex* m_vtxp = nullptr; // Vertex to replace + bool m_inv = false; // '!=', instead of '==' Term() = default; Term(DfgVertex* vtxp, bool inv) : m_vtxp{vtxp} diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index f3ef92c65..1bc756bf0 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -234,7 +234,9 @@ public: uint32_t toU32() const { return static_cast(num().toUInt()); } + // cppcheck-suppress duplInheritedMember bool isZero() const { return num().isEqZero(); } + // cppcheck-suppress duplInheritedMember bool isOnes() const { return num().isEqAllOnes(width()); } // Does this DfgConst have the given value? Note this is not easy to answer if wider than 32. diff --git a/src/V3DiagSarif.cpp b/src/V3DiagSarif.cpp index e8008725d..1a3881c99 100644 --- a/src/V3DiagSarif.cpp +++ b/src/V3DiagSarif.cpp @@ -33,14 +33,14 @@ class V3DiagSarifImp final { // METHODS void calculate() { - for (auto& msgr : m_messages) + for (const VErrorMessage& msgr : m_messages) if (msgr.code().isNamed()) m_codes.emplace(msgr.code()); int i = 0; - for (const auto& code : m_codes) m_codeIndex[code] = i++; + for (const V3ErrorCode& code : m_codes) m_codeIndex[code] = i++; } void putRules(V3OutJsonFile& of) const { - for (const auto& code : m_codes) { + for (const V3ErrorCode& code : m_codes) { of.begin().put("id", code.ascii()).put("helpUri", code.url()).end(); } } @@ -97,7 +97,7 @@ class V3DiagSarifImp final { of.begin("message"); putText(of, first_clean, first_fmt); of.end(); - if (auto fl = msg.fileline()) { + if (const FileLine* const fl = msg.fileline()) { of.begin("locations", '[').begin(); putLocation(of, fl); of.end(); @@ -175,7 +175,7 @@ public: .end(); of.begin("results", '['); - for (auto& msgr : m_messages) putResult(of, msgr); + for (const VErrorMessage& msgr : m_messages) putResult(of, msgr); of.end(); of.end(); // runs ] diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index c6dce2755..82fef00fa 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -148,8 +148,8 @@ public: void emitVarAccessors(const AstVar* nodep); template static void forModCUse(const AstNodeModule* modp, VUseType useType, T_Callable action) { - for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { - if (AstCUse* const usep = VN_CAST(itemp, CUse)) { + for (const AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { + if (const AstCUse* const usep = VN_CAST(itemp, CUse)) { if (usep->useType().containsAny(useType)) { if (usep->useType().containsAny(VUseType::INT_INCLUDE)) { action("#include \"" + prefixNameProtect(usep) + ".h\"\n"); diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h index c80154ddb..051b75fb4 100644 --- a/src/V3EmitCConstInit.h +++ b/src/V3EmitCConstInit.h @@ -33,7 +33,7 @@ class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitorConst { // METHODS - uint32_t tabModulus(AstNodeDType* dtypep) { + uint32_t tabModulus(const AstNodeDType* dtypep) { const uint32_t elemBytes = dtypep->widthTotalBytes(); return dtypep->isString() ? 1 // String : elemBytes <= 2 ? 8 // CData, SData diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index e43d511ef..3f9565d0a 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -110,7 +110,7 @@ class EmitCConstPool final : public EmitCConstInit { } public: - explicit EmitCConstPool(AstConstPool* poolp) { + explicit EmitCConstPool(const AstConstPool* poolp) { emitVars(poolp); V3Stats::addStatSum("ConstPool, Tables emitted", m_tablesEmitted); V3Stats::addStatSum("ConstPool, Constants emitted", m_constsEmitted); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 2c1c92d1c..c367bee6a 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -175,7 +175,7 @@ public: // ACCESSORS void splitSizeInc(int count) { m_splitSize += count; } - void splitSizeInc(AstNode* nodep) { splitSizeInc(nodep->nodeCount()); } + void splitSizeInc(const AstNode* nodep) { splitSizeInc(nodep->nodeCount()); } void splitSizeReset() { m_splitSize = 0; } bool splitNeeded() const { return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit(); @@ -189,13 +189,13 @@ public: char fmtLetter); bool emitSimpleOk(AstNodeExpr* nodep); - void emitIQW(AstNode* nodep) { + void emitIQW(const AstNode* nodep) { // See "Type letters" in verilated.h // Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g, "R"=queue, "U"npacked puts(nodep->dtypep()->skipRefp()->charIQWN()); } - void emitRU(AstNode* nodep) { - AstNodeDType* dtp = nodep->dtypep()->skipRefp(); + void emitRU(const AstNode* nodep) { + const AstNodeDType* dtp = nodep->dtypep()->skipRefp(); // See "Type letters" in verilated.h if (VN_IS(dtp, UnpackArrayDType)) puts("U"); @@ -210,7 +210,7 @@ public: : nodep->isDouble() ? "SD" : (nodep->isScQuad() ? "SQ" : "SI")); } - void emitDatap(AstNode* nodep) { + void emitDatap(const AstNode* nodep) { // When passing to a function with va_args the compiler doesn't // know need a pointer so when wide, need to look inside VlWide if (nodep->isWide()) puts(".data()"); @@ -319,7 +319,7 @@ public: // VISITORS using EmitCConstInit::visit; - void visit(AstCFunc* nodep) override { + void visit(AstCFunc* const nodep) override { if (nodep->emptyBody() && !nodep->isLoose()) return; VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); @@ -339,7 +339,7 @@ public: if (nodep->isConstructor()) { const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class); - if (nodep->isConstructor() && classp && classp->extendsp()) { + if (classp && classp->extendsp()) { puts("\n : "); putConstructorSubinit(classp, nodep); } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index ce420095f..c7a5824f1 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -259,7 +259,7 @@ class EmitCHeader final : public EmitCConstInit { case AttributeType::Width: { if (isArrayType) { // For arrays, get innermost element width - AstNodeDType* dtype = itemp->dtypep(); + const AstNodeDType* dtype = itemp->dtypep(); while (dtype->subDTypep()) dtype = dtype->subDTypep(); return dtype->width(); } @@ -461,7 +461,7 @@ class EmitCHeader final : public EmitCConstInit { AstMemberDType* itemp; AstMemberDType* lastItemp; - AstMemberDType* witemp = nullptr; + const AstMemberDType* witemp = nullptr; // LSB is first field in C, so loop backwards for (lastItemp = sdtypep->membersp(); lastItemp && lastItemp->nextp(); lastItemp = VN_AS(lastItemp->nextp(), MemberDType)) { diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index e2b9e0196..e1945a393 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -724,28 +724,28 @@ class EmitCTrace final : EmitCFunc { && m_typeSplitSize >= v3Global.opt.outputSplitCTrace(); } - bool emitTraceIsScBv(AstTraceInc* nodep) { + bool emitTraceIsScBv(const AstTraceInc* nodep) { const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); if (!varrefp) return false; - AstVar* const varp = varrefp->varp(); + const AstVar* const varp = varrefp->varp(); return varp->isSc() && varp->isScBv(); } - bool emitTraceIsScBigUint(AstTraceInc* nodep) { + bool emitTraceIsScBigUint(const AstTraceInc* nodep) { const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); if (!varrefp) return false; - AstVar* const varp = varrefp->varp(); + const AstVar* const varp = varrefp->varp(); return varp->isSc() && varp->isScBigUint(); } - bool emitTraceIsScUint(AstTraceInc* nodep) { + bool emitTraceIsScUint(const AstTraceInc* nodep) { const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); if (!varrefp) return false; - AstVar* const varp = varrefp->varp(); + const AstVar* const varp = varrefp->varp(); return varp->isSc() && (varp->isScUint() || varp->isScUintBool()); } - void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { + void emitTraceInitOne(const AstTraceDecl* nodep, int enumNum) { if (nodep->dtypep()->basicp()->isDouble()) { puts("tracep->declDouble("); } else if (nodep->isWide()) { @@ -906,7 +906,7 @@ class EmitCTrace final : EmitCFunc { void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { - AstVar* const varp = varrefp->varp(); + const AstVar* const varp = varrefp->varp(); if (varp->isEvent()) puts("&"); puts("("); if (emitTraceIsScBigUint(nodep)) { diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index 0949ce3fa..02926abfc 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -39,7 +39,7 @@ class CMakeEmitter final { template static string cmake_list(const T_List& strs) { string s; - for (auto& itr : strs) { + for (const std::string& itr : strs) { if (!s.empty()) s += ' '; s += '"'; s += V3OutFormatter::quoteNameControls(itr); @@ -49,7 +49,7 @@ class CMakeEmitter final { } static string cmake_list(const VFileLibList& strs) { string s; - for (auto& itr : strs) { + for (const VFileLibName& itr : strs) { if (!s.empty()) s += ' '; s += '"'; s += V3OutFormatter::quoteNameControls(itr.filename()); @@ -191,14 +191,16 @@ class CMakeEmitter final { *of << "target_link_libraries(${TOP_TARGET_NAME} PRIVATE " << prefix << ")\n"; if (!children.empty()) { *of << "target_link_libraries(" << prefix << " INTERFACE"; - for (const auto& childr : children) *of << " " << (childr)->hierPrefix(); + for (const V3HierBlock* const childp : children) { + *of << " " << childp->hierPrefix(); + } *of << ")\n"; } *of << "verilate(" << prefix << " PREFIX " << prefix << " TOP_MODULE " << hblockp->modp()->name() << " DIRECTORY " << v3Global.opt.makeDir() + "/" + prefix << " SOURCES "; - for (const auto& childr : children) { - *of << " " << v3Global.opt.makeDir() + "/" + childr->hierWrapperFilename(true); + for (const V3HierBlock* const childp : children) { + *of << " " << v3Global.opt.makeDir() + "/" + childp->hierWrapperFilename(true); } *of << " "; const string vFile = hblockp->vFileIfNecessary(); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index dc09fef88..3364308d4 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -152,11 +152,11 @@ private: VL_DEFINE_DEBUG_FUNCTIONS; // Potentially very slow, intended for debugging - string prettyNumber(const V3Number* nump, AstNodeDType* dtypep) { - if (AstRefDType* const refdtypep = VN_CAST(dtypep, RefDType)) { // + string prettyNumber(const V3Number* nump, const AstNodeDType* dtypep) { + if (const AstRefDType* const refdtypep = VN_CAST(dtypep, RefDType)) { // dtypep = refdtypep->skipRefp(); } - if (AstNodeUOrStructDType* const stp = VN_CAST(dtypep, NodeUOrStructDType)) { + if (const AstNodeUOrStructDType* const stp = VN_CAST(dtypep, NodeUOrStructDType)) { if (stp->packed()) { std::ostringstream out; out << "'{"; @@ -218,13 +218,13 @@ public: m_whyNotOptimizable = why; std::ostringstream stack; for (const auto& callstack : vlstd::reverse_view(m_callStack)) { - AstFuncRef* const funcp = callstack->m_funcp; + const AstFuncRef* const funcp = callstack->m_funcp; stack << "\n " << funcp->fileline() << "... Called from '" << funcp->prettyName() << "()' with parameters:"; V3TaskConnects* tconnects = callstack->m_tconnects; for (V3TaskConnects::iterator conIt = tconnects->begin(); conIt != tconnects->end(); ++conIt) { - AstVar* const portp = conIt->first; + const AstVar* const portp = conIt->first; AstNodeExpr* const pinp = conIt->second->exprp(); AstNodeDType* const dtypep = pinp->dtypep(); if (AstConst* const valp = fetchConstNull(pinp)) { @@ -400,7 +400,7 @@ private: // True if current node might be jumped over - all visitors must call this up front bool jumpingOver() const { return m_jumpp; } - void assignOutValue(AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) { + void assignOutValue(const AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) { if (VN_IS(nodep, AssignDly)) { // Don't do setValue, as value isn't yet visible to following statements newOutValue(vscp, valuep); @@ -458,7 +458,7 @@ private: m_varAux(vscp).usage |= VU_RV; const bool varIsConst = (nodep->varp()->isConst() || nodep->varp()->isParam()) && nodep->varp()->valuep(); - AstNodeExpr* const valuep + const AstNodeExpr* const valuep = varIsConst ? fetchValueNull(nodep->varp()->valuep()) : nullptr; // Propagate PARAM constants for constant function analysis if (varIsConst && valuep) { @@ -800,9 +800,7 @@ private: handleAssignArray(nodep, selp, valueFromp); } else if (AstConcat* const selp = VN_CAST(lhsp, Concat)) { checkNodeInfo(selp); - AstBasicDType* const rhsBasicp - = VN_CAST(selp->rhsp()->dtypep()->skipRefp(), BasicDType); - if (!rhsBasicp) { + if (!VN_IS(selp->rhsp()->dtypep()->skipRefp(), BasicDType)) { clearOptimizable(lhsp, "Assign LHS concat of non-basic type"); return; } @@ -879,7 +877,7 @@ private: checkNodeInfo(nodep); iterateChildrenConst(nodep); if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { - AstConst* const indexp = fetchConst(nodep->bitp()); + const AstConst* const indexp = fetchConst(nodep->bitp()); const uint32_t offset = indexp->num().toUInt(); AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(offset); if (!itemp) { @@ -904,7 +902,7 @@ private: iterateChildrenConst(nodep); if (m_checkOnly || !optimizable()) return; // Fetch the base constant array - if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { + if (const AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { const VNumRange& sliceRange = nodep->declRange(); const uint32_t sliceElements = sliceRange.elements(); const int sliceLo = sliceRange.lo(); @@ -962,7 +960,7 @@ private: for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { if (hit) break; - if (!hit && itemp->isDefault()) { + if (itemp->isDefault()) { iterateAndNextConstNull(itemp->stmtsp()); hit = true; } @@ -1250,7 +1248,7 @@ private: checkNodeInfo(nodep, /*display:*/ true); iterateChildrenConst(nodep); if (m_params) { - AstConst* const textp = fetchConst(nodep->fmtp()); + const AstConst* const textp = fetchConst(nodep->fmtp()); switch (nodep->displayType()) { case VDisplayType::DT_DISPLAY: // FALLTHRU case VDisplayType::DT_INFO: v3warn(USERINFO, textp->name()); break; diff --git a/src/astgen b/src/astgen index f59e84865..d36d6fd59 100755 --- a/src/astgen +++ b/src/astgen @@ -938,10 +938,11 @@ def write_ast_impl(filename): emitBlock(" BROKEN_RTN(!m_{name});\n" + " BROKEN_RTN(!m_{name}->brokeExists());\n", name=ptr['name']) - if ptr['legals'] != '': + legals = ptr['legals'].split('|') + if legals and legals != ["Node"]: emitBlock(" BROKEN_RTN(m_{name} && !(", name=ptr['name']) eor = "" - for legal in ptr['legals'].split('|'): + for legal in legals: # We use privateTypeTest, as VN_IS would assert that we know # the type is correct, but we want to check regardless, # to find errors after raw node edits/replacements @@ -956,10 +957,11 @@ def write_ast_impl(filename): if op is None: continue name, _, _, legals = op - if legals != '': + legals = legals.split('|') + if legals and legals != ["Node"]: emitBlock(" BROKEN_RTN({name}() && !(", name=name) eor = "" - for legal in legals.split('|'): + for legal in legals: emitBlock("{eor}privateTypeTest({name}())", eor=eor, name=name, @@ -978,12 +980,13 @@ def write_ast_impl(filename): if op is None: continue name, _, _, legals = op - if legals != '': + legals = legals.split('|') + if legals and legals != ["Node"]: # 'this' is a parent, where oldp replacing newp as op1p, must follow op1p's rules # Could also be on a list, we don't check for speed reasons and as V3Broken doesn't emitBlock(" if (oldp == op{i}p() && !(", i=i) eor = "" - for legal in legals.split('|'): + for legal in legals: emitBlock("{eor}privateTypeTest(newp)", eor=eor, name=name, @@ -994,16 +997,17 @@ def write_ast_impl(filename): emitBlock(" return false;\n") emitBlock("}}\n") - emitBlock("void Ast{t}::cloneRelinkGen() {{\n", t=node.name) - if node.superClass.name != 'Node': - emitBlock(" Ast{base}::cloneRelinkGen();\n", base=node.superClass.name) - for ptr in node.ptrs: - emitBlock( - " if (m_{name} && m_{name}->clonep()) m_{name} = m_{name}->clonep();\n", - name=ptr['name'], - kind=ptr['kind']) + if node.ptrs: + emitBlock("void Ast{t}::cloneRelinkGen() {{\n", t=node.name) + if node.superClass.name != 'Node': + emitBlock(" Ast{base}::cloneRelinkGen();\n", base=node.superClass.name) + for ptr in node.ptrs: + emitBlock( + " if (m_{name} && m_{name}->clonep()) m_{name} = m_{name}->clonep();\n", + name=ptr['name'], + kind=ptr['kind']) - emitBlock("}}\n") + emitBlock("}}\n") emitBlock("void Ast{t}::dumpJsonGen(std::ostream& str) const {{\n", t=node.name) if node.superClass.name != 'Node': @@ -1067,11 +1071,14 @@ def write_ast_macros(filename): Ast{t}* addNext(Ast{t}* nodep) {{ return static_cast(AstNode::addNext(this, nodep)); }} const char* brokenGen() const override; bool wouldBreakGen(const AstNode* const oldp, const AstNode* const newp) const override; - void cloneRelinkGen() override; void dumpTreeJsonOpGen(std::ostream& str, const string& indent) const override; void dumpJsonGen(std::ostream& str) const; ''', t=node.name) + if node.ptrs: + emitBlock('''\ + void cloneRelinkGen() override; + ''') if node.isLeaf: emitBlock('''\ diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt new file mode 100644 index 000000000..e1fad255b --- /dev/null +++ b/src/cppcheck-suppressions.txt @@ -0,0 +1,33 @@ +// Copyright 2003-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 + +cstyleCast +ctunullpointer +derefInvalidIteratorRedundantCheck +nullPointer +nullPointerRedundantCheck +templateRecursion +unusedFunction +unusedScopedObject +useInitializationList +useStlAlgorithm + +// Seems useless +missingIncludeSystem +// Tedious +unmatchedSuppression +// Not our code +*:include/gtkwave/* +*:include/vltstd/* +// We intentionally redefine AstNode methods to improve type safety +duplInheritedMember:src/V3AstNode*.h +duplInheritedMember:src/obj_*/V3Ast__gen_impl.h +// We intentionally redefine DfgVertex methods to match Ast +duplInheritedMember:src/obj_*/V3Dfg__gen_auto_classes.h +// TODO: fix - maybe? +duplInheritedMember:src/V3File*.h +// They are used after astgen expansion +unusedPrivateFunction:src/V3Const.cpp diff --git a/src/cppcheck_filtered b/src/cppcheck_filtered deleted file mode 100755 index ccf633938..000000000 --- a/src/cppcheck_filtered +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python3 -# pylint: disable=C0103,C0114,C0115,C0116,C0209,R0911,R0912,R0915,W0621 -###################################################################### - -import argparse -import os -import re -import sys - -SuppressMap = { - # New cpp check error Can suppress with old error - 'nullPointerRedundantCheck': 'nullPointer' -} - -###################################################################### - - -def process(cppcheck_args): - cmd = " ".join(cppcheck_args) + " 2>&1" - if Args.debug: - print("\t" + cmd) - fh = os.popen(cmd) - errs = False - last_error = "" - for line in fh: - line = line.rstrip() - if Args.debug: - print(">>>" + line) - line = re.sub(r'^\s+', '', line) - # Sometimes tacked at end-of-line - line = re.sub(r'Checking usage of global functions\.+', '', line) - line = re.sub(r' file0="[^"]+"', r'', line) - - if re.search(r'^<\?xml version', line): - continue - if re.search(r'^', line): - continue - if re.search(r'^', line): - continue - if re.search(r'^', line): - continue - if re.search(r'^', line): - continue - # An earlier id line is more specific - if re.search(r'Cppcheck cannot find all the include files', line): - continue - if re.search(r'^Checking ', line): - continue - if re.search(r'^make.*Entering directory ', line): - continue - if re.search(r'^make.*Leaving directory ', line): - continue - if re.search(r'^\s+$', line): - continue - - # Output - if re.search(r'^cppcheck --', line): - if Args.debug: - print(line) - continue - if re.search(r'^\d+/\d+ files checked', line): - print(line) - continue - - suppress = False - # --xml-format=2 - if re.search(r' Date: Tue, 19 Aug 2025 23:05:34 +0100 Subject: [PATCH 195/252] Internals: Improve V3Rtti for cppcheck (#6312) Rewrite with much less running around in the templates. Use private methods only + friend functions that do the actual type check. This avoids cppcheck warnings. --- src/V3Graph.h | 10 +--- src/V3Rtti.h | 140 +++++++++++++++++++------------------------------- 2 files changed, 56 insertions(+), 94 deletions(-) diff --git a/src/V3Graph.h b/src/V3Graph.h index 222806bb8..6a8d10b42 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -140,10 +140,7 @@ public: bool is() const { static_assert(std::is_base_of::value, "'T' must be a subtype of V3GraphEdge"); - static_assert(std::is_same::type, - VTypeListFront>::value, - "Missing VL_RTTI_IMPL(...) call in 'T'"); - return this->isInstanceOfClassWithId(T::rttiClassId()); + return V3Rtti::isInstanceOf(this); } // Return cast to subtype T and assert of that type @@ -245,10 +242,7 @@ public: bool is() const { static_assert(std::is_base_of::value, "'T' must be a subtype of V3GraphVertex"); - static_assert(std::is_same::type, - VTypeListFront>::value, - "Missing VL_RTTI_IMPL(...) call in 'T'"); - return this->isInstanceOfClassWithId(T::rttiClassId()); + return V3Rtti::isInstanceOf(this); } // Return cast to subtype T and assert of that type diff --git a/src/V3Rtti.h b/src/V3Rtti.h index 627310e02..e830d63ab 100644 --- a/src/V3Rtti.h +++ b/src/V3Rtti.h @@ -22,110 +22,78 @@ #include #include -// Holds list of types as template parameter pack. -// Useful in compile-time code generation. -template -struct VTypeList final { - template - constexpr VTypeList operator+(VTypeList) const { - return {}; - } -}; +namespace V3Rtti { -// Holds one type. -// Can be safely used as a return or argument type, and even instantiated, without triggering any -// potential limitations or effects of the held type. +// Use 'V3Rtti::isInstanceOf(objp)' to check if 'objp' is of subtype 'T' (downcasting) +template +bool isInstanceOf(SuperClass* objp) { + static_assert(std::is_base_of::value, "Impossible 'isInstanceOf'"); + static_assert(!std::is_same::value, "Useless 'isInstanceOf'"); + static_assert(std::is_same< // + typename std::remove_cv::type, // + typename SubClass::V3RttiThisClass // + >::value, + "Missing VL_RTTI_IMPL(...) in 'SubClass'"); + return objp->v3RttiIsInstanceOfImpl(SubClass::v3RttiClassId()); +} + +namespace internal { + +// Returns true iff 'id' equals the 'rttiClassId()' of either 'T', or any of its base classes template -struct VTypeWrapper final { - using type_t = T; -}; - -// Implementation details of other constructs defined in this header. -namespace V3RttiInternal { - -// Helper function for extracting first type from VTypeList. -template -static inline constexpr VTypeWrapper vlTypeListFront(VTypeList) { - return {}; +inline bool matchClassId(uintptr_t id) VL_PURE { + return id == T::v3RttiClassId() || matchClassId(id); } -// Overload for empty type list. Returns false. -inline static constexpr bool isClassIdOfOneOf(uintptr_t id, VTypeList<>) VL_PURE { return false; } - -// Returns true iff `id` has the same value as `T::rttiClassId()`, where `T` is any type held by -// `VTypeList` object passed as the second argument. -template -inline static constexpr bool isClassIdOfOneOf(uintptr_t id, VTypeList) VL_PURE { - return id == Base0::rttiClassId() || isClassIdOfOneOf(id, VTypeList{}); +// Specialization for the root class, which have 'void' for 'V3RttiBaseClass' +template <> +inline bool matchClassId(uintptr_t) VL_PURE { + return false; } -} // namespace V3RttiInternal +} // namespace internal -// Alias for the first (frontmost) type held by type list `TL`. -template -using VTypeListFront = typename decltype(::V3RttiInternal::vlTypeListFront(TL{}))::type_t; - -// `VTypeList` holding types from type lists `TL1` followed by types from type list `TL2`. -template -using VJoinedTypeLists = decltype(TL1{} + TL2{}); +} // namespace V3Rtti // Common code used by VL_RTTI_COMMON_IMPL and VL_RTTI_COMMON_IMPL_BASE. -#define V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \ -private: \ +#define V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass, OVERRIDE) \ + using V3RttiThisClass = ThisClass; \ /* A type used only for implementation of the static_assert below. */ \ struct RttiUniqueTypeForThisClass {}; \ static_assert( \ std::is_same::value, \ "'ThisClass' argument (" #ThisClass ") does not match the class name"); \ -\ -public: \ - /* Returns unique ID of the class. Useful with `isInstanceOfClassWithId()` method. */ \ - static uintptr_t rttiClassId() VL_PURE { \ - /* The only purpose of the following variable is to occupy an unique memory address. */ \ - /* This address is used as an unique class ID. */ \ - static char aStaticVariable; \ - return reinterpret_cast(&aStaticVariable); \ + /* The implementation can pick up the private members */ \ + template \ + friend bool V3Rtti::internal::matchClassId(uintptr_t id); \ + template \ + friend bool V3Rtti::isInstanceOf(U*); \ + /* Returns unique type ID of the class. */ \ + static uintptr_t v3RttiClassId() VL_PURE { \ + /* Don't complain this function is unused */ (void)&v3RttiClassId; \ + /* The address of this static variable is used as the unique class type ID. */ \ + static char s_vlrttiClassId; \ + return reinterpret_cast(&s_vlrttiClassId); \ + } \ + /* Dispatch to matchClassId with the correct sub type (this type) */ \ + virtual bool v3RttiIsInstanceOfImpl(uintptr_t id) const OVERRIDE VL_PURE { \ + return ::V3Rtti::internal::matchClassId(id); \ } -// Call this macro at the beginning of class definition if the class derives from a -// class with VL_RTTI_IMPL or VL_RTTI_IMPL_BASE calls. +// Call this macro at the beginning of class definition if the class derives +// from a class with VL_RTTI_IMPL or VL_RTTI_IMPL_BASE calls. #define VL_RTTI_IMPL(ThisClass, DirectBaseClass) \ - V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \ - static_assert( \ - std::is_same>::value, \ - "Missing VL_RTTI_IMPL(...) in the direct base class (" #DirectBaseClass ")"); \ -\ -public: \ - /* Type list containing this class and all classes from the inheritance chain. */ \ - using RttiThisAndBaseClassesList \ - = VJoinedTypeLists, \ - typename DirectBaseClass::RttiThisAndBaseClassesList>; \ -\ -protected: \ - /* Returns true iff `id` has the same value as `T::rttiClassId()`, where `T` is either this \ - * class or any class from this class' inheritance chain. */ \ - bool isInstanceOfClassWithId(uintptr_t id) const override VL_PURE { \ - return ::V3RttiInternal::isClassIdOfOneOf(id, RttiThisAndBaseClassesList{}); \ - } \ -\ -private: /* Revert to private visibility after this macro */ +private: \ + using V3RttiBaseClass = DirectBaseClass; \ + V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass, override) \ +private: -// Call this macro at the beginning of a base class to implement class type queries using -// `p->isInstanceOfClassWithId(ClassName::rttiClassId())`. +// Call this macro at the beginning of a base class to implement class type +// usable with V3Rtti::InstanceOf #define VL_RTTI_IMPL_BASE(ThisClass) \ - V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \ -public: \ - /* Type list containing this class and all classes from the inheritance chain. */ \ - using RttiThisAndBaseClassesList = VTypeList; \ -\ -protected: \ - /* Returns true iff `id` has the same value as value returned by this class' \ - `rttiClassId()` method. */ \ - virtual bool isInstanceOfClassWithId(uintptr_t id) const VL_PURE { \ - return id == rttiClassId(); \ - } \ -\ -private: /* Revert to private visibility after this macro */ +private: \ + using V3RttiBaseClass = void; \ + V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass, /* base */) \ +private: #endif // Guard From f71b8e61955cc52c122f8f1ae917a17afc6697fd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 19 Aug 2025 21:36:52 -0400 Subject: [PATCH 196/252] Internals: Fix up include/ cppcheck issues (#6311) --- Makefile.in | 6 +++--- include/verilated.cpp | 23 +++++++---------------- include/verilated.h | 6 +++--- include/verilated_cov.cpp | 10 +++++++++- include/verilated_dpi.cpp | 4 ++-- include/verilated_fst_c.cpp | 2 +- include/verilated_funcs.h | 5 ++--- include/verilated_imp.h | 2 +- include/verilated_profiler.cpp | 2 +- include/verilated_random.cpp | 4 ++-- include/verilated_random.h | 4 ++-- include/verilated_save.cpp | 2 +- include/verilated_save.h | 12 ++++++++---- include/verilated_timing.cpp | 2 +- include/verilated_types.h | 2 +- include/verilated_vcd_c.cpp | 1 + include/verilated_vpi.cpp | 30 ++++++++++++++++++++++++------ 17 files changed, 69 insertions(+), 48 deletions(-) diff --git a/Makefile.in b/Makefile.in index 763b18f94..0ed2b5cf7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -392,7 +392,6 @@ install-msg: ###################################################################### # Format/Lint -# Use --xml flag to see the cppcheck code to use for suppression CPPCHECK1_CPP = $(wildcard $(srcdir)/include/*.cpp) CPPCHECK2_CPP = $(wildcard $(srcdir)/examples/*/*.cpp) CPPCHECK3_CPP = $(wildcard $(srcdir)/src/Vlc*.cpp) @@ -422,8 +421,9 @@ CPPCHECK_INC += -I$(srcdir)/include/vltstd CPPCHECK_INC += -I$(srcdir)/src/obj_dbg CPPCHECK_INC += -I$(srcdir)/src -$(CPPCHECK_CACHE): - mkdir -p $@ +$(CPPCHECK_CACHE): $(CHECK_CPP) $(CHECK_H) $(CHECK_YL) + /bin/rm -rf $@ + /bin/mkdir -p $@ cppcheck: cppcheck-1 cppcheck-2 cppcheck-3 cppcheck-4 cppcheck-5 cppcheck-6 cppcheck-7 cppcheck-8 cppcheck-1: | $(CPPCHECK_CACHE) diff --git a/include/verilated.cpp b/include/verilated.cpp index 3fe16f361..73cc45884 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -627,11 +627,9 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lw 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; - // 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) { if (bit > 0) { // power = power*power @@ -1384,7 +1382,6 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf _vl_vsss_skipspace(fp, floc, fromp, fstr); _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE"); if (!t_tmp[0]) goto done; - // cppcheck-has-bug-suppress unusedStructMember, unreadVariable union { double r; int64_t ld; @@ -2151,7 +2148,6 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star if (VL_UNLIKELY(!m_fp)) { // We don't report the Verilog source filename as it slow to have to pass it down VL_WARN_MT(filename.c_str(), 0, "", "$readmem file not found"); - // cppcheck-has-bug-suppress resourceLeak // m_fp is nullptr return; } } @@ -2291,7 +2287,6 @@ VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData st m_fp = std::fopen(filename.c_str(), "w"); if (VL_UNLIKELY(!m_fp)) { VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found"); - // cppcheck-has-bug-suppress resourceLeak // m_fp is nullptr return; } } @@ -2475,7 +2470,6 @@ static const char* vl_time_str(int scale) VL_PURE { double vl_time_multiplier(int scale) VL_PURE { // Return timescale multiplier -18 to +18 // For speed, this does not check for illegal values - // cppcheck-has-bug-suppress arrayIndexOutOfBoundsCond if (scale < 0) { static const double neg10[] = {1.0, 0.1, @@ -2496,7 +2490,6 @@ double vl_time_multiplier(int scale) VL_PURE { 0.0000000000000001, 0.00000000000000001, 0.000000000000000001}; - // cppcheck-has-bug-suppress arrayIndexOutOfBoundsCond return neg10[-scale]; } else { static const double pow10[] = {1.0, @@ -2518,7 +2511,6 @@ double vl_time_multiplier(int scale) VL_PURE { 10000000000000000.0, 100000000000000000.0, 1000000000000000000.0}; - // cppcheck-has-bug-suppress arrayIndexOutOfBoundsCond return pow10[scale]; } } @@ -2814,7 +2806,7 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE { VerilatedImp::userDump(); } -void VerilatedContext::addModel(VerilatedModel* modelp) { +void VerilatedContext::addModel(const VerilatedModel* modelp) { if (!quiet()) { // CPU time isn't read as starting point until model creation, so that quiet() is set // Thus if quiet(), avoids slow OS read affecting some usages that make many models @@ -2852,7 +2844,7 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() { void VerilatedContext::prepareClone() { delete m_threadPool.release(); } VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() { - if (VL_UNLIKELY(m_threadPool)) m_threadPool.release(); + if (VL_UNLIKELY(m_threadPool)) (void)m_threadPool.release(); m_threadPool = std::unique_ptr(new VlThreadPool{this, m_threads - 1}); return m_threadPool.get(); } @@ -3133,7 +3125,7 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) VL_FATAL_MT("", 0, "", "Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated" " with --trace-fst or --trace-vcd option"); - for (auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options); + for (const auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options); } void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE { // Model creation registering a callback for when Verilated::trace() called @@ -3148,7 +3140,6 @@ VerilatedSyms::VerilatedSyms(VerilatedContext* contextp) : _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) { VerilatedContext::checkMagic(_vm_contextp__); Verilated::threadContextp(_vm_contextp__); - // cppcheck-has-bug-suppress noCopyConstructor __Vm_evalMsgQp = new VerilatedEvalMsgQueue; } @@ -3550,16 +3541,16 @@ void VerilatedScope::scopeDump() const { VerilatedImp::exportName(i)); } } - if (const VerilatedVarNameMap* const varsp = this->varsp()) { - for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first); + if (const VerilatedVarNameMap* const ivarsp = this->varsp()) { + for (const auto& i : *ivarsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first); } } -void VerilatedHierarchy::add(VerilatedScope* fromp, VerilatedScope* top) { +void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) { VerilatedImp::hierarchyAdd(fromp, top); } -void VerilatedHierarchy::remove(VerilatedScope* fromp, VerilatedScope* top) { +void VerilatedHierarchy::remove(const VerilatedScope* fromp, const VerilatedScope* top) { VerilatedImp::hierarchyRemove(fromp, top); } diff --git a/include/verilated.h b/include/verilated.h index 799cc4bb4..294ab94a1 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -644,7 +644,7 @@ public: } // Internal: Model and thread setup - void addModel(VerilatedModel*); + void addModel(const VerilatedModel* modelp); VerilatedVirtualBase* threadPoolp(); void prepareClone(); VerilatedVirtualBase* threadPoolpOnClone(); @@ -763,8 +763,8 @@ public: // But internals only - called from VerilatedModule's class VerilatedHierarchy final { public: - static void add(VerilatedScope* fromp, VerilatedScope* top); - static void remove(VerilatedScope* fromp, VerilatedScope* top); + static void add(const VerilatedScope* fromp, const VerilatedScope* top); + static void remove(const VerilatedScope* fromp, const VerilatedScope* top); }; //=========================================================================== diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index d81742199..96e7bb1d1 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -255,17 +255,21 @@ private: public: // PUBLIC METHODS + // cppcheck-suppress dupInheritedMember std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); } + // cppcheck-suppress dupInheritedMember void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; m_forcePerInstance = flag; } + // cppcheck-suppress dupInheritedMember void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; clearGuts(); } + // cppcheck-suppress dupInheritedMember void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; @@ -281,10 +285,11 @@ public: m_items = newlist; } } + // cppcheck-suppress dupInheritedMember void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; - for (const auto& itemp : m_items) itemp->zero(); + for (const VerilatedCovImpItem* const itemp : m_items) itemp->zero(); } // We assume there's always call to i/f/p in that order @@ -307,6 +312,7 @@ public: valps[0] = m_insertFilenamep; const std::string linestr = std::to_string(m_insertLineno); ckeyps[1] = "lineno"; + // cppcheck-suppress autoVariables // Used only below for insert valps[1] = linestr.c_str(); // Default page if not specified const char* fnstartp = m_insertFilenamep; @@ -316,6 +322,7 @@ public: const size_t page_len = fnendp - fnstartp; const std::string page_default = "sp_user/" + std::string{fnstartp, page_len}; ckeyps[2] = "page"; + // cppcheck-suppress autoVariables // Used only below for insert valps[2] = page_default.c_str(); // Keys -> strings @@ -357,6 +364,7 @@ public: m_insertp = nullptr; } + // cppcheck-suppress dupInheritedMember void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 9e2b1cf39..208db5feb 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -290,7 +290,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, break; } case VLVT_WDATA: { - WDataOutP wdatap = (reinterpret_cast(datap)); + WDataInP wdatap = (reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i]; return; } @@ -329,7 +329,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl break; } case VLVT_WDATA: { - WDataOutP wdatap = (reinterpret_cast(datap)); + WDataInP wdatap = (reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) { d[i].aval = wdatap[i]; d[i].bval = 0; diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index bd2281030..604bee576 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -297,7 +297,7 @@ VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) { void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) { if (offload()) { - OffloadBuffer* const offloadBufferp = static_cast(bufp); + const OffloadBuffer* const offloadBufferp = static_cast(bufp); if (offloadBufferp->m_offloadBufferWritep) { m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep; return; // Buffer will be deleted by the offload thread diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index f8c5ef0a7..c18c32022 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -652,7 +652,7 @@ static inline IData VL_REDAND_IW(int lbits, WDataInP const lwp) VL_PURE { EData combine = lwp[0]; for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; - // cppcheck-has-bug-suppress knownConditionTrueFalse + // cppcheck-suppress knownConditionTrueFalse return ((~combine) == 0); } @@ -1094,14 +1094,12 @@ static inline WDataOutP VL_MULS_WWW(int lbits, WDataOutP owp, WDataInP const lwp if (lneg) { // Negate lhs lwusp = lwstore; VL_NEGATE_W(words, lwstore, lwp); - // cppcheck-has-bug-suppress unreadVariable lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it } const EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); if (rneg) { // Negate rhs rwusp = rwstore; VL_NEGATE_W(words, rwstore, rwp); - // cppcheck-has-bug-suppress unreadVariable rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it } VL_MUL_W(words, owp, lwusp, rwusp); @@ -2760,6 +2758,7 @@ static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataIn const int w = obits < upperbits ? obits : upperbits; const int insmask = VL_MASK_E(w); iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask); + // cppcheck-suppress knownConditionTrueFalse if (w == obits) return; obits -= w; } diff --git a/include/verilated_imp.h b/include/verilated_imp.h index cdc801b53..603f896c4 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -363,7 +363,7 @@ private: VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(m_fdMutex) { VerilatedFpList fp; // cppverilator-suppress integerOverflow shiftTooManyBitsSigned - if ((fdi & (1 << 31)) != 0) { + if (VL_BITISSET_I(fdi, 31)) { // Non-MCD case const IData idx = fdi & VL_MASK_I(31); switch (idx) { diff --git a/include/verilated_profiler.cpp b/include/verilated_profiler.cpp index 310345137..35617db6c 100644 --- a/include/verilated_profiler.cpp +++ b/include/verilated_profiler.cpp @@ -145,7 +145,7 @@ void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd) fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n", Verilated::threadContextp()->profExecWindow()); std::string numa = "no threads"; - if (VlThreadPool* const threadPoolp + if (const VlThreadPool* const threadPoolp = static_cast(Verilated::threadContextp()->threadPoolp())) { numa = threadPoolp->numaStatus(); } diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index c61c95880..b822c2f09 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -455,8 +455,8 @@ bool VlRandomizer::parseSolution(std::iostream& f) { const auto it = m_vars.find(name); if (it == m_vars.end()) continue; const VlRandomVar& varr = *it->second; - if (m_randmode && !varr.randModeIdxNone()) { - if (!(m_randmode->at(varr.randModeIdx()))) continue; + if (m_randmodep && !varr.randModeIdxNone()) { + if (!m_randmodep->at(varr.randModeIdx())) continue; } if (!indices.empty()) { std::ostringstream oss; diff --git a/include/verilated_random.h b/include/verilated_random.h index b3bea7147..ce9f22ab1 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -200,7 +200,7 @@ class VlRandomizer final { std::map> m_vars; // Solver-dependent // variables ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration - const VlQueue* m_randmode; // rand_mode state; + const VlQueue* m_randmodep = nullptr; // rand_mode state; int m_index = 0; // Internal counter for key generation // PRIVATE METHODS @@ -574,7 +574,7 @@ public: void hard(std::string&& constraint); void clear(); - void set_randmode(const VlQueue& randmode) { m_randmode = &randmode; } + void set_randmode(const VlQueue& randmode) { m_randmodep = &randmode; } #ifdef VL_DEBUG void dump() const; #endif diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index b866de572..d9807d2e2 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -220,7 +220,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE { if (VL_UNLIKELY(!isOpen())) return; // Move remaining characters down to start of buffer. (No memcpy, overlaps allowed) uint8_t* rp = m_bufp; - for (uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps + for (const uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps m_endp = m_bufp + (m_endp - m_cp); m_cp = m_bufp; // Reset buffer // Read into buffer starting at m_endp diff --git a/include/verilated_save.h b/include/verilated_save.h index c1a7c4a0f..c28fda329 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -47,8 +47,10 @@ protected: std::string m_filename; // Filename, for error messages VerilatedAssertOneThread m_assertOne; // Assert only called from single thread - static constexpr size_t bufferSize() { return 256 * 1024; } // See below for slack calculation - static constexpr size_t bufferInsertSize() { return 16 * 1024; } + static constexpr size_t bufferSize() { + return 256 * 1024L; + } // See below for slack calculation + static constexpr size_t bufferInsertSize() { return 16 * 1024L; } void header() VL_MT_UNSAFE_ONE; void trailer() VL_MT_UNSAFE_ONE; @@ -119,8 +121,10 @@ protected: std::string m_filename; // Filename, for error messages VerilatedAssertOneThread m_assertOne; // Assert only called from single thread - static constexpr size_t bufferSize() { return 256 * 1024; } // See below for slack calculation - static constexpr size_t bufferInsertSize() { return 16 * 1024; } + static constexpr size_t bufferSize() { + return 256 * 1024L; + } // See below for slack calculation + static constexpr size_t bufferInsertSize() { return 16 * 1024L; } virtual void fill() = 0; void header() VL_MT_UNSAFE_ONE; diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 0724a43e9..58c21bdb4 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -99,7 +99,7 @@ void VlDelayScheduler::dump() const { VL_DBG_MSGF(" No delayed processes:\n"); } else { VL_DBG_MSGF(" Delayed processes:\n"); - for (auto& susp : m_zeroDelayed) { + for (const auto& susp : m_zeroDelayed) { VL_DBG_MSGF(" Awaiting #0-delayed resumption, " "time () %" PRIu64 ": ", m_context.time()); diff --git a/include/verilated_types.h b/include/verilated_types.h index 91f9e8cba..969fdec7c 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -499,7 +499,7 @@ private: public: // CONSTRUCTORS - // m_defaultValue isn't defaulted. Caller's constructor must do it. + // cppcheck-suppress uninitMember // m_defaultValue isn't defaulted, caller's constructor must VlQueue() = default; ~VlQueue() = default; VlQueue(const VlQueue&) = default; diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index ee4581a0d..ccab11a6d 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -490,6 +490,7 @@ VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) { // Note: This is called from VerilatedVcd::dump, which already holds the lock // If no buffer available, allocate a new one if (m_freeBuffers.empty()) { + // cppcheck-suppress unreadVariable // cppcheck bug, used below constexpr size_t pageSize = 4096; // 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety size_t startingSize = roundUpToMultipleOf(4 * m_maxSignalBytes); diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 144587e91..5cd59ffc6 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -114,6 +114,7 @@ public: #endif } // MEMBERS + // cppcheck-suppress duplInheritedMember static VerilatedVpio* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -144,6 +145,7 @@ public: , m_time{time} , m_reason{reason} {} ~VerilatedVpioReasonCb() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioReasonCb* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -158,6 +160,7 @@ public: explicit VerilatedVpioConst(int32_t num) : m_num{num} {} ~VerilatedVpioConst() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -187,6 +190,7 @@ public: m_indexedDim = varp->m_indexedDim; } } + // cppcheck-suppress duplInheritedMember static VerilatedVpioVarBase* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -258,6 +262,7 @@ public: : VerilatedVpioVarBase{varp, scopep} {} ~VerilatedVpioParam() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioParam* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -283,6 +288,7 @@ public: explicit VerilatedVpioRange(const VerilatedRange* rangep) : m_rangep{rangep} {} ~VerilatedVpioRange() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -301,6 +307,7 @@ public: m_iter = m_ranges.begin(); } ~VerilatedVpioRangeIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioRangeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -333,6 +340,7 @@ public: m_defname = m_scopep->defname(); } ~VerilatedVpioScope() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -379,6 +387,7 @@ public: ~VerilatedVpioVar() override { if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr); } + // cppcheck-suppress duplInheritedMember static VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -450,6 +459,7 @@ public: m_topscopep = Verilated::threadContextp()->scopeFind("TOP"); } ~VerilatedVpioVarIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -508,6 +518,7 @@ public: for (auto it : m_ranges) m_nextIndex.push_back(it.right()); } ~VerilatedVpioRegIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioRegIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -562,6 +573,7 @@ public: } if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true; } + // cppcheck-suppress duplInheritedMember static VerilatedVpioModule* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -578,6 +590,7 @@ public: m_it = m_vec->begin(); } ~VerilatedVpioModuleIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioModuleIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -607,6 +620,7 @@ public: m_it = m_vec->begin(); } ~VerilatedVpioScopeIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioScopeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -640,6 +654,7 @@ public: if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit; } + // cppcheck-suppress duplInheritedMember static VerilatedVpioPackage* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -657,6 +672,7 @@ public: m_it = m_vec->begin(); } ~VerilatedVpioInstanceIter() override = default; + // cppcheck-suppress duplInheritedMember static VerilatedVpioInstanceIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } @@ -1041,7 +1057,7 @@ public: } if (was_last) break; } - for (const auto& ip : update) { + for (const VerilatedVpioVar* const ip : update) { std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize()); } return called; @@ -2540,7 +2556,7 @@ void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, s = (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi) | ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); } - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) | ((word << info.m_bitOffset) & info.m_maskLo); } @@ -3589,12 +3605,14 @@ PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) { return chars; } +// cppcheck-suppress constParameterPointer PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); return VL_VPRINTF(formatp, ap); } +// cppcheck-suppress constParameterPointer PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) { VerilatedVpiImp::assertOneCheck(); FILE* const fp = VL_CVT_I_FP(mcd); @@ -3631,10 +3649,10 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) { // executing vpi_chk_error does not reset error // error_info_p can be nullptr, so only return level in that case VerilatedVpiImp::assertOneCheck(); - p_vpi_error_info const _error_info_p = VerilatedVpiImp::error_info()->getError(); - if (error_info_p && _error_info_p) *error_info_p = *_error_info_p; - if (!_error_info_p) return 0; // no error occurred - return _error_info_p->level; // return error severity level + const p_vpi_error_info imp_info_p = VerilatedVpiImp::error_info()->getError(); + if (error_info_p && imp_info_p) *error_info_p = *imp_info_p; + if (!imp_info_p) return 0; // no error occurred + return imp_info_p->level; // return error severity level } #ifndef VL_NO_LEGACY From 9b2f55ab391532d15c50c2c5678dce29701d79cd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 20 Aug 2025 07:27:55 -0400 Subject: [PATCH 197/252] Internals: Fix up include/ cppcheck issues (#6311) --- include/verilated_cov.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 96e7bb1d1..a52fe0ad6 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -255,21 +255,21 @@ private: public: // PUBLIC METHODS - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); } - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; m_forcePerInstance = flag; } - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; clearGuts(); } - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; @@ -285,7 +285,7 @@ public: m_items = newlist; } } - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; @@ -364,7 +364,7 @@ public: m_insertp = nullptr; } - // cppcheck-suppress dupInheritedMember + // cppcheck-suppress duplInheritedMember void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock{m_mutex}; From 95c8b7bb0027960d0f24b7b82f159d2f781c7d29 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 20 Aug 2025 13:31:04 +0200 Subject: [PATCH 198/252] Support separate coverage counters for toggles 0->1 and 1->0 (#6086) --- include/verilated_cov.h | 42 +- src/V3AstNodeOther.h | 5 +- src/V3EmitCFunc.h | 6 +- src/V3EmitCImp.cpp | 3 + test_regress/t/t_cover_toggle.out | 89 ++-- test_regress/t/t_cover_toggle.v | 45 +- test_regress/t/t_cover_toggle__points.out | 431 ++++++++++++------ test_regress/t/t_cover_toggle_min.info.out | 14 +- .../t/t_wrapper_context__top0.dat.out | 204 ++++++--- .../t/t_wrapper_context__top1.dat.out | 204 ++++++--- 10 files changed, 678 insertions(+), 365 deletions(-) diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 31d4fac0f..d7ede48bc 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -77,21 +77,28 @@ class VerilatedCovImp; static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData, const IData oldData) { - for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; + const IData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1; + } } -static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const IData newData, - const IData oldData) { - for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; +static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData, + const QData oldData) { + const QData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1; + } } static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData, WDataInP oldData) { for (int i = 0; i < VL_WORDS_I(width); ++i) { - const EData changed = newData[i] ^ oldData[i]; - if (changed) { + const EData chgData = newData[i] ^ oldData[i]; + if (chgData) { for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { - *(covp + i * VL_EDATASIZE + j) += (changed >> j) & 1; + *(covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1)) + += (chgData >> j) & 1; } } } @@ -99,18 +106,20 @@ static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WData static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic* covp, const IData newData, const IData oldData) VL_MT_SAFE { + const IData chgData = newData ^ oldData; for (int i = 0; i < width; ++i) { - if (VL_BITISSET_I((newData ^ oldData), i)) { - (covp + i)->fetch_add(1, std::memory_order_relaxed); + if (VL_BITISSET_I(chgData, i)) { + (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed); } } } static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic* covp, - const IData newData, const IData oldData) VL_MT_SAFE { + const QData newData, const QData oldData) VL_MT_SAFE { + const QData chgData = newData ^ oldData; for (int i = 0; i < width; ++i) { - if (VL_BITISSET_Q((newData ^ oldData), i)) { - (covp + i)->fetch_add(1, std::memory_order_relaxed); + if (VL_BITISSET_Q(chgData, i)) { + (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed); } } } @@ -118,11 +127,12 @@ static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic* covp, WDataInP newData, WDataInP oldData) VL_MT_SAFE { for (int i = 0; i < VL_WORDS_I(width); ++i) { - const EData changed = newData[i] ^ oldData[i]; - if (changed) { + const EData chgData = newData[i] ^ oldData[i]; + if (chgData) { for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { - if (VL_BITISSET_E(changed, j)) { - (covp + i * VL_EDATASIZE + j)->fetch_add(1, std::memory_order_relaxed); + if (VL_BITISSET_E(chgData, j)) { + (covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1)) + ->fetch_add(1, std::memory_order_relaxed); } } } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index aaaf9698f..b69061703 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2326,7 +2326,10 @@ public: ASTGEN_MEMBERS_AstCoverToggleDecl; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - int size() const override { return m_range.elements(); } + int size() const override { + // Changes 0 -> 1 and 1 -> 0 are counted separately + return 2 * m_range.elements(); + } const VNumRange& range() const { return m_range; } bool sameNode(const AstNode* samep) const override { const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index c367bee6a..46a42d316 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -758,7 +758,7 @@ public: puts(");\n"); } void visit(AstCoverInc* nodep) override { - if (nodep->declp()->size() == 1) { + if (VN_IS(nodep->declp(), CoverOtherDecl)) { if (v3Global.opt.threads() > 1) { putns(nodep, "vlSymsp->__Vcoverage["); puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); @@ -777,7 +777,9 @@ public: } emitIQW(nodep->toggleExprp()); puts("("); - puts(cvtToStr(nodep->declp()->size())); + // Each bit of variable has 2 counters, so the size of variable is half of the size of + // coverpoint + puts(cvtToStr(nodep->declp()->size() / 2)); puts(", "); puts("vlSymsp->__Vcoverage + "); puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index e1945a393..cfe543dd4 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -355,6 +355,7 @@ class EmitCImp final : EmitCFunc { puts("int step = (end >= begin) ? 1 : -1;\n"); // range is inclusive puts("for (int i = begin; i != end + step; i += step) {\n"); + puts("for (int j = 0; j < 2; j++) {\n"); if (v3Global.opt.threads() > 1) { puts("uint32_t* count32p = reinterpret_cast(countp);\n"); } else { @@ -366,6 +367,7 @@ class EmitCImp final : EmitCFunc { puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n"); puts("std::string commentWithIndex = commentp;\n"); puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n"); + puts("commentWithIndex += j ? \":0->1\" : \":1->0\";\n"); // Used for second++ instantiation of identical bin puts("if (!enable) count32p = &fake_zero_count;\n"); puts("*count32p = 0;\n"); @@ -382,6 +384,7 @@ class EmitCImp final : EmitCFunc { puts("++countp;\n"); puts("}\n"); puts("}\n"); + puts("}\n"); splitSizeInc(10); } } diff --git a/test_regress/t/t_cover_toggle.out b/test_regress/t/t_cover_toggle.out index cb27dbf59..b143b4d08 100644 --- a/test_regress/t/t_cover_toggle.out +++ b/test_regress/t/t_cover_toggle.out @@ -12,7 +12,7 @@ clk, check_real, check_array_real, check_string ); - 000019 input clk; +~000010 input clk; input real check_real; // Check issue #2741 input real check_array_real [1:0]; input string check_string; // Check issue #2766 @@ -25,11 +25,11 @@ logic b; } str_t; -%000002 reg toggle; initial toggle='0; +%000001 reg toggle; initial toggle='0; -%000002 str_t stoggle; initial stoggle='0; +%000001 str_t stoggle; initial stoggle='0; - 000019 str_logic strl; initial strl='0; +~000010 str_logic strl; initial strl='0; union { real val1; // TODO use bit [7:0] here @@ -38,11 +38,11 @@ const reg aconst = '0; -%000002 reg [1:0][1:0] ptoggle; initial ptoggle=0; +%000001 reg [1:0][1:0] ptoggle; initial ptoggle=0; integer cyc; initial cyc=1; -~000011 wire [7:0] cyc_copy = cyc[7:0]; -%000002 wire toggle_up; +%000006 wire [7:0] cyc_copy = cyc[7:0]; +%000001 wire toggle_up; typedef struct { int q[$]; @@ -55,8 +55,8 @@ // verilator lint_on ASCRANGE bit [0:0] y; } str_bit_t; -%000002 str_bit_t str_bit; -%000002 str_bit_t [5:2] str_bit_arr; +%000001 str_bit_t str_bit; +%000001 str_bit_t [5:2] str_bit_arr; assign strl.a = clk; @@ -146,29 +146,40 @@ // t.a1 and t.a2 collapse to a count of 2 - 000038 input clk; + 000020 input clk; -%000004 input toggle; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 input toggle; + // CHECK_COVER(-1,"top.t.a*","toggle:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle:1->0",2) + // (t.a1 and t.a2) -~000022 input [7:0] cyc_copy; - // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]",22) - // CHECK_COVER(-2,"top.t.a*","cyc_copy[1]",10) - // CHECK_COVER(-3,"top.t.a*","cyc_copy[2]",4) - // CHECK_COVER(-4,"top.t.a*","cyc_copy[3]",2) - // CHECK_COVER(-5,"top.t.a*","cyc_copy[4]",0) - // CHECK_COVER(-6,"top.t.a*","cyc_copy[5]",0) - // CHECK_COVER(-7,"top.t.a*","cyc_copy[6]",0) - // CHECK_COVER(-8,"top.t.a*","cyc_copy[7]",0) +~000012 input [7:0] cyc_copy; + // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]:0->1",12) + // CHECK_COVER(-2,"top.t.a*","cyc_copy[0]:1->0",10) + // CHECK_COVER(-3,"top.t.a*","cyc_copy[1]:0->1",6) + // CHECK_COVER(-4,"top.t.a*","cyc_copy[1]:1->0",4) + // CHECK_COVER(-5,"top.t.a*","cyc_copy[2]:0->1",2) + // CHECK_COVER(-6,"top.t.a*","cyc_copy[2]:1->0",2) + // CHECK_COVER(-7,"top.t.a*","cyc_copy[3]:0->1",2) + // CHECK_COVER(-8,"top.t.a*","cyc_copy[3]:1->0",0) + // CHECK_COVER(-9,"top.t.a*","cyc_copy[4]:0->1",0) + // CHECK_COVER(-10,"top.t.a*","cyc_copy[4]:1->0",0) + // CHECK_COVER(-11,"top.t.a*","cyc_copy[5]:0->1",0) + // CHECK_COVER(-12,"top.t.a*","cyc_copy[5]:1->0",0) + // CHECK_COVER(-13,"top.t.a*","cyc_copy[6]:0->1",0) + // CHECK_COVER(-14,"top.t.a*","cyc_copy[6]:1->0",0) + // CHECK_COVER(-15,"top.t.a*","cyc_copy[7]:0->1",0) + // CHECK_COVER(-16,"top.t.a*","cyc_copy[7]:1->0",0) -%000004 reg toggle_internal; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 reg toggle_internal; + // CHECK_COVER(-1,"top.t.a*","toggle_internal:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_internal:1->0",2) + // (t.a1 and t.a2) -%000004 output reg toggle_up; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 output reg toggle_up; + // CHECK_COVER(-1,"top.t.a*","toggle_up:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_up:1->0",2) + // (t.a1 and t.a2) always @ (posedge clk) begin toggle_internal <= toggle; @@ -181,10 +192,11 @@ clk, toggle_up ); - 000019 input clk; +~000010 input clk; -%000002 input toggle_up; - // CHECK_COVER(-1,"top.t.b1","toggle_up",2) +%000001 input toggle_up; + // CHECK_COVER(-1,"top.t.b1","toggle_up:0->1",1) + // CHECK_COVER(-2,"top.t.b1","toggle_up:1->0",1) /* verilator public_module */ @@ -203,8 +215,9 @@ // CHECK_COVER_MISSING(-1) // verilator coverage_on -%000002 input toggle; - // CHECK_COVER(-1,"top.t.o1","toggle",2) +%000001 input toggle; + // CHECK_COVER(-1,"top.t.o1","toggle:0->1",1) + // CHECK_COVER(-2,"top.t.o1","toggle:1->0",1) endmodule @@ -213,18 +226,18 @@ clk, toggle ); - 000019 input clk; -%000002 input toggle; +~000010 input clk; +%000001 input toggle; %000001 logic z; for (genvar i = 0; i < P; i++) begin -%000002 logic x; +%000001 logic x; always @ (posedge clk) begin x <= toggle; end for (genvar j = 0; j < 3; j++) begin -%000003 logic [2:0] y; +%000002 logic [2:0] y; always @ (negedge clk) begin y <= {toggle, ~toggle, 1'b1}; end @@ -240,6 +253,6 @@ input_struct ); - 000019 input str_logic input_struct; +~000010 input str_logic input_struct; endmodule diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v index 456df1d08..1bbdbf8b6 100644 --- a/test_regress/t/t_cover_toggle.v +++ b/test_regress/t/t_cover_toggle.v @@ -148,26 +148,37 @@ module alpha (/*AUTOARG*/ input clk; input toggle; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) + // CHECK_COVER(-1,"top.t.a*","toggle:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle:1->0",2) + // (t.a1 and t.a2) input [7:0] cyc_copy; - // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]",22) - // CHECK_COVER(-2,"top.t.a*","cyc_copy[1]",10) - // CHECK_COVER(-3,"top.t.a*","cyc_copy[2]",4) - // CHECK_COVER(-4,"top.t.a*","cyc_copy[3]",2) - // CHECK_COVER(-5,"top.t.a*","cyc_copy[4]",0) - // CHECK_COVER(-6,"top.t.a*","cyc_copy[5]",0) - // CHECK_COVER(-7,"top.t.a*","cyc_copy[6]",0) - // CHECK_COVER(-8,"top.t.a*","cyc_copy[7]",0) + // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]:0->1",12) + // CHECK_COVER(-2,"top.t.a*","cyc_copy[0]:1->0",10) + // CHECK_COVER(-3,"top.t.a*","cyc_copy[1]:0->1",6) + // CHECK_COVER(-4,"top.t.a*","cyc_copy[1]:1->0",4) + // CHECK_COVER(-5,"top.t.a*","cyc_copy[2]:0->1",2) + // CHECK_COVER(-6,"top.t.a*","cyc_copy[2]:1->0",2) + // CHECK_COVER(-7,"top.t.a*","cyc_copy[3]:0->1",2) + // CHECK_COVER(-8,"top.t.a*","cyc_copy[3]:1->0",0) + // CHECK_COVER(-9,"top.t.a*","cyc_copy[4]:0->1",0) + // CHECK_COVER(-10,"top.t.a*","cyc_copy[4]:1->0",0) + // CHECK_COVER(-11,"top.t.a*","cyc_copy[5]:0->1",0) + // CHECK_COVER(-12,"top.t.a*","cyc_copy[5]:1->0",0) + // CHECK_COVER(-13,"top.t.a*","cyc_copy[6]:0->1",0) + // CHECK_COVER(-14,"top.t.a*","cyc_copy[6]:1->0",0) + // CHECK_COVER(-15,"top.t.a*","cyc_copy[7]:0->1",0) + // CHECK_COVER(-16,"top.t.a*","cyc_copy[7]:1->0",0) reg toggle_internal; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) + // CHECK_COVER(-1,"top.t.a*","toggle_internal:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_internal:1->0",2) + // (t.a1 and t.a2) output reg toggle_up; - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) + // CHECK_COVER(-1,"top.t.a*","toggle_up:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_up:1->0",2) + // (t.a1 and t.a2) always @ (posedge clk) begin toggle_internal <= toggle; @@ -183,7 +194,8 @@ module beta (/*AUTOARG*/ input clk; input toggle_up; - // CHECK_COVER(-1,"top.t.b1","toggle_up",2) + // CHECK_COVER(-1,"top.t.b1","toggle_up:0->1",1) + // CHECK_COVER(-2,"top.t.b1","toggle_up:1->0",1) /* verilator public_module */ @@ -203,7 +215,8 @@ module off (/*AUTOARG*/ // verilator coverage_on input toggle; - // CHECK_COVER(-1,"top.t.o1","toggle",2) + // CHECK_COVER(-1,"top.t.o1","toggle:0->1",1) + // CHECK_COVER(-2,"top.t.o1","toggle:1->0",1) endmodule diff --git a/test_regress/t/t_cover_toggle__points.out b/test_regress/t/t_cover_toggle__points.out index 6dc8fc51d..87fcc8e72 100644 --- a/test_regress/t/t_cover_toggle__points.out +++ b/test_regress/t/t_cover_toggle__points.out @@ -12,8 +12,9 @@ clk, check_real, check_array_real, check_string ); - 000019 input clk; -+000019 point: comment=clk hier=top.t +~000010 input clk; ++000010 point: comment=clk:0->1 hier=top.t +-000009 point: comment=clk:1->0 hier=top.t input real check_real; // Check issue #2741 input real check_array_real [1:0]; input string check_string; // Check issue #2766 @@ -26,15 +27,19 @@ logic b; } str_t; -%000002 reg toggle; initial toggle='0; --000002 point: comment=toggle hier=top.t +%000001 reg toggle; initial toggle='0; +-000001 point: comment=toggle:0->1 hier=top.t +-000001 point: comment=toggle:1->0 hier=top.t -%000002 str_t stoggle; initial stoggle='0; --000002 point: comment=stoggle.b hier=top.t --000002 point: comment=stoggle.u.ua hier=top.t +%000001 str_t stoggle; initial stoggle='0; +-000001 point: comment=stoggle.b:0->1 hier=top.t +-000001 point: comment=stoggle.b:1->0 hier=top.t +-000001 point: comment=stoggle.u.ua:0->1 hier=top.t +-000001 point: comment=stoggle.u.ua:1->0 hier=top.t - 000019 str_logic strl; initial strl='0; -+000019 point: comment=strl.a hier=top.t +~000010 str_logic strl; initial strl='0; ++000010 point: comment=strl.a:0->1 hier=top.t +-000009 point: comment=strl.a:1->0 hier=top.t union { real val1; // TODO use bit [7:0] here @@ -43,24 +48,37 @@ const reg aconst = '0; -%000002 reg [1:0][1:0] ptoggle; initial ptoggle=0; --000002 point: comment=ptoggle[0][0] hier=top.t --000000 point: comment=ptoggle[0][1] hier=top.t --000000 point: comment=ptoggle[1][0] hier=top.t --000000 point: comment=ptoggle[1][1] hier=top.t +%000001 reg [1:0][1:0] ptoggle; initial ptoggle=0; +-000001 point: comment=ptoggle[0][0]:0->1 hier=top.t +-000001 point: comment=ptoggle[0][0]:1->0 hier=top.t +-000000 point: comment=ptoggle[0][1]:0->1 hier=top.t +-000000 point: comment=ptoggle[0][1]:1->0 hier=top.t +-000000 point: comment=ptoggle[1][0]:0->1 hier=top.t +-000000 point: comment=ptoggle[1][0]:1->0 hier=top.t +-000000 point: comment=ptoggle[1][1]:0->1 hier=top.t +-000000 point: comment=ptoggle[1][1]:1->0 hier=top.t integer cyc; initial cyc=1; -~000011 wire [7:0] cyc_copy = cyc[7:0]; -+000011 point: comment=cyc_copy[0] hier=top.t --000005 point: comment=cyc_copy[1] hier=top.t --000002 point: comment=cyc_copy[2] hier=top.t --000001 point: comment=cyc_copy[3] hier=top.t --000000 point: comment=cyc_copy[4] hier=top.t --000000 point: comment=cyc_copy[5] hier=top.t --000000 point: comment=cyc_copy[6] hier=top.t --000000 point: comment=cyc_copy[7] hier=top.t -%000002 wire toggle_up; --000002 point: comment=toggle_up hier=top.t +%000006 wire [7:0] cyc_copy = cyc[7:0]; +-000006 point: comment=cyc_copy[0]:0->1 hier=top.t +-000005 point: comment=cyc_copy[0]:1->0 hier=top.t +-000003 point: comment=cyc_copy[1]:0->1 hier=top.t +-000002 point: comment=cyc_copy[1]:1->0 hier=top.t +-000001 point: comment=cyc_copy[2]:0->1 hier=top.t +-000001 point: comment=cyc_copy[2]:1->0 hier=top.t +-000001 point: comment=cyc_copy[3]:0->1 hier=top.t +-000000 point: comment=cyc_copy[3]:1->0 hier=top.t +-000000 point: comment=cyc_copy[4]:0->1 hier=top.t +-000000 point: comment=cyc_copy[4]:1->0 hier=top.t +-000000 point: comment=cyc_copy[5]:0->1 hier=top.t +-000000 point: comment=cyc_copy[5]:1->0 hier=top.t +-000000 point: comment=cyc_copy[6]:0->1 hier=top.t +-000000 point: comment=cyc_copy[6]:1->0 hier=top.t +-000000 point: comment=cyc_copy[7]:0->1 hier=top.t +-000000 point: comment=cyc_copy[7]:1->0 hier=top.t +%000001 wire toggle_up; +-000001 point: comment=toggle_up:0->1 hier=top.t +-000001 point: comment=toggle_up:1->0 hier=top.t typedef struct { int q[$]; @@ -73,28 +91,48 @@ // verilator lint_on ASCRANGE bit [0:0] y; } str_bit_t; -%000002 str_bit_t str_bit; --000002 point: comment=str_bit.x[3] hier=top.t --000001 point: comment=str_bit.x[4] hier=top.t --000001 point: comment=str_bit.x[5] hier=top.t --000002 point: comment=str_bit.y[0] hier=top.t -%000002 str_bit_t [5:2] str_bit_arr; --000000 point: comment=str_bit_arr[2].x[3] hier=top.t --000000 point: comment=str_bit_arr[2].x[4] hier=top.t --000000 point: comment=str_bit_arr[2].x[5] hier=top.t --000000 point: comment=str_bit_arr[2].y[0] hier=top.t --000000 point: comment=str_bit_arr[3].x[3] hier=top.t --000000 point: comment=str_bit_arr[3].x[4] hier=top.t --000000 point: comment=str_bit_arr[3].x[5] hier=top.t --000000 point: comment=str_bit_arr[3].y[0] hier=top.t --000002 point: comment=str_bit_arr[4].x[3] hier=top.t --000001 point: comment=str_bit_arr[4].x[4] hier=top.t --000001 point: comment=str_bit_arr[4].x[5] hier=top.t --000000 point: comment=str_bit_arr[4].y[0] hier=top.t --000000 point: comment=str_bit_arr[5].x[3] hier=top.t --000000 point: comment=str_bit_arr[5].x[4] hier=top.t --000000 point: comment=str_bit_arr[5].x[5] hier=top.t --000000 point: comment=str_bit_arr[5].y[0] hier=top.t +%000001 str_bit_t str_bit; +-000001 point: comment=str_bit.x[3]:0->1 hier=top.t +-000001 point: comment=str_bit.x[3]:1->0 hier=top.t +-000001 point: comment=str_bit.x[4]:0->1 hier=top.t +-000000 point: comment=str_bit.x[4]:1->0 hier=top.t +-000001 point: comment=str_bit.x[5]:0->1 hier=top.t +-000000 point: comment=str_bit.x[5]:1->0 hier=top.t +-000001 point: comment=str_bit.y[0]:0->1 hier=top.t +-000001 point: comment=str_bit.y[0]:1->0 hier=top.t +%000001 str_bit_t [5:2] str_bit_arr; +-000000 point: comment=str_bit_arr[2].x[3]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[2].x[3]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[2].x[4]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[2].x[4]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[2].x[5]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[2].x[5]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[2].y[0]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[2].y[0]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[3].x[3]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[3].x[3]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[3].x[4]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[3].x[4]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[3].x[5]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[3].x[5]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[3].y[0]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[3].y[0]:1->0 hier=top.t +-000001 point: comment=str_bit_arr[4].x[3]:0->1 hier=top.t +-000001 point: comment=str_bit_arr[4].x[3]:1->0 hier=top.t +-000001 point: comment=str_bit_arr[4].x[4]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[4].x[4]:1->0 hier=top.t +-000001 point: comment=str_bit_arr[4].x[5]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[4].x[5]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[4].y[0]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[4].y[0]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[5].x[3]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[5].x[3]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[5].x[4]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[5].x[4]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[5].x[5]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[5].x[5]:1->0 hier=top.t +-000000 point: comment=str_bit_arr[5].y[0]:0->1 hier=top.t +-000000 point: comment=str_bit_arr[5].y[0]:1->0 hier=top.t assign strl.a = clk; @@ -139,30 +177,54 @@ %000001 reg [1:0] memory[121:110]; --000001 point: comment=memory[110][0] hier=top.t --000000 point: comment=memory[110][1] hier=top.t --000000 point: comment=memory[111][0] hier=top.t --000000 point: comment=memory[111][1] hier=top.t --000000 point: comment=memory[112][0] hier=top.t --000000 point: comment=memory[112][1] hier=top.t --000000 point: comment=memory[113][0] hier=top.t --000000 point: comment=memory[113][1] hier=top.t --000000 point: comment=memory[114][0] hier=top.t --000000 point: comment=memory[114][1] hier=top.t --000000 point: comment=memory[115][0] hier=top.t --000000 point: comment=memory[115][1] hier=top.t --000000 point: comment=memory[116][0] hier=top.t --000000 point: comment=memory[116][1] hier=top.t --000000 point: comment=memory[117][0] hier=top.t --000000 point: comment=memory[117][1] hier=top.t --000000 point: comment=memory[118][0] hier=top.t --000000 point: comment=memory[118][1] hier=top.t --000000 point: comment=memory[119][0] hier=top.t --000000 point: comment=memory[119][1] hier=top.t --000000 point: comment=memory[120][0] hier=top.t --000000 point: comment=memory[120][1] hier=top.t --000000 point: comment=memory[121][0] hier=top.t --000000 point: comment=memory[121][1] hier=top.t +-000001 point: comment=memory[110][0]:0->1 hier=top.t +-000000 point: comment=memory[110][0]:1->0 hier=top.t +-000000 point: comment=memory[110][1]:0->1 hier=top.t +-000000 point: comment=memory[110][1]:1->0 hier=top.t +-000000 point: comment=memory[111][0]:0->1 hier=top.t +-000000 point: comment=memory[111][0]:1->0 hier=top.t +-000000 point: comment=memory[111][1]:0->1 hier=top.t +-000000 point: comment=memory[111][1]:1->0 hier=top.t +-000000 point: comment=memory[112][0]:0->1 hier=top.t +-000000 point: comment=memory[112][0]:1->0 hier=top.t +-000000 point: comment=memory[112][1]:0->1 hier=top.t +-000000 point: comment=memory[112][1]:1->0 hier=top.t +-000000 point: comment=memory[113][0]:0->1 hier=top.t +-000000 point: comment=memory[113][0]:1->0 hier=top.t +-000000 point: comment=memory[113][1]:0->1 hier=top.t +-000000 point: comment=memory[113][1]:1->0 hier=top.t +-000000 point: comment=memory[114][0]:0->1 hier=top.t +-000000 point: comment=memory[114][0]:1->0 hier=top.t +-000000 point: comment=memory[114][1]:0->1 hier=top.t +-000000 point: comment=memory[114][1]:1->0 hier=top.t +-000000 point: comment=memory[115][0]:0->1 hier=top.t +-000000 point: comment=memory[115][0]:1->0 hier=top.t +-000000 point: comment=memory[115][1]:0->1 hier=top.t +-000000 point: comment=memory[115][1]:1->0 hier=top.t +-000000 point: comment=memory[116][0]:0->1 hier=top.t +-000000 point: comment=memory[116][0]:1->0 hier=top.t +-000000 point: comment=memory[116][1]:0->1 hier=top.t +-000000 point: comment=memory[116][1]:1->0 hier=top.t +-000000 point: comment=memory[117][0]:0->1 hier=top.t +-000000 point: comment=memory[117][0]:1->0 hier=top.t +-000000 point: comment=memory[117][1]:0->1 hier=top.t +-000000 point: comment=memory[117][1]:1->0 hier=top.t +-000000 point: comment=memory[118][0]:0->1 hier=top.t +-000000 point: comment=memory[118][0]:1->0 hier=top.t +-000000 point: comment=memory[118][1]:0->1 hier=top.t +-000000 point: comment=memory[118][1]:1->0 hier=top.t +-000000 point: comment=memory[119][0]:0->1 hier=top.t +-000000 point: comment=memory[119][0]:1->0 hier=top.t +-000000 point: comment=memory[119][1]:0->1 hier=top.t +-000000 point: comment=memory[119][1]:1->0 hier=top.t +-000000 point: comment=memory[120][0]:0->1 hier=top.t +-000000 point: comment=memory[120][0]:1->0 hier=top.t +-000000 point: comment=memory[120][1]:0->1 hier=top.t +-000000 point: comment=memory[120][1]:1->0 hier=top.t +-000000 point: comment=memory[121][0]:0->1 hier=top.t +-000000 point: comment=memory[121][0]:1->0 hier=top.t +-000000 point: comment=memory[121][1]:0->1 hier=top.t +-000000 point: comment=memory[121][1]:1->0 hier=top.t wire [1023:0] largeish = {992'h0, cyc}; // CHECK_COVER_MISSING(-1) @@ -208,41 +270,64 @@ // t.a1 and t.a2 collapse to a count of 2 - 000038 input clk; -+000038 point: comment=clk hier=top.t.a* + 000020 input clk; ++000020 point: comment=clk:0->1 hier=top.t.a* ++000018 point: comment=clk:1->0 hier=top.t.a* -%000004 input toggle; --000004 point: comment=toggle hier=top.t.a* - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 input toggle; +-000002 point: comment=toggle:0->1 hier=top.t.a* +-000002 point: comment=toggle:1->0 hier=top.t.a* + // CHECK_COVER(-1,"top.t.a*","toggle:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle:1->0",2) + // (t.a1 and t.a2) -~000022 input [7:0] cyc_copy; -+000022 point: comment=cyc_copy[0] hier=top.t.a* -+000010 point: comment=cyc_copy[1] hier=top.t.a* --000004 point: comment=cyc_copy[2] hier=top.t.a* --000002 point: comment=cyc_copy[3] hier=top.t.a* --000000 point: comment=cyc_copy[4] hier=top.t.a* --000000 point: comment=cyc_copy[5] hier=top.t.a* --000000 point: comment=cyc_copy[6] hier=top.t.a* --000000 point: comment=cyc_copy[7] hier=top.t.a* - // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]",22) - // CHECK_COVER(-2,"top.t.a*","cyc_copy[1]",10) - // CHECK_COVER(-3,"top.t.a*","cyc_copy[2]",4) - // CHECK_COVER(-4,"top.t.a*","cyc_copy[3]",2) - // CHECK_COVER(-5,"top.t.a*","cyc_copy[4]",0) - // CHECK_COVER(-6,"top.t.a*","cyc_copy[5]",0) - // CHECK_COVER(-7,"top.t.a*","cyc_copy[6]",0) - // CHECK_COVER(-8,"top.t.a*","cyc_copy[7]",0) +~000012 input [7:0] cyc_copy; ++000012 point: comment=cyc_copy[0]:0->1 hier=top.t.a* ++000010 point: comment=cyc_copy[0]:1->0 hier=top.t.a* +-000006 point: comment=cyc_copy[1]:0->1 hier=top.t.a* +-000004 point: comment=cyc_copy[1]:1->0 hier=top.t.a* +-000002 point: comment=cyc_copy[2]:0->1 hier=top.t.a* +-000002 point: comment=cyc_copy[2]:1->0 hier=top.t.a* +-000002 point: comment=cyc_copy[3]:0->1 hier=top.t.a* +-000000 point: comment=cyc_copy[3]:1->0 hier=top.t.a* +-000000 point: comment=cyc_copy[4]:0->1 hier=top.t.a* +-000000 point: comment=cyc_copy[4]:1->0 hier=top.t.a* +-000000 point: comment=cyc_copy[5]:0->1 hier=top.t.a* +-000000 point: comment=cyc_copy[5]:1->0 hier=top.t.a* +-000000 point: comment=cyc_copy[6]:0->1 hier=top.t.a* +-000000 point: comment=cyc_copy[6]:1->0 hier=top.t.a* +-000000 point: comment=cyc_copy[7]:0->1 hier=top.t.a* +-000000 point: comment=cyc_copy[7]:1->0 hier=top.t.a* + // CHECK_COVER(-1,"top.t.a*","cyc_copy[0]:0->1",12) + // CHECK_COVER(-2,"top.t.a*","cyc_copy[0]:1->0",10) + // CHECK_COVER(-3,"top.t.a*","cyc_copy[1]:0->1",6) + // CHECK_COVER(-4,"top.t.a*","cyc_copy[1]:1->0",4) + // CHECK_COVER(-5,"top.t.a*","cyc_copy[2]:0->1",2) + // CHECK_COVER(-6,"top.t.a*","cyc_copy[2]:1->0",2) + // CHECK_COVER(-7,"top.t.a*","cyc_copy[3]:0->1",2) + // CHECK_COVER(-8,"top.t.a*","cyc_copy[3]:1->0",0) + // CHECK_COVER(-9,"top.t.a*","cyc_copy[4]:0->1",0) + // CHECK_COVER(-10,"top.t.a*","cyc_copy[4]:1->0",0) + // CHECK_COVER(-11,"top.t.a*","cyc_copy[5]:0->1",0) + // CHECK_COVER(-12,"top.t.a*","cyc_copy[5]:1->0",0) + // CHECK_COVER(-13,"top.t.a*","cyc_copy[6]:0->1",0) + // CHECK_COVER(-14,"top.t.a*","cyc_copy[6]:1->0",0) + // CHECK_COVER(-15,"top.t.a*","cyc_copy[7]:0->1",0) + // CHECK_COVER(-16,"top.t.a*","cyc_copy[7]:1->0",0) -%000004 reg toggle_internal; --000004 point: comment=toggle_internal hier=top.t.a* - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 reg toggle_internal; +-000002 point: comment=toggle_internal:0->1 hier=top.t.a* +-000002 point: comment=toggle_internal:1->0 hier=top.t.a* + // CHECK_COVER(-1,"top.t.a*","toggle_internal:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_internal:1->0",2) + // (t.a1 and t.a2) -%000004 output reg toggle_up; --000004 point: comment=toggle_up hier=top.t.a* - // CHECK_COVER(-1,"top.t.a*",4) - // 2 edges * (t.a1 and t.a2) +%000002 output reg toggle_up; +-000002 point: comment=toggle_up:0->1 hier=top.t.a* +-000002 point: comment=toggle_up:1->0 hier=top.t.a* + // CHECK_COVER(-1,"top.t.a*","toggle_up:0->1",2) + // CHECK_COVER(-2,"top.t.a*","toggle_up:1->0",2) + // (t.a1 and t.a2) always @ (posedge clk) begin toggle_internal <= toggle; @@ -255,12 +340,15 @@ clk, toggle_up ); - 000019 input clk; -+000019 point: comment=clk hier=top.t.b1 +~000010 input clk; ++000010 point: comment=clk:0->1 hier=top.t.b1 +-000009 point: comment=clk:1->0 hier=top.t.b1 -%000002 input toggle_up; --000002 point: comment=toggle_up hier=top.t.b1 - // CHECK_COVER(-1,"top.t.b1","toggle_up",2) +%000001 input toggle_up; +-000001 point: comment=toggle_up:0->1 hier=top.t.b1 +-000001 point: comment=toggle_up:1->0 hier=top.t.b1 + // CHECK_COVER(-1,"top.t.b1","toggle_up:0->1",1) + // CHECK_COVER(-2,"top.t.b1","toggle_up:1->0",1) /* verilator public_module */ @@ -279,9 +367,11 @@ // CHECK_COVER_MISSING(-1) // verilator coverage_on -%000002 input toggle; --000002 point: comment=toggle hier=top.t.o1 - // CHECK_COVER(-1,"top.t.o1","toggle",2) +%000001 input toggle; +-000001 point: comment=toggle:0->1 hier=top.t.o1 +-000001 point: comment=toggle:1->0 hier=top.t.o1 + // CHECK_COVER(-1,"top.t.o1","toggle:0->1",1) + // CHECK_COVER(-2,"top.t.o1","toggle:1->0",1) endmodule @@ -290,54 +380,90 @@ clk, toggle ); - 000019 input clk; -+000019 point: comment=clk hier=top.t.p2 -+000019 point: comment=clk hier=top.t.p1 -%000002 input toggle; --000002 point: comment=toggle hier=top.t.p2 --000002 point: comment=toggle hier=top.t.p1 +~000010 input clk; ++000010 point: comment=clk:0->1 hier=top.t.p2 +-000009 point: comment=clk:1->0 hier=top.t.p2 ++000010 point: comment=clk:0->1 hier=top.t.p1 +-000009 point: comment=clk:1->0 hier=top.t.p1 +%000001 input toggle; +-000001 point: comment=toggle:0->1 hier=top.t.p2 +-000001 point: comment=toggle:1->0 hier=top.t.p2 +-000001 point: comment=toggle:0->1 hier=top.t.p1 +-000001 point: comment=toggle:1->0 hier=top.t.p1 %000001 logic z; --000001 point: comment=z hier=top.t.p2 --000000 point: comment=z hier=top.t.p1 +-000001 point: comment=z:0->1 hier=top.t.p2 +-000000 point: comment=z:1->0 hier=top.t.p2 +-000000 point: comment=z:0->1 hier=top.t.p1 +-000000 point: comment=z:1->0 hier=top.t.p1 for (genvar i = 0; i < P; i++) begin -%000002 logic x; --000002 point: comment=genblk1[0].x hier=top.t.p2 --000002 point: comment=genblk1[1].x hier=top.t.p2 --000002 point: comment=genblk1[0].x hier=top.t.p1 +%000001 logic x; +-000001 point: comment=genblk1[0].x:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].x:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].x:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].x:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].x:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].x:1->0 hier=top.t.p1 always @ (posedge clk) begin x <= toggle; end for (genvar j = 0; j < 3; j++) begin -%000003 logic [2:0] y; --000001 point: comment=genblk1[0].genblk1[0].y[0] hier=top.t.p2 --000003 point: comment=genblk1[0].genblk1[0].y[1] hier=top.t.p2 --000002 point: comment=genblk1[0].genblk1[0].y[2] hier=top.t.p2 --000001 point: comment=genblk1[0].genblk1[1].y[0] hier=top.t.p2 --000003 point: comment=genblk1[0].genblk1[1].y[1] hier=top.t.p2 --000002 point: comment=genblk1[0].genblk1[1].y[2] hier=top.t.p2 --000001 point: comment=genblk1[0].genblk1[2].y[0] hier=top.t.p2 --000003 point: comment=genblk1[0].genblk1[2].y[1] hier=top.t.p2 --000002 point: comment=genblk1[0].genblk1[2].y[2] hier=top.t.p2 --000001 point: comment=genblk1[1].genblk1[0].y[0] hier=top.t.p2 --000003 point: comment=genblk1[1].genblk1[0].y[1] hier=top.t.p2 --000002 point: comment=genblk1[1].genblk1[0].y[2] hier=top.t.p2 --000001 point: comment=genblk1[1].genblk1[1].y[0] hier=top.t.p2 --000003 point: comment=genblk1[1].genblk1[1].y[1] hier=top.t.p2 --000002 point: comment=genblk1[1].genblk1[1].y[2] hier=top.t.p2 --000001 point: comment=genblk1[1].genblk1[2].y[0] hier=top.t.p2 --000003 point: comment=genblk1[1].genblk1[2].y[1] hier=top.t.p2 --000002 point: comment=genblk1[1].genblk1[2].y[2] hier=top.t.p2 --000001 point: comment=genblk1[0].genblk1[0].y[0] hier=top.t.p1 --000003 point: comment=genblk1[0].genblk1[0].y[1] hier=top.t.p1 --000002 point: comment=genblk1[0].genblk1[0].y[2] hier=top.t.p1 --000001 point: comment=genblk1[0].genblk1[1].y[0] hier=top.t.p1 --000003 point: comment=genblk1[0].genblk1[1].y[1] hier=top.t.p1 --000002 point: comment=genblk1[0].genblk1[1].y[2] hier=top.t.p1 --000001 point: comment=genblk1[0].genblk1[2].y[0] hier=top.t.p1 --000003 point: comment=genblk1[0].genblk1[2].y[1] hier=top.t.p1 --000002 point: comment=genblk1[0].genblk1[2].y[2] hier=top.t.p1 +%000002 logic [2:0] y; +-000001 point: comment=genblk1[0].genblk1[0].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[0].genblk1[0].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[0].genblk1[0].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[0].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[0].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[0].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[1].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[0].genblk1[1].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[0].genblk1[1].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[1].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[1].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[1].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[2].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[0].genblk1[2].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[0].genblk1[2].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[2].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[2].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[2].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[0].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[1].genblk1[0].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[1].genblk1[0].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[0].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[0].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[0].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[1].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[1].genblk1[1].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[1].genblk1[1].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[1].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[1].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[1].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[2].y[0]:0->1 hier=top.t.p2 +-000000 point: comment=genblk1[1].genblk1[2].y[0]:1->0 hier=top.t.p2 +-000002 point: comment=genblk1[1].genblk1[2].y[1]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[2].y[1]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[2].y[2]:0->1 hier=top.t.p2 +-000001 point: comment=genblk1[1].genblk1[2].y[2]:1->0 hier=top.t.p2 +-000001 point: comment=genblk1[0].genblk1[0].y[0]:0->1 hier=top.t.p1 +-000000 point: comment=genblk1[0].genblk1[0].y[0]:1->0 hier=top.t.p1 +-000002 point: comment=genblk1[0].genblk1[0].y[1]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[0].y[1]:1->0 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[0].y[2]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[0].y[2]:1->0 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[1].y[0]:0->1 hier=top.t.p1 +-000000 point: comment=genblk1[0].genblk1[1].y[0]:1->0 hier=top.t.p1 +-000002 point: comment=genblk1[0].genblk1[1].y[1]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[1].y[1]:1->0 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[1].y[2]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[1].y[2]:1->0 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[2].y[0]:0->1 hier=top.t.p1 +-000000 point: comment=genblk1[0].genblk1[2].y[0]:1->0 hier=top.t.p1 +-000002 point: comment=genblk1[0].genblk1[2].y[1]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[2].y[1]:1->0 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[2].y[2]:0->1 hier=top.t.p1 +-000001 point: comment=genblk1[0].genblk1[2].y[2]:1->0 hier=top.t.p1 always @ (negedge clk) begin y <= {toggle, ~toggle, 1'b1}; end @@ -353,7 +479,8 @@ input_struct ); - 000019 input str_logic input_struct; -+000019 point: comment=input_struct.a hier=top.t.i_mod_struct +~000010 input str_logic input_struct; ++000010 point: comment=input_struct.a:0->1 hier=top.t.i_mod_struct +-000009 point: comment=input_struct.a:1->0 hier=top.t.i_mod_struct endmodule diff --git a/test_regress/t/t_cover_toggle_min.info.out b/test_regress/t/t_cover_toggle_min.info.out index a5581bcd2..975b502e0 100644 --- a/test_regress/t/t_cover_toggle_min.info.out +++ b/test_regress/t/t_cover_toggle_min.info.out @@ -3,12 +3,18 @@ SF:t/t_cover_toggle_min.v DA:10,1 BRDA:10,0,0,1 BRDA:10,0,1,0 +BRDA:10,0,2,0 +BRDA:10,0,3,0 DA:11,1 BRDA:11,0,0,0 -BRDA:11,0,1,1 -DA:12,2 -BRDA:12,0,0,2 +BRDA:11,0,1,0 +BRDA:11,0,2,1 +BRDA:11,0,3,0 +DA:12,1 +BRDA:12,0,0,1 BRDA:12,0,1,1 -BRF:6 +BRDA:12,0,2,1 +BRDA:12,0,3,0 +BRF:12 BRH:0 end_of_record diff --git a/test_regress/t/t_wrapper_context__top0.dat.out b/test_regress/t/t_wrapper_context__top0.dat.out index d4620c660..734b39e8d 100644 --- a/test_regress/t/t_wrapper_context__top0.dat.out +++ b/test_regress/t/t_wrapper_context__top0.dat.out @@ -1,72 +1,140 @@ # SystemC::Coverage-3 -C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclkhtop0.top' 21 -C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporsthtop0.top' 2 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]htop0.top' 1 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]htop0.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]htop0.top' 0 -C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostophtop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]htop0.top' 10 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]htop0.top' 5 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]htop0.top' 2 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]htop0.top' 1 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]htop0.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]htop0.top' 0 -C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_ohtop0.top' 1 +C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclk:0->1htop0.top' 11 +C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclk:1->0htop0.top' 10 +C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporst:0->1htop0.top' 1 +C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporst:1->0htop0.top' 1 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]:0->1htop0.top' 1 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostop:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostop:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]:0->1htop0.top' 5 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]:1->0htop0.top' 5 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]:0->1htop0.top' 3 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]:1->0htop0.top' 2 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]:0->1htop0.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]:1->0htop0.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]:0->1htop0.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]:0->1htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]:1->0htop0.top' 0 +C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_o:0->1htop0.top' 1 +C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_o:1->0htop0.top' 0 C 'ft/t_wrapper_context.vl22n4tlinepagev_line/topoblockS22,25-29htop0.top' 1 C 'ft/t_wrapper_context.vl32n4tlinepagev_line/topoblockS32htop0.top' 11 C 'ft/t_wrapper_context.vl33n7tbranchpagev_branch/topoifS33-34htop0.top' 1 diff --git a/test_regress/t/t_wrapper_context__top1.dat.out b/test_regress/t/t_wrapper_context__top1.dat.out index 4b335614e..c4f90612a 100644 --- a/test_regress/t/t_wrapper_context__top1.dat.out +++ b/test_regress/t/t_wrapper_context__top1.dat.out @@ -1,72 +1,140 @@ # SystemC::Coverage-3 -C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclkhtop1.top' 11 -C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporsthtop1.top' 2 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]htop1.top' 0 -C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]htop1.top' 0 -C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostophtop1.top' 1 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]htop1.top' 5 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]htop1.top' 2 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]htop1.top' 1 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]htop1.top' 0 -C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]htop1.top' 0 -C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_ohtop1.top' 1 +C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclk:0->1htop1.top' 6 +C 'ft/t_wrapper_context.vl14n22ttogglepagev_toggle/topoclk:1->0htop1.top' 5 +C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporst:0->1htop1.top' 1 +C 'ft/t_wrapper_context.vl15n22ttogglepagev_toggle/toporst:1->0htop1.top' 1 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[0]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[10]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[11]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[12]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[13]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[14]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[15]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[16]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[17]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[18]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[19]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[1]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[20]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[21]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[22]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[23]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[24]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[25]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[26]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[27]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[28]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[29]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[2]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[30]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[31]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[3]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[4]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[5]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[6]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[7]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[8]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22ttogglepagev_toggle/topotrace_number[9]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostop:0->1htop1.top' 1 +C 'ft/t_wrapper_context.vl17n22ttogglepagev_toggle/topostop:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]:0->1htop1.top' 3 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[0]:1->0htop1.top' 2 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[10]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[11]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[12]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[13]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[14]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[15]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[16]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[17]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[18]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[19]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]:0->1htop1.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[1]:1->0htop1.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[20]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[21]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[22]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[23]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[24]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[25]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[26]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[27]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[28]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[29]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]:0->1htop1.top' 1 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[2]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[30]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[31]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[3]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[4]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[5]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[6]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[7]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[8]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]:0->1htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22ttogglepagev_toggle/topocounter[9]:1->0htop1.top' 0 +C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_o:0->1htop1.top' 1 +C 'ft/t_wrapper_context.vl19n22ttogglepagev_toggle/topodone_o:1->0htop1.top' 0 C 'ft/t_wrapper_context.vl22n4tlinepagev_line/topoblockS22,25-29htop1.top' 1 C 'ft/t_wrapper_context.vl32n4tlinepagev_line/topoblockS32htop1.top' 6 C 'ft/t_wrapper_context.vl33n7tbranchpagev_branch/topoifS33-34htop1.top' 1 From 11667160f2a1a36bb4123be7e6077a9d4aaeea67 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Wed, 20 Aug 2025 17:23:16 +0200 Subject: [PATCH 199/252] Fix static vars under member select (#6313) --- src/V3WidthCommit.cpp | 19 +++- test_regress/t/t_assigndly_deep_ref.py | 18 ++++ test_regress/t/t_assigndly_deep_ref.v | 75 ++++++++++++++++ test_regress/t/t_assigndly_deep_ref_array.py | 18 ++++ test_regress/t/t_assigndly_deep_ref_array.v | 93 ++++++++++++++++++++ 5 files changed, 221 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_assigndly_deep_ref.py create mode 100644 test_regress/t/t_assigndly_deep_ref.v create mode 100755 test_regress/t/t_assigndly_deep_ref_array.py create mode 100644 test_regress/t/t_assigndly_deep_ref_array.v diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 20cc4677d..53031bf2c 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -44,6 +44,7 @@ class WidthCommitVisitor final : public VNVisitor { AstNodeModule* m_modp = nullptr; std::string m_contNba; // In continuous- or non-blocking assignment VMemberMap m_memberMap; // Member names cached for fast lookup + bool m_underSel = false; // Whether is currently under AstMemberSel or AstSel public: // METHODS @@ -126,7 +127,9 @@ private: } } void varLifetimeCheck(AstNode* nodep, AstVar* varp) { - if (!m_contNba.empty()) { + // Skip if we are under a member select (lhs of a dot) + // We don't care about lifetime of anything else than rhs of a dot + if (!m_underSel && !m_contNba.empty()) { std::string varType; const AstNodeDType* const varDtp = varp->dtypep()->skipRefp(); if (varp->lifetime().isAutomatic() && !VN_IS(varDtp, IfaceRefDType) @@ -368,7 +371,11 @@ private: classEncapCheck(nodep, nodep->taskp(), VN_CAST(nodep->classOrPackagep(), Class)); } void visit(AstMemberSel* nodep) override { - iterateChildren(nodep); + { + VL_RESTORER(m_underSel); + m_underSel = true; + iterateChildren(nodep); + } editDType(nodep); if (auto* const classrefp = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType)) { classEncapCheck(nodep, nodep->varp(), classrefp->classp()); @@ -388,6 +395,14 @@ private: // This check could go anywhere after V3Param nodep->v3fatalSrc("Presels should have been removed before this point"); } + void visit(AstSel* nodep) override { + { + VL_RESTORER(m_underSel); + m_underSel = true; + iterateChildren(nodep); + } + editDType(nodep); + } void visit(AstNode* nodep) override { iterateChildren(nodep); editDType(nodep); diff --git a/test_regress/t/t_assigndly_deep_ref.py b/test_regress/t/t_assigndly_deep_ref.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_assigndly_deep_ref.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assigndly_deep_ref.v b/test_regress/t/t_assigndly_deep_ref.v new file mode 100644 index 000000000..643b08226 --- /dev/null +++ b/test_regress/t/t_assigndly_deep_ref.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface Iface; + bit clk; + int x; + + clocking cb @(posedge clk); + default input #0 output #0; + inout x; + endclocking +endinterface + +class Foo; + virtual Iface iface; + function new(virtual Iface tmp); + iface = tmp; + endfunction + task update(virtual Iface tmp); + iface = tmp; + endtask +endclass + +class Bar; + Foo foo; + function new(Foo tmp); + foo = tmp; + endfunction + task update(Foo tmp); + foo = tmp; + endtask + task assignment(); + foo.iface.cb.x <= 8; + endtask +endclass + +module t; + Iface iface(); + Iface iface2(); + + task clockSome(); + #2; + iface.clk = ~iface.clk; + iface2.clk = ~iface2.clk; + #2; + iface.clk = ~iface.clk; + iface2.clk = ~iface2.clk; + endtask + + initial begin + Foo foo = new(iface); + Foo foo2 = new(iface2); + Bar bar = new(foo); + clockSome(); + if (iface.x != 0) $stop; + if (iface2.x != 0) $stop; + bar.assignment(); + clockSome(); + if (iface.x != 8) $stop; + if (iface2.x != 0) $stop; + foo.update(iface2); + clockSome(); + if (iface.x != 8) $stop; + if (iface2.x != 0) $stop; + bar.update(foo2); + clockSome(); + if (iface.x != 8) $stop; + if (iface2.x != 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_assigndly_deep_ref_array.py b/test_regress/t/t_assigndly_deep_ref_array.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_assigndly_deep_ref_array.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assigndly_deep_ref_array.v b/test_regress/t/t_assigndly_deep_ref_array.v new file mode 100644 index 000000000..4bf3eb9ce --- /dev/null +++ b/test_regress/t/t_assigndly_deep_ref_array.v @@ -0,0 +1,93 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface Iface; + bit clk; + int x[2:0]; + + clocking cb @(posedge clk); + default input #0 output #0; + inout x; + endclocking +endinterface + +class Foo; + virtual Iface iface; + int index = 0; + function new(virtual Iface tmp); + iface = tmp; + endfunction + task update(virtual Iface tmp); + iface = tmp; + endtask + task update_index(int i); + index = i; + endtask +endclass + +class Bar; + Foo foo; + function new(Foo tmp); + foo = tmp; + endfunction + task update(Foo tmp); + foo = tmp; + endtask + task assignment(); + foo.iface.cb.x[foo.index] <= 8; + endtask +endclass + +module t; + Iface iface(); + Iface iface2(); + + task clockSome(); + #2; + iface.clk = ~iface.clk; + iface2.clk = ~iface2.clk; + #2; + iface.clk = ~iface.clk; + iface2.clk = ~iface2.clk; + endtask + + initial begin + Foo foo = new(iface); + Foo foo2 = new(iface2); + Bar bar = new(foo); + clockSome(); + if (iface.x[0] != 0) $stop; + if (iface.x[1] != 0) $stop; + if (iface2.x[0] != 0) $stop; + if (iface2.x[1] != 0) $stop; + bar.assignment(); + clockSome(); + if (iface.x[0] != 8) $stop; + if (iface.x[1] != 0) $stop; + if (iface2.x[0] != 0) $stop; + if (iface2.x[1] != 0) $stop; + foo.update_index(1); + clockSome(); + if (iface.x[0] != 8) $stop; + if (iface.x[1] != 0) $stop; + if (iface2.x[0] != 0) $stop; + if (iface2.x[1] != 0) $stop; + foo.update(iface2); + clockSome(); + if (iface.x[0] != 8) $stop; + if (iface.x[1] != 0) $stop; + if (iface2.x[0] != 0) $stop; + if (iface2.x[1] != 0) $stop; + bar.update(foo2); + clockSome(); + if (iface.x[0] != 8) $stop; + if (iface.x[1] != 0) $stop; + if (iface2.x[0] != 0) $stop; + if (iface2.x[1] != 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 3d3462b209d1cdc56bd792aa56f014ef437e5ca3 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 20 Aug 2025 18:21:07 +0200 Subject: [PATCH 200/252] Support disabling a fork from within that fork (#6314) --- src/V3LinkJump.cpp | 22 +++++++++++++--------- test_regress/t/t_disable.out | 5 +---- test_regress/t/t_disable_inside.py | 18 ++++++++++++++++++ test_regress/t/t_disable_inside.v | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 13 deletions(-) create mode 100755 test_regress/t/t_disable_inside.py create mode 100644 test_regress/t/t_disable_inside.v diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index e572b87fb..fe7e640c8 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -180,16 +180,12 @@ class LinkJumpVisitor final : public VNVisitor { fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; } void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { - // The support is limited only to disabling a fork from outside that fork. - // It utilizes the process::kill()` method. For each `disable` a queue of processes is - // declared. At the beginning of each fork that can be disabled, its process handle is - // pushed to the queue. `disable` statement is replaced with calling `kill()` method on - // each element of the queue. + // The support utilizes the process::kill()` method. For each `disable` a queue of + // processes is declared. At the beginning of each fork that can be disabled, its process + // handle is pushed to the queue. `disable` statement is replaced with calling `kill()` + // method on each element of the queue. FileLine* const fl = nodep->fileline(); const std::string targetName = nodep->targetp()->name(); - if (existsBlockAbove(targetName)) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork"); - } if (m_ftaskp) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); } @@ -222,7 +218,15 @@ class LinkJumpVisitor final : public VNVisitor { = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), new AstArg{fl, "", queueRefp}}; killQueueCall->classOrPackagep(processClassp); - nodep->addNextHere(new AstStmtExpr{fl, killQueueCall}); + AstStmtExpr* const killStmtp = new AstStmtExpr{fl, killQueueCall}; + nodep->addNextHere(killStmtp); + if (existsBlockAbove(targetName)) { + // process::kill doesn't kill the current process immediately, because it is in the + // running state. Since the current process has to be terminated immediately, we jump + // at the end of the fork that is being disabled + AstJumpBlock* const jumpBlockp = getJumpBlock(nodep->targetp(), false); + killStmtp->addNextHere(new AstJumpGo{fl, jumpBlockp}); + } } static bool directlyUnderFork(const AstNode* const nodep) { if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp()); diff --git a/test_regress/t/t_disable.out b/test_regress/t/t_disable.out index a42b835d4..a70dd874c 100644 --- a/test_regress/t/t_disable.out +++ b/test_regress/t/t_disable.out @@ -1,8 +1,5 @@ -%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork from within same fork - 11 | disable foo; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error-UNSUPPORTED: t/t_disable.v:17:13: Unsupported: disabling block that contains a fork 17 | disable forked; | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_disable_inside.py b/test_regress/t/t_disable_inside.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_inside.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_inside.v b/test_regress/t/t_disable_inside.v new file mode 100644 index 000000000..04b74b3bf --- /dev/null +++ b/test_regress/t/t_disable_inside.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t ( /*AUTOARG*/); + initial begin + int x = 0; + fork : fork_blk + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk; + #2; + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 67da797816bbef02562639dc4f3cbfb8c2c0bddf Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 20 Aug 2025 18:21:24 +0100 Subject: [PATCH 201/252] Internals: Cleanup cppcheck warnings (#6317) `make cppcheck-4` is now clean. --- src/V3Active.cpp | 2 ++ src/V3AssertPre.cpp | 1 + src/V3AstNodes.cpp | 7 ++-- src/V3Cast.cpp | 1 + src/V3Combine.cpp | 1 + src/V3Const.cpp | 44 ++++++++++++++++--------- src/V3Dead.cpp | 3 ++ src/V3Delayed.cpp | 4 ++- src/V3Dfg.cpp | 61 +++++++++++++++++++---------------- src/V3Dfg.h | 38 +++++++++++----------- src/V3DfgBreakCycles.cpp | 51 +++++++++++++++-------------- src/V3DfgCache.h | 8 ++--- src/V3DfgColorSCCs.cpp | 2 +- src/V3DfgContext.h | 4 +-- src/V3DfgDecomposition.cpp | 15 +++++---- src/V3DfgDfgToAst.cpp | 4 +-- src/V3DfgOptimizer.cpp | 4 ++- src/V3DfgPasses.cpp | 10 +++--- src/V3DfgPeephole.cpp | 6 ++-- src/V3DfgSynthesize.cpp | 16 +++++---- src/V3DfgVertices.h | 4 ++- src/V3Error.h | 20 ++++++++---- src/V3File.h | 4 +++ src/V3Hash.h | 11 ++++--- src/V3PairingHeap.h | 2 +- src/V3Simulate.h | 8 ++--- src/cppcheck-suppressions.txt | 5 +-- 27 files changed, 195 insertions(+), 141 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 3497ede5e..513a05234 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -97,6 +97,7 @@ protected: result = vertexp->user(); break; case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings) + // cppcheck-suppress constVariableReference for (V3GraphEdge& edge : vertexp->outEdges()) { if (latchCheckInternal(castVertexp(edge.top()))) { result = true; @@ -128,6 +129,7 @@ public: } // Clear out userp field of referenced outputs on destruction // (occurs at the end of each combinational always block) + // cppcheck-suppress duplInheritedMember void clear() { m_outputs.clear(); // Calling base class clear will unlink & delete all edges & vertices diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index bcaf91c1e..7b6a21bc0 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -114,6 +114,7 @@ private: const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, propp->stmtsp()); for (const auto& tconnect : tconnects) { const AstVar* const portp = tconnect.first; + // cppcheck-suppress constVariablePointer // 'exprp' unlinked below AstArg* const argp = tconnect.second; AstNode* const pinp = argp->exprp()->unlinkFrBack(); replaceVarRefsWithExprRecurse(propExprp, portp, pinp); diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 2cea4e39b..014b1f155 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -87,6 +87,7 @@ bool AstNodeFTaskRef::isPure() { } bool AstNodeFTaskRef::getPurityRecurse() const { + // cppcheck-suppress shadowFunction AstNodeFTask* const taskp = this->taskp(); // Unlinked yet, so treat as impure if (!taskp) return false; @@ -1098,6 +1099,7 @@ std::pair AstNodeDType::dimensions(bool includeBasic) const int AstNodeDType::widthPow2() const { // I.e. width 30 returns 32, width 32 returns 32. + // cppcheck-suppress shadowFunction const uint32_t width = this->width(); for (int p2 = 30; p2 >= 0; p2--) { if (width > (1UL << p2)) return (1UL << (p2 + 1)); @@ -1374,6 +1376,7 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, return newp; } +// cppcheck-suppress duplInheritedMember AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) { const VBasicTypeKey key{nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(), nodep->nrange()}; @@ -1462,7 +1465,7 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) { UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const"); } // Try to find an existing table with the same content - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable const V3Hash hash = V3Hasher::uncachedHash(initp); const auto& er = m_tables.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { @@ -1492,7 +1495,7 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) { AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { // Try to find an existing constant with the same value - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable const V3Hash hash = initp->num().toHash(); const auto& er = m_consts.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 4839b8d04..c18d17fc0 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -85,6 +85,7 @@ class CastVisitor final : public VNVisitor { if (!nodep->isNull()) insertCast(nodep, castSize(nodep->backp())); } } + // cppcheck-suppress constParameterPointer // lhsp might be changed void ensureLower32Cast(AstCCast* nodep) { // If we have uint64 = CAST(uint64(x)) then the upcasting // really needs to be CAST(uint64(CAST(uint32(x))). diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index 6fea6c4c9..8c7bce596 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -150,6 +150,7 @@ class CombineVisitor final : VNVisitor { return replaced; } + // cppcheck-suppress constParameterPointer void process(AstNetlist* netlistp) { // First, remove empty functions. We need to do this separately, because removing // calls can change the hashes of the callers. diff --git a/src/V3Const.cpp b/src/V3Const.cpp index b2afb50c5..8c851fae5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1058,7 +1058,7 @@ class ConstVisitor final : public VNVisitor { // Someday we'll sort the biops completely and this can be simplified // This often results from our simplified clock generation: // if (rst) ... else if (enable)... -> OR(rst,AND(!rst,enable)) - AstNodeExpr* ap; + const AstNodeExpr* ap; AstNodeBiop* andp; if (VN_IS(nodep->lhsp(), And)) { andp = VN_AS(nodep->lhsp(), And); @@ -1080,7 +1080,7 @@ class ConstVisitor final : public VNVisitor { } else { return false; } - AstNodeExpr* const bp = notp->lhsp(); + const AstNodeExpr* const bp = notp->lhsp(); if (!operandsSame(ap, bp)) return false; // Do it cp->unlinkFrBack(); @@ -1099,6 +1099,7 @@ class ConstVisitor final : public VNVisitor { // (otherwise we'd be trading one operation for two operations) // V3Clean often makes this pattern, as it postpones the AND until // as high as possible, which is usually the right choice, except for this. + // cppcheck-suppress constVariablePointer // children unlinked below AstCond* const condp = VN_CAST(nodep->rhsp(), Cond); if (!condp) return false; if (!VN_IS(condp->thenp(), Const) && !VN_IS(condp->elsep(), Const)) return false; @@ -1322,6 +1323,7 @@ class ConstVisitor final : public VNVisitor { // A pattern created by []'s after offsets have been removed // SEL(EXTEND(any,width,...),(width-1),0) -> ... // Since select's return unsigned, this is always an extend + // cppcheck-suppress constVariablePointer // children unlinked below AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend); if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0 && static_cast(nodep->widthConst()) == extendp->lhsp()->width())) @@ -1347,7 +1349,7 @@ class ConstVisitor final : public VNVisitor { // AND({a}, SHIFTR({b}, {c})) is often shorthand in C for Verilog {b}[{c} :+ {a}] // becomes thought other optimizations // SEL(SHIFTR({a},{b}),{lsb},{width}) -> SEL({a},{lsb+b},{width}) - AstShiftR* const shiftp = VN_CAST(nodep->fromp(), ShiftR); + const AstShiftR* const shiftp = VN_CAST(nodep->fromp(), ShiftR); if (!(m_doV && shiftp && VN_IS(shiftp->rhsp(), Const) && VN_IS(nodep->lsbp(), Const))) { return false; } @@ -2005,21 +2007,22 @@ class ConstVisitor final : public VNVisitor { // Skip if we're not const'ing an entire module (IE doing only one assign, etc) if (!m_modp) return false; + // cppcheck-suppress constVariablePointer // children unlinked below AstSel* const sel1p = VN_CAST(nodep->lhsp(), Sel); if (!sel1p) return false; AstNodeAssign* const nextp = VN_CAST(nodep->nextp(), NodeAssign); if (!nextp) return false; if (nodep->type() != nextp->type()) return false; - AstSel* const sel2p = VN_CAST(nextp->lhsp(), Sel); + const AstSel* const sel2p = VN_CAST(nextp->lhsp(), Sel); if (!sel2p) return false; AstVarRef* const varref1p = VN_CAST(sel1p->fromp(), VarRef); if (!varref1p) return false; - AstVarRef* const varref2p = VN_CAST(sel2p->fromp(), VarRef); + const AstVarRef* const varref2p = VN_CAST(sel2p->fromp(), VarRef); if (!varref2p) return false; if (!varref1p->sameGateTree(varref2p)) return false; - AstConst* const con1p = VN_CAST(sel1p->lsbp(), Const); + const AstConst* const con1p = VN_CAST(sel1p->lsbp(), Const); if (!con1p) return false; - AstConst* const con2p = VN_CAST(sel2p->lsbp(), Const); + const AstConst* const con2p = VN_CAST(sel2p->lsbp(), Const); if (!con2p) return false; // We need to make sure there's no self-references involved in either // assignment. For speed, we only look 3 deep, then give up. @@ -2600,6 +2603,7 @@ class ConstVisitor final : public VNVisitor { void replaceSelSel(AstSel* nodep) { // SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d) + // cppcheck-suppress constVariablePointer // children unlinked below AstSel* const belowp = VN_AS(nodep->fromp(), Sel); AstNodeExpr* const fromp = belowp->fromp()->unlinkFrBack(); AstNodeExpr* const lsb1p = nodep->lsbp()->unlinkFrBack(); @@ -2667,9 +2671,10 @@ class ConstVisitor final : public VNVisitor { bool operandSelReplicate(AstSel* nodep) { // SEL(REPLICATE(from,rep),lsb,width) => SEL(from,0,width) as long // as SEL's width <= b's width + // cppcheck-suppress constVariablePointer // children unlinked below AstReplicate* const repp = VN_AS(nodep->fromp(), Replicate); AstNodeExpr* const fromp = repp->srcp(); - AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const); + const AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const); if (!lsbp) return false; UASSERT_OBJ(fromp->width(), nodep, "Not widthed"); if ((lsbp->toUInt() / fromp->width()) @@ -2687,6 +2692,7 @@ class ConstVisitor final : public VNVisitor { } bool operandRepRep(AstReplicate* nodep) { // REPLICATE(REPLICATE2(from2,cnt2),cnt1) => REPLICATE(from2,(cnt1+cnt2)) + // cppcheck-suppress constVariablePointer // children unlinked below AstReplicate* const rep2p = VN_AS(nodep->srcp(), Replicate); AstNodeExpr* const from2p = rep2p->srcp(); AstConst* const cnt1p = VN_CAST(nodep->countp(), Const); @@ -2713,13 +2719,13 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* from2p = nodep->rhsp(); uint32_t cnt2 = 1; if (VN_IS(from1p, Replicate)) { - AstConst* const cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->countp(), Const); + const AstConst* const cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->countp(), Const); if (!cnt1p) return false; from1p = VN_AS(from1p, Replicate)->srcp(); cnt1 = cnt1p->toUInt(); } if (VN_IS(from2p, Replicate)) { - AstConst* const cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->countp(), Const); + const AstConst* const cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->countp(), Const); if (!cnt2p) return false; from2p = VN_AS(from2p, Replicate)->srcp(); cnt2 = cnt2p->toUInt(); @@ -2826,9 +2832,9 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(replaceNum(nodep, num), nodep); did = true; } else if (m_selp && VN_IS(valuep, InitArray)) { - AstInitArray* const initarp = VN_AS(valuep, InitArray); + const AstInitArray* const initarp = VN_AS(valuep, InitArray); const uint32_t bit = m_selp->bitConst(); - AstNode* const itemp = initarp->getIndexDefaultedValuep(bit); + const AstNode* const itemp = initarp->getIndexDefaultedValuep(bit); if (VN_IS(itemp, Const)) { const V3Number& num = VN_AS(itemp, Const)->num(); // UINFO(2, "constVisit " << cvtToHex(valuep) << " " << num); @@ -3060,8 +3066,8 @@ class ConstVisitor final : public VNVisitor { if (!nextp) break; AstSenItem* const lItemp = senp; AstSenItem* const rItemp = nextp; - AstNodeExpr* const lSenp = lItemp->sensp(); - AstNodeExpr* const rSenp = rItemp->sensp(); + const AstNodeExpr* const lSenp = lItemp->sensp(); + const AstNodeExpr* const rSenp = rItemp->sensp(); if (!lSenp || !rSenp) continue; if (lSenp->sameGateTree(rSenp)) { @@ -3085,8 +3091,8 @@ class ConstVisitor final : public VNVisitor { // Not identical terms, check if they can be combined if (lSenp->width() != rSenp->width()) continue; - if (AstAnd* const lAndp = VN_CAST(lSenp, And)) { - if (AstAnd* const rAndp = VN_CAST(rSenp, And)) { + if (const AstAnd* const lAndp = VN_CAST(lSenp, And)) { + if (const AstAnd* const rAndp = VN_CAST(rSenp, And)) { if (AstConst* const lConstp = VN_CAST(lAndp->lhsp(), Const)) { if (AstConst* const rConstp = VN_CAST(rAndp->lhsp(), Const)) { if (lAndp->rhsp()->sameTree(rAndp->rhsp())) { @@ -3204,6 +3210,7 @@ class ConstVisitor final : public VNVisitor { } } void visit(AstRelease* nodep) override { + // cppcheck-suppress constVariablePointer // children unlinked below if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) { FileLine* const flp = nodep->fileline(); AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()}; @@ -3282,6 +3289,7 @@ class ConstVisitor final : public VNVisitor { UINFO(4, "IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})"); AstNodeAssign* const thensp = VN_AS(nodep->thensp(), NodeAssign); + // cppcheck-suppress constVariablePointer // children unlinked below AstNodeAssign* const elsesp = VN_AS(nodep->elsesp(), NodeAssign); thensp->unlinkFrBack(); AstNodeExpr* const condp = nodep->condp()->unlinkFrBack(); @@ -3330,6 +3338,7 @@ class ConstVisitor final : public VNVisitor { if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp()) return false; AstSFormatF* const pformatp = prevp->fmtp(); + // cppcheck-suppress constVariablePointer // children unlinked below AstSFormatF* const nformatp = nodep->fmtp(); // We don't merge scopeNames as can have only one and might be different scopes (late in // process) Also rare for real code to print %m multiple times in same message @@ -3979,6 +3988,7 @@ void V3Const::constifyParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); ConstVisitor visitor{ConstVisitor::PROC_PARAMS, /* globalPass: */ false}; + // cppcheck-suppress constVariablePointer // edited below if (AstVar* const varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -4000,6 +4010,7 @@ void V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false}; + // cppcheck-suppress constVariablePointer // edited below if (AstVar* const varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -4029,6 +4040,7 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthGenerateParamsEdit(nodep); ConstVisitor visitor{ConstVisitor::PROC_GENERATE, /* globalPass: */ false}; + // cppcheck-suppress constVariablePointer // edited below if (AstVar* const varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index b36758ed2..469c2e578 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -85,6 +85,7 @@ class DeadVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } + // cppcheck-suppress constParameterPointer void checkAll(AstNode* nodep) { if (AstNode* const subnodep = nodep->dtypep()) { if (nodep != subnodep // Not NodeDTypes reference themselves @@ -473,7 +474,9 @@ class DeadVisitor final : public VNVisitor { } } + // cppcheck-suppress constParameterPointer void preserveTopIfaces(AstNetlist* rootp) { + // cppcheck-suppress constVariablePointer for (AstNodeModule* modp = rootp->modulesp(); modp && modp->level() <= 2; modp = VN_AS(modp->nextp(), NodeModule)) { for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 072e73f29..964a80593 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -220,6 +220,7 @@ class DelayedVisitor final : public VNVisitor { // Remove duplicates V3Const::constifyExpensiveEdit(m_senTreep); } + // cppcheck-suppress constParameterPointer void addSensitivity(AstSenTree* nodep) { addSensitivity(nodep->sensesp()); } }; @@ -1126,7 +1127,7 @@ class DelayedVisitor final : public VNVisitor { // First gather all senItems AstSenItem* senItemp = nullptr; - for (AstSenTree* const domainp : m_timingDomains) { + for (const AstSenTree* const domainp : m_timingDomains) { if (domainp->sensesp()) senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true)); } @@ -1216,6 +1217,7 @@ class DelayedVisitor final : public VNVisitor { VL_RESTORER(m_currNbaLhsRefp); UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "NBAs should not nest"); nodep->lhsp()->foreach([&](AstNode* currp) { + // cppcheck-suppress constVariablePointer if (AstExprStmt* const exprp = VN_CAST(currp, ExprStmt)) { // Move statements before the NBA nodep->addHereThisAsNext(exprp->stmtsp()->unlinkFrBackWithNext()); diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 3311c3f81..b36b27a65 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -134,7 +134,7 @@ std::unique_ptr DfgGraph::clone() const { // Hook up inputs of cloned variables for (const DfgVertexVar& vtx : m_varVertices) { // All variable vertices are unary - if (DfgVertex* const srcp = vtx.srcp()) { + if (const DfgVertex* const srcp = vtx.srcp()) { vtxp2clonep.at(&vtx)->as()->srcp(vtxp2clonep.at(srcp)); } } @@ -146,7 +146,7 @@ std::unique_ptr DfgGraph::clone() const { const DfgSpliceArray* const vp = vtx.as(); DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as(); vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { + if (const DfgVertex* const srcp = edge.sourcep()) { cp->addDriver(vp->driverFileLine(i), // vp->driverLo(i), // vtxp2clonep.at(srcp)); @@ -158,7 +158,7 @@ std::unique_ptr DfgGraph::clone() const { const DfgSplicePacked* const vp = vtx.as(); DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as(); vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcVp = edge.sourcep()) { + if (const DfgVertex* const srcVp = edge.sourcep()) { DfgVertex* const srcCp = vtxp2clonep.at(srcVp); UASSERT_OBJ(!srcCp->is(), srcCp, "Cannot clone DfgLogic"); if (srcVp == vp->defaultp()) { @@ -183,7 +183,7 @@ std::unique_ptr DfgGraph::clone() const { UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, "Mismatched source count"); for (size_t i = 0; i < oSourceEdges.second; ++i) { - if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { + if (const DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); } } @@ -244,11 +244,11 @@ std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) { // Construct the tmpNameStub if we have not done so yet if (m_tmpNameStub.empty()) { // Use the hash of the graph name (avoid long names and non-identifiers) - const std::string name = V3Hash{m_name}.toString(); + const std::string hash = V3Hash{m_name}.toString(); // We need to keep every variable globally unique, and graph hashed // names might not be, so keep a static table to track multiplicity static std::unordered_map s_multiplicity; - m_tmpNameStub += '_' + name + '_' + std::to_string(s_multiplicity[name]++) + '_'; + m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_'; } // Assemble the globally unique name return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n); @@ -287,12 +287,12 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(& static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { if (const DfgVarPacked* const varVtxp = vtx.cast()) { - AstNode* const nodep = varVtxp->nodep(); - AstVar* const varp = varVtxp->varp(); + const AstNode* const nodep = varVtxp->nodep(); + const AstVar* const varp = varVtxp->varp(); os << toDotId(vtx); os << " [label=\"" << nodep->prettyName() << '\n'; os << cvtToHex(varVtxp) << '\n'; - if (AstNode* const tmpForp = varVtxp->tmpForp()) { + if (const AstNode* const tmpForp = varVtxp->tmpForp()) { os << "temporary for: " << tmpForp->prettyName() << "\n"; } varVtxp->dtypep()->dumpSmall(os); @@ -320,12 +320,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } if (const DfgVarArray* const arrVtxp = vtx.cast()) { - AstNode* const nodep = arrVtxp->nodep(); - AstVar* const varp = arrVtxp->varp(); + const AstNode* const nodep = arrVtxp->nodep(); + const AstVar* const varp = arrVtxp->varp(); os << toDotId(vtx); os << " [label=\"" << nodep->prettyName() << '\n'; os << cvtToHex(arrVtxp) << '\n'; - if (AstNode* const tmpForp = arrVtxp->tmpForp()) { + if (const AstNode* const tmpForp = arrVtxp->tmpForp()) { os << "temporary for: " << tmpForp->prettyName() << "\n"; } arrVtxp->dtypep()->dumpSmall(os); @@ -512,11 +512,12 @@ void DfgGraph::dumpDotFilePrefixed(const std::string& label, template static std::unique_ptr> -dfgGraphCollectCone(const std::vector vtxps) { +dfgGraphCollectCone(const std::vector& vtxps) { // Work queue for traversal starting from all the seed vertices std::vector queue = vtxps; // Set of already visited vertices - std::unordered_set* const resp = new std::unordered_set{}; + std::unique_ptr> resp{ + new std::unordered_set{}}; // Depth first traversal while (!queue.empty()) { // Pop next work item @@ -532,16 +533,16 @@ dfgGraphCollectCone(const std::vector vtxps) { } } // Done - return std::unique_ptr>{resp}; + return resp; } std::unique_ptr> -DfgGraph::sourceCone(const std::vector vtxps) const { +DfgGraph::sourceCone(const std::vector& vtxps) const { return dfgGraphCollectCone(vtxps); } std::unique_ptr> -DfgGraph::sinkCone(const std::vector vtxps) const { +DfgGraph::sinkCone(const std::vector& vtxps) const { return dfgGraphCollectCone(vtxps); } @@ -578,12 +579,12 @@ void DfgEdge::unlinkSource() { if (!m_sourcep) return; #ifdef VL_DEBUG { - DfgEdge* sinkp = m_sourcep->m_sinksp; - while (sinkp) { - if (sinkp == this) break; - sinkp = sinkp->m_nextp; + DfgEdge* currp = m_sourcep->m_sinksp; + while (currp) { + if (currp == this) break; + currp = currp->m_nextp; } - UASSERT(sinkp, "'m_sourcep' does not have this edge as sink"); + UASSERT(currp, "'m_sourcep' does not have this edge as sink"); } #endif // Relink pointers of predecessor and successor @@ -677,7 +678,7 @@ V3Hash DfgVertex::hash() { // variables, which we rely on. if (!is()) { hash += m_type; - if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) { + if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) { hash += adtypep->elementsConst(); // TODO: maybe include sub-dtype, but not hugely important at the moment } else { @@ -685,9 +686,9 @@ V3Hash DfgVertex::hash() { } const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; + const size_t nEdges = pair.second; // Sources must always be connected in well-formed graphs - for (size_t i = 0; i < arity; ++i) hash += edgesp[i].m_sourcep->hash(); + for (size_t i = 0; i < nEdges; ++i) hash += edgesp[i].m_sourcep->hash(); } result = hash; } @@ -758,11 +759,15 @@ DfgVertexVar* DfgVertex::getResultVar() { AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED { // If this is a variable, we are done - if (DfgVertexVar* const varp = this->cast()) return varp->varScopep()->scopep(); + if (const DfgVertexVar* const varp = this->cast()) { + return varp->varScopep()->scopep(); + } // Try the result var first if instructed (usully only in the recursive case) if (tryResultVar) { - if (DfgVertexVar* const varp = this->getResultVar()) return varp->varScopep()->scopep(); + if (const DfgVertexVar* const varp = this->getResultVar()) { + return varp->varScopep()->scopep(); + } } // Note: the recursive invocation can cause a re-hash but that will not invalidate references @@ -775,7 +780,7 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED AstScope* foundp = rootp; const auto edges = sourceEdges(); for (size_t i = 0; i < edges.second; ++i) { - DfgEdge& edge = edges.first[i]; + const DfgEdge& edge = edges.first[i]; foundp = edge.sourcep()->scopep(cache, true); if (foundp != rootp) break; } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 46a9b26b5..0297f975d 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -705,7 +705,8 @@ public: // Split this graph into individual components (unique sub-graphs with no edges between them). // Also removes any vertices that are not weakly connected to any variable. // Leaves 'this' graph empty. - std::vector> splitIntoComponents(std::string label) VL_MT_DISABLED; + std::vector> + splitIntoComponents(const std::string& label) VL_MT_DISABLED; // Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at // least one strongly connected component (SCC) plus any other vertices that feed or sink from @@ -716,7 +717,7 @@ public: // to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if // it was originally connected. std::vector> - extractCyclicComponents(std::string label) VL_MT_DISABLED; + extractCyclicComponents(const std::string& label) VL_MT_DISABLED; //----------------------------------------------------------------------- // Debug dumping @@ -742,10 +743,10 @@ public: // Returns the set of vertices in the upstream cones of the given vertices std::unique_ptr> - sourceCone(const std::vector) const VL_MT_DISABLED; + sourceCone(const std::vector&) const VL_MT_DISABLED; // Returns the set of vertices in the downstream cones of the given vertices std::unique_ptr> - sinkCone(const std::vector) const VL_MT_DISABLED; + sinkCone(const std::vector&) const VL_MT_DISABLED; //----------------------------------------------------------------------- // Static methods for data types @@ -824,9 +825,7 @@ T& DfgVertex::user() { UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");); if (m_userCnt != userCurrent) { m_userCnt = userCurrent; - // cppcheck-has-bug-suppress uninitvar - VL_ATTR_UNUSED T* const resultp = new (storagep) T{}; - UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd");); + new (storagep) T{}; } return *storagep; } @@ -864,8 +863,8 @@ void DfgVertex::setUser(T value) { void DfgVertex::forEachSource(std::function f) { const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { + const size_t nEdges = pair.second; + for (size_t i = 0; i < nEdges; ++i) { if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); } } @@ -873,8 +872,8 @@ void DfgVertex::forEachSource(std::function f) { void DfgVertex::forEachSource(std::function f) const { const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { + const size_t nEdges = pair.second; + for (size_t i = 0; i < nEdges; ++i) { if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); } } @@ -893,15 +892,15 @@ void DfgVertex::forEachSink(std::function f) const { void DfgVertex::forEachSourceEdge(std::function f) { const auto pair = sourceEdges(); DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); + const size_t nEdges = pair.second; + for (size_t i = 0; i < nEdges; ++i) f(edgesp[i], i); } void DfgVertex::forEachSourceEdge(std::function f) const { const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); + const size_t nEdges = pair.second; + for (size_t i = 0; i < nEdges; ++i) f(edgesp[i], i); } void DfgVertex::forEachSinkEdge(std::function f) { @@ -921,8 +920,8 @@ void DfgVertex::forEachSinkEdge(std::function f) const { const DfgEdge* DfgVertex::findSourceEdge(std::function p) const { const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { + const size_t nEdges = pair.second; + for (size_t i = 0; i < nEdges; ++i) { const DfgEdge& edge = edgesp[i]; if (p(edge, i)) return &edge; } @@ -979,8 +978,8 @@ DfgVertex* DfgVertexSplice::wholep() const { if (driverLo(0) != 0) return nullptr; DfgVertex* const srcp = DfgVertexVariadic::source(1); if (srcp->size() != size()) return nullptr; - if (DfgUnitArray* const uap = srcp->cast()) { - if (DfgVertexSplice* sp = uap->srcp()->cast()) { + if (const DfgUnitArray* const uap = srcp->cast()) { + if (const DfgVertexSplice* sp = uap->srcp()->cast()) { if (!sp->wholep()) return nullptr; } } @@ -1014,6 +1013,7 @@ DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) DfgVertexVar::~DfgVertexVar() { // Decrement reference count + // cppcheck-suppress shadowFunction AstNode* const nodep = this->nodep(); nodep->user1(nodep->user1() - 0x10); UASSERT_OBJ((nodep->user1() >> 4) >= 0, nodep, "Reference count underflow"); diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 9d0d852d5..aab0f1eab 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -46,7 +46,8 @@ class TraceDriver final : public DfgVisitor { struct Hash final { size_t operator()(const Visited& item) const { - V3Hash hash{reinterpret_cast(item.m_vtxp)}; + // cppcheck-suppress unreadVariable + V3Hash hash{item.m_vtxp}; hash += item.m_lsb; hash += item.m_msb; return hash.value(); @@ -244,7 +245,7 @@ class TraceDriver final : public DfgVisitor { // Somewhat rudimentary but sufficient for current purposes. static bool knownToBeZeroAtAndAbove(const DfgVertex* vtxp, uint32_t idx) { if (const DfgConcat* const catp = vtxp->cast()) { - DfgConst* const lConstp = catp->lhsp()->cast(); + const DfgConst* const lConstp = catp->lhsp()->cast(); return lConstp && idx >= catp->rhsp()->width() && lConstp->isZero(); } if (const DfgExtend* const extp = vtxp->cast()) { @@ -256,7 +257,7 @@ class TraceDriver final : public DfgVisitor { // Like knownToBeZeroAtAndAbove, but checks vtxp[idx:0] static bool knownToBeZeroAtAndBelow(const DfgVertex* vtxp, uint32_t idx) { if (const DfgConcat* const catp = vtxp->cast()) { - DfgConst* const rConstp = catp->rhsp()->cast(); + const DfgConst* const rConstp = catp->rhsp()->cast(); return rConstp && idx < rConstp->width() && rConstp->isZero(); } return false; @@ -381,10 +382,10 @@ class TraceDriver final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { // Only constant select - DfgConst* const idxp = vtxp->bitp()->cast(); + const DfgConst* const idxp = vtxp->bitp()->cast(); if (!idxp) return; // From a variable - DfgVarArray* varp = vtxp->fromp()->cast(); + const DfgVarArray* varp = vtxp->fromp()->cast(); if (!varp) return; // Skip through intermediate variables while (varp->srcp() && varp->srcp()->is()) { @@ -394,10 +395,10 @@ class TraceDriver final : public DfgVisitor { if (!varp->srcp()) return; // Driver might be a splice - if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { - DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + if (const DfgSpliceArray* const splicep = varp->srcp()->cast()) { + const DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); if (!driverp) return; - DfgUnitArray* const uap = driverp->cast(); + const DfgUnitArray* const uap = driverp->cast(); if (!uap) return; // Trace the driver SET_RESULT(trace(uap->srcp(), m_msb, m_lsb)); @@ -405,7 +406,7 @@ class TraceDriver final : public DfgVisitor { } // Or a unit array - DfgUnitArray* const uap = varp->srcp()->cast(); + const DfgUnitArray* const uap = varp->srcp()->cast(); if (!uap) return; // Trace the driver UASSERT_OBJ(idxp->toSizeT() == 0, vtxp, "Array Index out of range"); @@ -523,7 +524,7 @@ class TraceDriver final : public DfgVisitor { void visit(DfgShiftR* vtxp) override { DfgVertex* const lhsp = vtxp->lhsp(); - if (DfgConst* const rConstp = vtxp->rhsp()->cast()) { + if (const DfgConst* const rConstp = vtxp->rhsp()->cast()) { const uint32_t shiftAmnt = rConstp->toU32(); // Width of lower half of result const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt; @@ -558,7 +559,7 @@ class TraceDriver final : public DfgVisitor { void visit(DfgShiftL* vtxp) override { DfgVertex* const lhsp = vtxp->lhsp(); - if (DfgConst* const rConstp = vtxp->rhsp()->cast()) { + if (const DfgConst* const rConstp = vtxp->rhsp()->cast()) { const uint32_t shiftAmnt = rConstp->toU32(); // Width of lower half of result const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? vtxp->width() : shiftAmnt; @@ -642,13 +643,13 @@ public: // partial trace succeded, but an eventual one falied). Because new // vertices should be created depth first, it is enough to do a single // reverse pass over the collectoin - for (DfgVertex* const vtxp : vlstd::reverse_view(traceDriver.m_newVtxps)) { + for (DfgVertex* const newp : vlstd::reverse_view(traceDriver.m_newVtxps)) { // Keep the actual result! - if (vtxp == resultp) continue; + if (newp == resultp) continue; // Keep used ones! - if (vtxp->hasSinks()) continue; + if (newp->hasSinks()) continue; // Delete it - VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); + VL_DO_DANGLING(newp->unlinkDelete(dfg), newp); } // Return the result return resultp; @@ -733,10 +734,10 @@ class IndependentBits final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { // Only constant select - DfgConst* const idxp = vtxp->bitp()->cast(); + const DfgConst* const idxp = vtxp->bitp()->cast(); if (!idxp) return; // From a variable - DfgVarArray* varp = vtxp->fromp()->cast(); + const DfgVarArray* varp = vtxp->fromp()->cast(); if (!varp) return; // Skip through intermediate variables while (varp->srcp() && varp->srcp()->is()) { @@ -744,11 +745,11 @@ class IndependentBits final : public DfgVisitor { } // Find driver if (!varp->srcp()) return; - DfgSpliceArray* const splicep = varp->srcp()->cast(); + const DfgSpliceArray* const splicep = varp->srcp()->cast(); if (!splicep) return; - DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); + const DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT()); if (!driverp) return; - DfgUnitArray* const uap = driverp->cast(); + const DfgUnitArray* const uap = driverp->cast(); if (!uap) return; // Update mask MASK(vtxp) = MASK(uap->srcp()); @@ -1046,7 +1047,7 @@ class FixUpIndependentRanges final { V3Number result{vtxp->fileline(), static_cast(vtxp->width()), 0}; vtxp->forEachSink([&result](DfgVertex& sink) { // If used via a Sel, mark the selected bits used - if (DfgSel* const selp = sink.cast()) { + if (const DfgSel* const selp = sink.cast()) { uint32_t lsb = selp->lsb(); uint32_t msb = lsb + selp->width() - 1; for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1'); @@ -1058,12 +1059,12 @@ class FixUpIndependentRanges final { return result; } - static std::string debugStr(DfgVertex* vtxp) { - if (DfgArraySel* const aselp = vtxp->cast()) { + static std::string debugStr(const DfgVertex* vtxp) { + if (const DfgArraySel* const aselp = vtxp->cast()) { const size_t i = aselp->bitp()->as()->toSizeT(); return debugStr(aselp->fromp()) + "[" + std::to_string(i) + "]"; } - if (DfgVertexVar* const varp = vtxp->cast()) { + if (const DfgVertexVar* const varp = vtxp->cast()) { return varp->nodep()->name(); } vtxp->v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE @@ -1165,7 +1166,7 @@ class FixUpIndependentRanges final { // If no imporovement was possible, delete the terms we just created if (!nImprovements) { - for (DfgVertex* const vtxp : termps) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); + for (DfgVertex* const termp : termps) VL_DO_DANGLING(termp->unlinkDelete(dfg), termp); termps.clear(); return 0; } diff --git a/src/V3DfgCache.h b/src/V3DfgCache.h index 469130340..44c773fd1 100644 --- a/src/V3DfgCache.h +++ b/src/V3DfgCache.h @@ -282,22 +282,22 @@ inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) { } // These remove an existing vertex from the cache, if it is the cached vertex -inline void invalidateByValue(CacheSel& cache, DfgSel* vtxp) { +inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) { const auto it = find(cache, vtxp->dtypep(), vtxp->fromp(), vtxp->lsb()); if (it != cache.end() && it->second == vtxp) cache.erase(it); } -inline void invalidateByValue(CacheUnary& cache, DfgVertexUnary* vtxp) { +inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) { const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>()); if (it != cache.end() && it->second == vtxp) cache.erase(it); } -inline void invalidateByValue(CacheBinary& cache, DfgVertexBinary* vtxp) { +inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) { const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>()); if (it != cache.end() && it->second == vtxp) cache.erase(it); } -inline void invalidateByValue(CacheTernary& cache, DfgVertexTernary* vtxp) { +inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) { const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>()); if (it != cache.end() && it->second == vtxp) cache.erase(it); diff --git a/src/V3DfgColorSCCs.cpp b/src/V3DfgColorSCCs.cpp index 95da70dbc..c43566f76 100644 --- a/src/V3DfgColorSCCs.cpp +++ b/src/V3DfgColorSCCs.cpp @@ -126,7 +126,7 @@ class ColorStronglyConnectedComponents final { } } - ColorStronglyConnectedComponents(DfgGraph& dfg) + explicit ColorStronglyConnectedComponents(DfgGraph& dfg) : m_dfg{dfg} { UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name()); // Yet another implementation of Pearce's algorithm. diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index 9fad9e2ee..a32daedd0 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -352,8 +352,8 @@ public: , m_prefix{VString::removeWhitespace(label) + "-"} {} ~V3DfgContext() { - const string prefix = "Optimizations, DFG " + label() + " General, "; - V3Stats::addStat(prefix + "modules", m_modules); + const string front = "Optimizations, DFG " + label() + " General, "; + V3Stats::addStat(front + "modules", m_modules); // Print the collected patterns if (v3Global.opt.stats()) { diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 8de979b4b..6f2aa82fb 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -113,7 +113,7 @@ public: } }; -std::vector> DfgGraph::splitIntoComponents(std::string label) { +std::vector> DfgGraph::splitIntoComponents(const std::string& label) { return SplitIntoComponents::apply(*this, label); } @@ -125,7 +125,7 @@ class ExtractCyclicComponents final { uint64_t& m_userr; public: - VertexState(DfgVertex& vtx) + explicit VertexState(DfgVertex& vtx) : m_userr{vtx.getUser()} {} bool merged() const { return m_userr >> 63; } void setMerged() { m_userr |= 1ULL << 63; } @@ -266,11 +266,11 @@ class ExtractCyclicComponents final { dfg.forEachVertex([&](const DfgVertex& vtx) { vertices.insert(&vtx); }); // Check that each edge connects to a vertex that is within the same graph - dfg.forEachVertex([&](DfgVertex& vtx) { - vtx.forEachSource([&](DfgVertex& src) { + dfg.forEachVertex([&](const DfgVertex& vtx) { + vtx.forEachSource([&](const DfgVertex& src) { UASSERT_OBJ(vertices.count(&src), &vtx, "Source vertex not in graph"); }); - vtx.forEachSink([&](DfgVertex& snk) { + vtx.forEachSink([&](const DfgVertex& snk) { UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph"); }); }); @@ -287,7 +287,7 @@ class ExtractCyclicComponents final { // earlier merging of components ensured crossing in fact only happen at variable // boundaries). Note that fixing up the edges can create clones of variables. Clones do // not need fixing up, so we do not need to iterate them. - DfgVertex* const lastp = m_dfg.varVertices().backp(); + const DfgVertex* const lastp = m_dfg.varVertices().backp(); for (DfgVertexVar& vtx : m_dfg.varVertices()) { // Fix up the edges crossing components fixEdges(vtx); @@ -337,6 +337,7 @@ public: } }; -std::vector> DfgGraph::extractCyclicComponents(std::string label) { +std::vector> +DfgGraph::extractCyclicComponents(const std::string& label) { return ExtractCyclicComponents::apply(*this, label); } diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 09df3a63f..3894534b6 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -194,7 +194,7 @@ class DfgToAstVisitor final : DfgVisitor { AstConst* const idxp = new AstConst{dflp, sArrayp->driverLo(i)}; AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp}; // Convert source - if (DfgUnitArray* const uap = driverp->cast()) { + if (const DfgUnitArray* const uap = driverp->cast()) { convertDriver(assignments, dflp, nLhsp, uap->srcp()); } else { convertDriver(assignments, dflp, nLhsp, driverp); @@ -205,7 +205,7 @@ class DfgToAstVisitor final : DfgVisitor { return; } - if (DfgUnitArray* const uap = driverp->cast()) { + if (const DfgUnitArray* const uap = driverp->cast()) { // Single element array being assigned a unit array. Needs an ArraySel. AstConst* const idxp = new AstConst{flp, 0}; AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp}; diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index 3a12470fb..b4d0ef642 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -164,7 +164,9 @@ class DataflowExtractVisitor final : public VNVisitor { void visit(AstAssignW* nodep) override { // Mark LHS variable as combinationally driven - if (AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef)) vrefp->varp()->user4(true); + if (const AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef)) { + vrefp->varp()->user4(true); + } // iterateChildrenConst(nodep); } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index a2d47394c..005e6e66b 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -312,7 +312,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { v3Global.rootp()->typeTablep()->addTypesp(tabDTypep); // The index variable - DfgVarPacked* const idxVtxp = [&]() { + AstVar* const idxVarp = [&]() { // If there is an existing result variable, use that, otherwise create a new variable DfgVarPacked* varp = nullptr; if (DfgVertexVar* const vp = srcp->getResultVar()) { @@ -324,7 +324,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { varp->srcp(srcp); } varp->setHasModRdRefs(); - return varp; + return varp->varp(); }(); // The previous index variable - we don't need a vertex for this AstVar* const preVarp = [&]() { @@ -381,13 +381,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { logicp->addStmtsp(new AstAssign{ flp, // new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE}, - new AstVarRef{flp, idxVtxp->varp(), VAccess::READ}}, // + new AstVarRef{flp, idxVarp, VAccess::READ}}, // new AstConst{flp, AstConst::BitTrue{}}}); } { // pre = idx logicp->addStmtsp(new AstAssign{flp, // new AstVarRef{flp, preVarp, VAccess::WRITE}, // - new AstVarRef{flp, idxVtxp->varp(), VAccess::READ}}); + new AstVarRef{flp, idxVarp, VAccess::READ}}); } // Replace terms with ArraySels @@ -500,7 +500,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { ++ctx.m_varsRemoved; varp->replaceWith(varp->srcp()); ctx.m_deleteps.push_back(varp->nodep()); // Delete variable at the end - } else if (DfgVarPacked* const driverp = varp->srcp()->cast()) { + } else if (const DfgVarPacked* const driverp = varp->srcp()->cast()) { // If it's driven from another variable, it can be replaced by that. // Mark it for replacement ++ctx.m_varsReplaced; diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 634009af8..5a1df937f 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -232,7 +232,7 @@ class V3DfgPeephole final : public DfgVisitor { // Same as above, but 'flp' and 'dtypep' are taken from the given example vertex template - Vertex* make(DfgVertex* examplep, Operands... operands) { + Vertex* make(const DfgVertex* examplep, Operands... operands) { return make(examplep->fileline(), examplep->dtypep(), operands...); } @@ -408,8 +408,8 @@ class V3DfgPeephole final : public DfgVisitor { // If both sides are variable references, order the side in some defined way. This // allows CSE to later merge 'a op b' with 'b op a'. if (lhsp->is() && rhsp->is()) { - AstNode* const lVarp = lhsp->as()->nodep(); - AstNode* const rVarp = rhsp->as()->nodep(); + const AstNode* const lVarp = lhsp->as()->nodep(); + const AstNode* const rVarp = rhsp->as()->nodep(); if (lVarp->name() > rVarp->name()) { APPLYING(SWAP_VAR_IN_COMMUTATIVE_BINARY) { Vertex* const replacementp = make(vtxp, rhsp, lhsp); diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 4f16a52fe..3819542ec 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -153,7 +153,7 @@ class AstToDfgConverter final : public VNVisitor { } // Get (or create a new) temporary for this variable - DfgVertexVar* const vtxp = [&]() -> DfgVertexVar* { + const DfgVertexVar* const vtxp = [&]() -> DfgVertexVar* { // The variable being assigned Variable* const tgtp = getTarget(vrefp); @@ -240,7 +240,9 @@ class AstToDfgConverter final : public VNVisitor { // Return the splice driver DfgVertex* driverp = splicep->driverAt(index); - if (DfgUnitArray* const uap = driverp->cast()) driverp = uap->srcp(); + if (const DfgUnitArray* const uap = driverp->cast()) { + driverp = uap->srcp(); + } return {driverp->as(), 0}; } @@ -1319,6 +1321,7 @@ class AstToDfgSynthesize final { const bool missing = m_logicp->findSink([&](const DfgVertex& sink) -> bool { const DfgUnresolved* const unresolvedp = sink.as(); AstNode* const tgtp = unresolvedp->singleSink()->as()->nodep(); + // cppcheck-suppress constVariablePointer Variable* const varp = reinterpret_cast(tgtp); return !oSymTab.count(varp); }); @@ -1331,6 +1334,7 @@ class AstToDfgSynthesize final { m_logicp->forEachSink([&](DfgVertex& sink) { DfgUnresolved* const unresolvedp = sink.as(); AstNode* const tgtp = unresolvedp->singleSink()->as()->nodep(); + // cppcheck-suppress constVariablePointer Variable* const varp = reinterpret_cast(tgtp); DfgVertexVar* const resp = oSymTab.at(varp); UASSERT_OBJ(resp->srcp(), resp, "Undriven result"); @@ -1489,7 +1493,7 @@ class AstToDfgSynthesize final { //------------------------------------------------------------------- UINFO(5, "Step 2: Revert drivers of variables with unsynthesizeable drivers"); // We do this as the variables might be multi-driven, we just can't know at this point - for (DfgVertexVar& var : m_dfg.varVertices()) { + for (const DfgVertexVar& var : m_dfg.varVertices()) { if (!var.srcp()) continue; DfgUnresolved* const unresolvedp = var.srcp()->cast(); if (!unresolvedp) break; // Stop when reached the synthesized temporaries @@ -1509,7 +1513,7 @@ class AstToDfgSynthesize final { // List of multi-driven variables std::vector multidrivenps; // Map from variable to its resolved driver - std::unordered_map resolvedDrivers; + std::unordered_map resolvedDrivers; // Compute resolved drivers of all variablees for (DfgVertexVar& var : m_dfg.varVertices()) { if (!var.srcp()) continue; @@ -1536,7 +1540,7 @@ class AstToDfgSynthesize final { UASSERT_OBJ(newEntry, &var, "Dupliacte driver"); } // Revert and remove drivers of multi-driven variables - for (DfgVertexVar* const vtxp : multidrivenps) { + for (const DfgVertexVar* const vtxp : multidrivenps) { // Mark as multidriven for future DFG runs - here, so we get all warning before vtxp->varp()->setDfgMultidriven(); // Might not have a driver if transitively removed on an earlier iteration @@ -1546,7 +1550,7 @@ class AstToDfgSynthesize final { revertTransivelyAndRemove(unresolvedp, m_ctx.m_synt.revertMultidrive); } // Replace all DfgUnresolved with the resolved drivers - for (DfgVertexVar& var : m_dfg.varVertices()) { + for (const DfgVertexVar& var : m_dfg.varVertices()) { if (!var.srcp()) continue; DfgUnresolved* const srcp = var.srcp()->cast(); if (!srcp) break; // Stop when reached the synthesized temporaries diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 1bc756bf0..1b0370725 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -185,6 +185,7 @@ public: // the actual driver this DfgVertexSplice can be replaced with. inline DfgVertex* wholep() const; + // cppcheck-suppress duplInheritedMember void resetSources() { m_driverData.clear(); // Unlink default driver @@ -375,7 +376,7 @@ class DfgUnresolved final : public DfgVertexVariadic { // Represents a collection of unresolved variable drivers before synthesis public: - DfgUnresolved(DfgGraph& dfg, DfgVertexVar* vtxp) + DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp) : DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep(), 1u} {} ASTGEN_MEMBERS_DfgUnresolved; @@ -383,6 +384,7 @@ public: void addDriver(DfgLogic* vtxp) { addSource()->relinkSource(vtxp); } void addDriver(DfgVertexSplice* vtxp) { addSource()->relinkSource(vtxp); } + // cppcheck-suppress duplInheritedMember void clearSources() { DfgVertexVariadic::clearSources(); } DfgVertex* singleSource() const { return arity() == 1 ? source(0) : nullptr; } diff --git a/src/V3Error.h b/src/V3Error.h index aed9db65c..1df93054c 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -649,18 +649,19 @@ void v3errorEndFatal(std::ostringstream& sstr) /// Based on debug level, call UINFO then dumpTree on nodep, using given message prefix /// If uinfo_msg = "", suppress the first UINFO -/// If dt_msg = "", use the uinfo_msg; note any uinfo_msg side effects will happen twice. -#define UINFOTREE(level, nodep, uinfo_msg, dt_msg) \ +/// If dumptree_msg = "", use the uinfo_msg; note any uinfo_msg side effects will happen twice. +#define UINFOTREE(level, nodep, uinfo_msg, dumptree_msg) \ do { \ if (VL_UNCOVERABLE(debug() >= (level))) { \ std::ostringstream us; \ us << uinfo_msg; \ if (!us.str().empty()) UINFO(level, us.str()); \ - std::ostringstream ss; \ - ss << dt_msg; \ - if (ss.str().empty()) ss << uinfo_msg; \ - if (nodep) \ - nodep->dumpTree("- "s + V3Error::lineStr(__FILE__, __LINE__) + ss.str() + " - "); \ + if (nodep) { \ + std::ostringstream ss; \ + ss << dumptree_msg; \ + if (ss.str().empty()) ss << uinfo_msg; \ + nodep->dumpTree("- " + V3Error::lineStr(__FILE__, __LINE__) + ss.str() + " - "); \ + } \ } \ } while (false) @@ -722,6 +723,8 @@ void v3errorEndFatal(std::ostringstream& sstr) // Takes an optional "name" (as __VA_ARGS__) #define VL_DEFINE_DEBUG(...) \ VL_ATTR_UNUSED static int debug##__VA_ARGS__() VL_MT_SAFE { \ + /* Don't complain this function is unused */ \ + (void)&debug##__VA_ARGS__; \ static int level = -1; \ if (VL_UNLIKELY(level < 0)) { \ std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \ @@ -739,6 +742,8 @@ void v3errorEndFatal(std::ostringstream& sstr) // Takes an optional "name" (as __VA_ARGS__) #define VL_DEFINE_DUMP(func, tag) \ VL_ATTR_UNUSED static int dump##func() VL_MT_SAFE { \ + /* Don't complain this function is unused */ \ + (void)&dump##func; \ static int level = -1; \ if (VL_UNLIKELY(level < 0)) { \ const unsigned dumpTag = v3Global.opt.dumpLevel(tag); \ @@ -762,6 +767,7 @@ void v3errorEndFatal(std::ostringstream& sstr) VL_DEFINE_DUMP(TreeJsonLevel, \ "tree-json"); /* Define 'int dumpTreeJsonLevel()' for dumpi-tree-json */ \ VL_ATTR_UNUSED static int dumpTreeEitherLevel() { \ + /* Don't complain this function is unused */ (void)&dumpTreeEitherLevel; \ return dumpTreeJsonLevel() >= dumpTreeLevel() ? dumpTreeJsonLevel() : dumpTreeLevel(); \ } \ static_assert(true, "") diff --git a/src/V3File.h b/src/V3File.h index 009b658e7..746948386 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -302,7 +302,9 @@ public: puts("\n"); } virtual void putsHeader() {} + // cppcheck-suppress duplInheritedMember void puts(const char* strg) { putsNoTracking(strg); } + // cppcheck-suppress duplInheritedMember void puts(const string& strg) { putsNoTracking(strg); } // METHODS @@ -388,7 +390,9 @@ public: ~V3OutMkFile() override = default; virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } // No automatic indentation yet. + // cppcheck-suppress duplInheritedMember void puts(const char* strg) { putsNoTracking(strg); } + // cppcheck-suppress duplInheritedMember void puts(const string& strg) { putsNoTracking(strg); } // Put VARIABLE = VALUE void putSet(const string& var, const string& value) { diff --git a/src/V3Hash.h b/src/V3Hash.h index d73fbae6f..3263e2208 100644 --- a/src/V3Hash.h +++ b/src/V3Hash.h @@ -38,14 +38,17 @@ public: : m_value{0} {} explicit V3Hash(uint32_t val) : m_value{val} {} - explicit V3Hash(int32_t val) - : m_value{static_cast(val)} {} explicit V3Hash(uint64_t val) : m_value{combine(static_cast(val), static_cast(val >> 32))} {} - explicit V3Hash(int64_t val) - : m_value{combine(static_cast(val), static_cast(val >> 32))} {} explicit V3Hash(const std::string& val); + explicit V3Hash(int32_t val) + : V3Hash{static_cast(val)} {} + explicit V3Hash(int64_t val) + : V3Hash{static_cast(val)} {} + explicit V3Hash(void* val) + : V3Hash{reinterpret_cast(val)} {} + // METHODS uint32_t value() const VL_MT_SAFE { return m_value; } std::string toString() const VL_MT_SAFE; diff --git a/src/V3PairingHeap.h b/src/V3PairingHeap.h index 98b22b1e6..7956e3766 100644 --- a/src/V3PairingHeap.h +++ b/src/V3PairingHeap.h @@ -93,7 +93,7 @@ public: Link m_next; // Next in list of sibling heaps Link m_kids; // Head of list of child heaps Node** m_ownerpp = nullptr; // Pointer to the Link pointer pointing to this heap - T_Key m_key; // The key in the heap + T_Key m_key{}; // The key in the heap // CONSTRUCTOR explicit Node() = default; diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 3364308d4..65b58cf7b 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -168,7 +168,7 @@ private: V3Number fieldNum{nump, width}; fieldNum.opSel(*nump, msb, lsb); out << itemp->name() << ": "; - if (AstNodeDType* const childTypep = itemp->subDTypep()) { + if (const AstNodeDType* const childTypep = itemp->subDTypep()) { out << prettyNumber(&fieldNum, childTypep); } else { out << fieldNum; @@ -179,7 +179,7 @@ private: return out.str(); } } else if (const AstPackArrayDType* const arrayp = VN_CAST(dtypep, PackArrayDType)) { - if (AstNodeDType* const childTypep = arrayp->subDTypep()) { + if (const AstNodeDType* const childTypep = arrayp->subDTypep()) { std::ostringstream out; out << "["; const int arrayElements = arrayp->elementsConst(); @@ -226,7 +226,7 @@ public: conIt != tconnects->end(); ++conIt) { const AstVar* const portp = conIt->first; AstNodeExpr* const pinp = conIt->second->exprp(); - AstNodeDType* const dtypep = pinp->dtypep(); + const AstNodeDType* const dtypep = pinp->dtypep(); if (AstConst* const valp = fetchConstNull(pinp)) { stack << "\n " << portp->prettyName() << " = " << prettyNumber(&valp->num(), dtypep); @@ -876,7 +876,7 @@ private: void visit(AstArraySel* nodep) override { checkNodeInfo(nodep); iterateChildrenConst(nodep); - if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { + if (const AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { const AstConst* const indexp = fetchConst(nodep->bitp()); const uint32_t offset = indexp->num().toUInt(); AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(offset); diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt index e1fad255b..c83e0c03e 100644 --- a/src/cppcheck-suppressions.txt +++ b/src/cppcheck-suppressions.txt @@ -27,7 +27,8 @@ duplInheritedMember:src/V3AstNode*.h duplInheritedMember:src/obj_*/V3Ast__gen_impl.h // We intentionally redefine DfgVertex methods to match Ast duplInheritedMember:src/obj_*/V3Dfg__gen_auto_classes.h -// TODO: fix - maybe? -duplInheritedMember:src/V3File*.h // They are used after astgen expansion unusedPrivateFunction:src/V3Const.cpp +// These are all inappropriate +constVariablePointer:src/V3DfgPeephole.cpp +constParameterPointer:src/V3DfgPeephole.cpp From 92d47ca23ac00ea04a0a3a7e729cb32b8a657521 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 20 Aug 2025 18:49:59 +0100 Subject: [PATCH 202/252] Internals: Add separate cppcheck targets for whole components (#6318) cppcheck provides best results when analysing the whole program. It also has a builtin `-j` option to run on multiple threads. Replace existing cppcheck-* targets with cppcheck-verilator, cppcheck-runtime, cppcheck-example and cppcheck-vlc that checks each of those components as a whole. Each of these runs on the maximum available logical processors on the host by default, as reported by `nproc`, but can be manually overridden with `make CPPCHECK_JOBS=` instead. The `make cppcheck` target runs the 4 cppcheck-* targets listed above sequentially (but each of them uses the specified CPPCHECK_JOBS threads) --- Makefile.in | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/Makefile.in b/Makefile.in index 0ed2b5cf7..fb315cb70 100644 --- a/Makefile.in +++ b/Makefile.in @@ -392,16 +392,11 @@ install-msg: ###################################################################### # Format/Lint -CPPCHECK1_CPP = $(wildcard $(srcdir)/include/*.cpp) -CPPCHECK2_CPP = $(wildcard $(srcdir)/examples/*/*.cpp) -CPPCHECK3_CPP = $(wildcard $(srcdir)/src/Vlc*.cpp) -CPPCHECK4_CPP = $(wildcard $(srcdir)/src/V3[A-D]*.cpp $(srcdir)/src/Verilator*.cpp) -CPPCHECK5_CPP = $(wildcard $(srcdir)/src/V3[E-I]*.cpp) -CPPCHECK6_CPP = $(wildcard $(srcdir)/src/V3[P-Z]*.cpp) -CPPCHECK7_CPP = $(wildcard $(srcdir)/src/V3[L-R]*.cpp) -CPPCHECK8_CPP = $(wildcard $(srcdir)/src/V3[S-Z]*.cpp) -CHECK_CPP = $(CPPCHECK1_CPP) $(CPPCHECK2_CPP) $(CPPCHECK3_CPP) $(CPPCHECK4_CPP) \ - $(CPPCHECK5_CPP) $(CPPCHECK6_CPP) $(CPPCHECK7_CPP) $(CPPCHECK8_CPP) +CPPCHECK_VERILATOR_CPP = $(wildcard $(srcdir)/src/V3*.cpp $(srcdir)/src/Verilator*.cpp) +CPPCHECK_RUNTIME_CPP = $(wildcard $(srcdir)/include/*.cpp) +CPPCHECK_VLC_CPP = $(wildcard $(srcdir)/src/Vlc*.cpp) +CPPCHECK_EXAMPLES_CPP = $(wildcard $(srcdir)/examples/*/*.cpp) +CHECK_CPP = $(CPPCHECK_VERILATOR_CPP) $(CPPCHECK_RUNTIME_CPP) $(CPPCHECK_VLC_CPP) $(CPPCHECK_EXAMPLES_CPP) CHECK_H = $(wildcard \ $(srcdir)/include/*.h \ $(srcdir)/src/*.h ) @@ -409,12 +404,14 @@ CHECK_YL = $(wildcard \ $(srcdir)/src/*.y \ $(srcdir)/src/*.l ) CPPCHECK = cppcheck +CPPCHECK_JOBS = $(shell nproc) CPPCHECK_CACHE = $(srcdir)/src/obj_dbg/cppcheck-cache CPPCHECK_FLAGS = --enable=all CPPCHECK_FLAGS += --inline-suppr CPPCHECK_FLAGS += --suppressions-list=$(srcdir)/src/cppcheck-suppressions.txt CPPCHECK_FLAGS += --cppcheck-build-dir=$(CPPCHECK_CACHE) CPPCHECK_FLAGS += -DVL_DEBUG=1 -DVL_CPPCHECK=1 -D__GNUC__=1 +CPPCHECK_FLAGS += -j$(CPPCHECK_JOBS) CPPCHECK_INC = -I$(srcdir)/include CPPCHECK_INC += -I$(srcdir)/include/gtkwave CPPCHECK_INC += -I$(srcdir)/include/vltstd @@ -425,23 +422,20 @@ $(CPPCHECK_CACHE): $(CHECK_CPP) $(CHECK_H) $(CHECK_YL) /bin/rm -rf $@ /bin/mkdir -p $@ -cppcheck: cppcheck-1 cppcheck-2 cppcheck-3 cppcheck-4 cppcheck-5 cppcheck-6 cppcheck-7 cppcheck-8 -cppcheck-1: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK1_CPP) -cppcheck-2: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK2_CPP) -cppcheck-3: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK3_CPP) -cppcheck-4: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK4_CPP) -cppcheck-5: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK5_CPP) -cppcheck-6: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK6_CPP) -cppcheck-7: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK7_CPP) -cppcheck-8: | $(CPPCHECK_CACHE) - $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK8_CPP) +cppcheck-verilator: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK_VERILATOR_CPP) +cppcheck-runtime: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK_RUNTIME_CPP) +cppcheck-vlc: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK_VLC_CPP) +cppcheck-examples: | $(CPPCHECK_CACHE) + $(CPPCHECK) $(CPPCHECK_FLAGS) $(CPPCHECK_INC) $(CPPCHECK_EXAMPLES_CPP) + +cppcheck: + $(MAKE) cppcheck-vlc + $(MAKE) cppcheck-examples + $(MAKE) cppcheck-runtime + $(MAKE) cppcheck-verilator CLANGTIDY = clang-tidy CLANGTIDY_FLAGS = -config='' \ From 254eb407fff39885df682a87aa5a7b3d174ad46a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 20 Aug 2025 21:00:40 -0400 Subject: [PATCH 203/252] Commentary: Changes update --- Changes | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 157f59d8c..d1905b720 100644 --- a/Changes +++ b/Changes @@ -21,6 +21,7 @@ Verilator 5.039 devel * Add PROTOTYPEMIS error on missing and mismatching prototypes (#6206) (#6207). [Alex Solomatnikov] * Add error when trying to assign class object to variable of non-class types (#6237). [Igor Zaworski, Antmicro Ltd.] * Add ALWNEVER warning, for `always @*` that never execute (#6291). +* Add separate coverage counters for toggles 0->1 and 1->0 (#6086). [Ryszard Rozak, Antmicro Ltd.] * Add error on class 'function static'. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] @@ -35,12 +36,13 @@ Verilator 5.039 devel * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Support parameter resolution of 1D unpacked array slices (#6257) (#6268). [Michael Bedford Taylor] +* Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Improve `--skip-identical` to skip on identical input file contents (#6109). * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] -* Optimize more complex combinational assignments in DFG (#6205) (#6209). [Geza Lore] +* Optimize more complex combinational logic in DFG (#6205) (#6209) (#6298). [Geza Lore] * Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] * Optimize variable removal in scoped DFG (#6260). [Geza Lore] * Optimize acyclic DFG components into the original acyclic sub-graph. (#6261). [Geza Lore] @@ -80,6 +82,8 @@ Verilator 5.039 devel * Fix DFG circular driver tracing. [Geza Lore] * Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290). [Mateusz Gancarz] * Fix PowerPC support (#6292). [Sergey Fedorov] +* Fix referencing module variables above classes (#6304). [Artur Bieniek, Antmicro Ltd.] +* Fix static vars under member select (#6313). [Igor Zaworski, Antmicro Ltd.] Verilator 5.038 2025-07-08 From dc5a17fea01dca07ad3315a53de195b56e8899b2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 20 Aug 2025 21:01:34 -0400 Subject: [PATCH 204/252] Support unpacked array `with` methods (#6134). --- Changes | 1 + include/verilated_types.h | 60 +++++++++ src/V3Width.cpp | 64 ++++++---- test_regress/t/t_array_method.v | 163 ++++++++++++++---------- test_regress/t/t_array_method_unsup.out | 22 ---- test_regress/t/t_array_method_unsup.py | 19 --- test_regress/t/t_array_method_unsup.v | 34 ----- 7 files changed, 195 insertions(+), 168 deletions(-) delete mode 100644 test_regress/t/t_array_method_unsup.out delete mode 100755 test_regress/t/t_array_method_unsup.py delete mode 100644 test_regress/t/t_array_method_unsup.v diff --git a/Changes b/Changes index d1905b720..c9269bce3 100644 --- a/Changes +++ b/Changes @@ -29,6 +29,7 @@ Verilator 5.039 devel * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support bit queue streaming (#5830) (#6103). [Paul Swirhun] +* Support unpacked array `with` methods (#6134). * Support Verilog real ports as SystemC double ports (#6136) (#6158). [George Polack] * Support `$countones` in constraints (#6144 partial) (#6235). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] diff --git a/include/verilated_types.h b/include/verilated_types.h index 969fdec7c..d1d1e8119 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -1313,6 +1313,9 @@ class VlUnpacked final { using Unpacked = T_Value[N_Depth]; public: + template + using WithFuncReturnType = decltype(std::declval()(0, std::declval())); + // MEMBERS // This should be the only data member, otherwise generated static initializers need updating Unpacked m_storage; // Contents of the unpacked array @@ -1561,6 +1564,63 @@ public: return VlQueue::consV(*it); } + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_storage) out += i; + return out; + } + template + T_Value r_sum(T_Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_storage) out += with_func(0, i); + return out; + } + T_Value r_product() const { + T_Value out = T_Value(1); + for (const auto& i : m_storage) out *= i; + return out; + } + template + T_Value r_product(T_Func with_func) const { + T_Value out = T_Value(1); + for (const auto& i : m_storage) out *= with_func(0, i); + return out; + } + T_Value r_and() const { + if (m_storage.empty()) return T_Value(0); // The big three do it this way + T_Value out = ~T_Value(0); + for (const auto& i : m_storage) out &= i; + return out; + } + template + T_Value r_and(T_Func with_func) const { + T_Value out = ~T_Value(0); + for (const auto& i : m_storage) out &= with_func(0, i); + return out; + } + T_Value r_or() const { + T_Value out = T_Value(0); + for (const auto& i : m_storage) out |= i; + return out; + } + template + T_Value r_or(T_Func with_func) const { + T_Value out = T_Value(0); + for (const auto& i : m_storage) out |= with_func(0, i); + return out; + } + T_Value r_xor() const { + T_Value out = T_Value(0); + for (const auto& i : m_storage) out ^= i; + return out; + } + template + T_Value r_xor(T_Func with_func) const { + T_Value out = T_Value(0); + for (const auto& i : m_storage) out ^= with_func(0, i); + return out; + } + // Dumping. Verilog: str = $sformatf("%p", assoc) std::string to_string() const { std::string out = "'{"; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 70f52ac73..0753c96d5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3452,16 +3452,11 @@ class WidthVisitor final : public VNVisitor { } return nullptr; } - void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg, - bool withUnsup = false) { + void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) { int narg = 0; for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { if (VN_IS(argp, With)) { - if (withUnsup) { - argp->v3warn(E_UNSUPPORTED, "Unsupported: 'with' on this method"); - } else { - argp->v3error("'with' not legal on this method"); - } + argp->v3error("'with' not legal on this method"); // Delete all arguments as nextp() otherwise dangling VL_DO_DANGLING(pushDeletep(argp->unlinkFrBackWithNext()), argp); break; @@ -4325,27 +4320,46 @@ class WidthVisitor final : public VNVisitor { } if (methodId) { - methodOkArguments(nodep, 0, 0, true /*withUnsup*/); - FileLine* const fl = nodep->fileline(); - AstNodeExpr* newp = nullptr; - for (int i = 0; i < adtypep->elementsConst(); ++i) { - AstNodeExpr* const arrayRef = nodep->fromp()->cloneTreePure(false); - AstNodeExpr* const selector = new AstArraySel{fl, arrayRef, i}; - if (!newp) { - newp = selector; - } else { - switch (methodId) { - case ARRAY_OR: newp = new AstOr{fl, newp, selector}; break; - case ARRAY_AND: newp = new AstAnd{fl, newp, selector}; break; - case ARRAY_XOR: newp = new AstXor{fl, newp, selector}; break; - case ARRAY_SUM: newp = new AstAdd{fl, newp, selector}; break; - case ARRAY_PRODUCT: newp = new AstMul{fl, newp, selector}; break; - default: nodep->v3fatalSrc("bad case"); + AstWith* const withp + = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + nodep->findUInt32DType(), adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + if (withp) { + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + AstCMethodHard* const newp + = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "r_" + nodep->name(), withp}; + newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); + if (!nodep->firstAbovep()) newp->dtypeSetVoid(); + newp->protect(false); + newp->didWidth(true); + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; + } else { + // TODO use this code for small vectors, otherwise use runtime reduction. + // Historically we expand all vectors. + FileLine* const fl = nodep->fileline(); + AstNodeExpr* newp = nullptr; + for (int i = 0; i < adtypep->elementsConst(); ++i) { + AstNodeExpr* const arrayRef = nodep->fromp()->cloneTreePure(false); + AstNodeExpr* const selector = new AstArraySel{fl, arrayRef, i}; + if (!newp) { + newp = selector; + } else { + switch (methodId) { + case ARRAY_OR: newp = new AstOr{fl, newp, selector}; break; + case ARRAY_AND: newp = new AstAnd{fl, newp, selector}; break; + case ARRAY_XOR: newp = new AstXor{fl, newp, selector}; break; + case ARRAY_SUM: newp = new AstAdd{fl, newp, selector}; break; + case ARRAY_PRODUCT: newp = new AstMul{fl, newp, selector}; break; + default: nodep->v3fatalSrc("bad case"); + } } } + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); } - nodep->replaceWith(newp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); } else if (AstCMethodHard* newp = methodCallArray(nodep, adtypep)) { newp->protect(false); newp->didWidth(true); diff --git a/test_regress/t/t_array_method.v b/test_regress/t/t_array_method.v index f0447ce0b..0ad0d177f 100644 --- a/test_regress/t/t_array_method.v +++ b/test_regress/t/t_array_method.v @@ -4,90 +4,117 @@ // any use, without warranty, 2019 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off `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); `define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) != (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); +// verilog_format: on -module t (/*AUTOARG*/); - initial begin - int q[5]; - int qv[$]; // Value returns - int qi[$]; // Index returns - int i; - string v; +module t; + initial begin + int q[5]; + int qv[$]; // Value returns + int qi[$]; // Index returns + int i; + string v; - q = '{1, 2, 2, 4, 3}; - `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + q = '{1, 2, 2, 4, 3}; + `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); - // NOT tested: with ... selectors + // NOT tested: with ... selectors - q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); - q.sort with (item == 2); - `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); - q.sort(x) with (x == 3); - `checkp(q, "'{'h1, 'h4, 'h2, 'h2, 'h3} "); + q.sort; + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + q.sort with (item == 2); + `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); + q.sort(x) with (x == 3); + `checkp(q, "'{'h1, 'h4, 'h2, 'h2, 'h3} "); - q.rsort; - `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); - q.rsort with (item == 2); - `checkp(q, "'{'h2, 'h2, 'h4, 'h3, 'h1} "); + q.rsort; + `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + q.rsort with (item == 2); + `checkp(q, "'{'h2, 'h2, 'h4, 'h3, 'h1} "); - qv = q.unique; - `checkp(qv, "'{'h2, 'h4, 'h3, 'h1} "); - qi = q.unique_index; qi.sort; - `checkp(qi, "'{'h0, 'h2, 'h3, 'h4} "); - q.reverse; - `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); - q.shuffle(); q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + qv = q.unique; + `checkp(qv, "'{'h2, 'h4, 'h3, 'h1} "); + qi = q.unique_index; + qi.sort; + `checkp(qi, "'{'h0, 'h2, 'h3, 'h4} "); + q.reverse; + `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); + q.shuffle(); + q.sort; + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); - // These require an with clause or are illegal - // TODO add a lint check that with clause is provided - qv = q.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); - qv = q.find_first with (item == 2); - `checkp(qv, "'{'h2} "); - qv = q.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + // These require an with clause or are illegal + // TODO add a lint check that with clause is provided + qv = q.find with (item == 2); + `checkp(qv, "'{'h2, 'h2} "); + qv = q.find_first with (item == 2); + `checkp(qv, "'{'h2} "); + qv = q.find_last with (item == 2); + `checkp(qv, "'{'h2} "); - qv = q.find with (item == 20); - `checkp(qv, "'{}"); - qv = q.find_first with (item == 20); - `checkp(qv, "'{}"); - qv = q.find_last with (item == 20); - `checkp(qv, "'{}"); + qv = q.find with (item == 20); + `checkp(qv, "'{}"); + qv = q.find_first with (item == 20); + `checkp(qv, "'{}"); + qv = q.find_last with (item == 20); + `checkp(qv, "'{}"); - qi = q.find_index with (item == 2); qi.sort; - `checkp(qi, "'{'h1, 'h2} "); - qi = q.find_first_index with (item == 2); - `checkp(qi, "'{'h1} "); - qi = q.find_last_index with (item == 2); - `checkp(qi, "'{'h2} "); + qi = q.find_index with (item == 2); + qi.sort; + `checkp(qi, "'{'h1, 'h2} "); + qi = q.find_first_index with (item == 2); + `checkp(qi, "'{'h1} "); + qi = q.find_last_index with (item == 2); + `checkp(qi, "'{'h2} "); - qi = q.find_index with (item == 20); qi.sort; - `checkp(qi, "'{}"); - qi = q.find_first_index with (item == 20); - `checkp(qi, "'{}"); - qi = q.find_last_index with (item == 20); - `checkp(qi, "'{}"); + qi = q.find_index with (item == 20); + qi.sort; + `checkp(qi, "'{}"); + qi = q.find_first_index with (item == 20); + `checkp(qi, "'{}"); + qi = q.find_last_index with (item == 20); + `checkp(qi, "'{}"); - qv = q.min; - `checkp(qv, "'{'h1} "); - qv = q.max; - `checkp(qv, "'{'h4} "); + qv = q.min; + `checkp(qv, "'{'h1} "); + qv = q.max; + `checkp(qv, "'{'h4} "); - // Reduction methods + // Reduction methods - i = q.sum; `checkh(i, 32'hc); - i = q.product; `checkh(i, 32'h30); + i = q.sum; + `checkh(i, 32'hc); + i = q.product; + `checkh(i, 32'h30); - q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; - i = q.and; `checkh(i, 32'b1000); - i = q.or; `checkh(i, 32'b1110); - i = q.xor; `checkh(i, 32'ha); + q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; + i = q.and; + `checkh(i, 32'b1000); + i = q.or; + `checkh(i, 32'b1110); + i = q.xor; + `checkh(i, 32'ha); - $write("*-* All Finished *-*\n"); - $finish; - end + q = '{1, 2, 2, 4, 3}; + // `checkp(q, "'{1, 2, 2, 4, 3} "); + + i = q.sum with (item + 1); + `checkh(i, 32'h11); + i = q.product with (item + 1); + `checkh(i, 32'h168); + + q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; + i = q.and with (item + 1); + `checkh(i, 32'b1001); + i = q.or with (item + 1); + `checkh(i, 32'b1111); + i = q.xor with (item + 1); + `checkh(i, 32'hb); + + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_array_method_unsup.out b/test_regress/t/t_array_method_unsup.out deleted file mode 100644 index 9a5d614e1..000000000 --- a/test_regress/t/t_array_method_unsup.out +++ /dev/null @@ -1,22 +0,0 @@ -%Error-UNSUPPORTED: t/t_array_method_unsup.v:23:17: Unsupported: 'with' on this method - : ... note: In instance 't' - 23 | i = q.sum with (item + 1); do if ((i) !== (32'h11)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",23, (i), (32'h11)); $stop; end while(0);; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_array_method_unsup.v:24:21: Unsupported: 'with' on this method - : ... note: In instance 't' - 24 | i = q.product with (item + 1); do if ((i) !== (32'h168)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",24, (i), (32'h168)); $stop; end while(0);; - | ^~~~ -%Error-UNSUPPORTED: t/t_array_method_unsup.v:27:17: Unsupported: 'with' on this method - : ... note: In instance 't' - 27 | i = q.and with (item + 1); do if ((i) !== (32'b1001)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",27, (i), (32'b1001)); $stop; end while(0);; - | ^~~~ -%Error-UNSUPPORTED: t/t_array_method_unsup.v:28:16: Unsupported: 'with' on this method - : ... note: In instance 't' - 28 | i = q.or with (item + 1); do if ((i) !== (32'b1111)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",28, (i), (32'b1111)); $stop; end while(0);; - | ^~~~ -%Error-UNSUPPORTED: t/t_array_method_unsup.v:29:17: Unsupported: 'with' on this method - : ... note: In instance 't' - 29 | i = q.xor with (item + 1); do if ((i) !== (32'hb)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",29, (i), (32'hb)); $stop; end while(0);; - | ^~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_array_method_unsup.py b/test_regress/t/t_array_method_unsup.py deleted file mode 100755 index 966dc53da..000000000 --- a/test_regress/t/t_array_method_unsup.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/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(fails=test.vlt_all, expect_filename=test.golden_filename) - -if not test.vlt_all: - test.execute() - -test.passes() diff --git a/test_regress/t/t_array_method_unsup.v b/test_regress/t/t_array_method_unsup.v deleted file mode 100644 index 2308f56a0..000000000 --- a/test_regress/t/t_array_method_unsup.v +++ /dev/null @@ -1,34 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2023 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); -`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) != (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); - -module t (/*AUTOARG*/); - initial begin - int q[5]; - int qv[$]; // Value returns - int qi[$]; // Index returns - int i; - - q = '{1, 2, 2, 4, 3}; - `checkp(q, "'{1, 2, 2, 4, 3} "); - - // Reduction methods - - i = q.sum with (item + 1); `checkh(i, 32'h11); - i = q.product with (item + 1); `checkh(i, 32'h168); - - q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; - i = q.and with (item + 1); `checkh(i, 32'b1001); - i = q.or with (item + 1); `checkh(i, 32'b1111); - i = q.xor with (item + 1); `checkh(i, 32'hb); - - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule From bd91b619adf928dd40a3a1181a15eeb5de1143b3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 20 Aug 2025 21:19:42 -0400 Subject: [PATCH 205/252] Support `$fread` with missing start (#6125). --- Changes | 1 + src/verilog.y | 1 + test_regress/t/t_sys_fread.out | 8 +- test_regress/t/t_sys_fread.v | 150 ++++++++++++++++++--------------- 4 files changed, 87 insertions(+), 73 deletions(-) diff --git a/Changes b/Changes index c9269bce3..58c07fc5d 100644 --- a/Changes +++ b/Changes @@ -29,6 +29,7 @@ Verilator 5.039 devel * Support randomization of scope variables with 'std::randomize()' (#5438) (#6185). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support bit queue streaming (#5830) (#6103). [Paul Swirhun] +* Support `$fread` with missing start (#6125). [Iztok Jeras] * Support unpacked array `with` methods (#6134). * Support Verilog real ports as SystemC double ports (#6136) (#6158). [George Polack] * Support `$countones` in constraints (#6144 partial) (#6235). [Ryszard Rozak, Antmicro Ltd.] diff --git a/src/verilog.y b/src/verilog.y index d2fd145c0..0befaa115 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4500,6 +4500,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_FREAD '(' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, nullptr}; } | yD_FREAD '(' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, nullptr}; } | yD_FREAD '(' expr ',' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, $9}; } + | yD_FREAD '(' expr ',' expr ',' ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, $8}; } | yD_FREWIND '(' expr ')' { $$ = new AstFRewind{$1, $3}; } | yD_FLOOR '(' expr ')' { $$ = new AstFloorD{$1, $3}; } | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF{$1, *$5, $3, $6}; } diff --git a/test_regress/t/t_sys_fread.out b/test_regress/t/t_sys_fread.out index a10cdbc6d..6b6f314a8 100644 --- a/test_regress/t/t_sys_fread.out +++ b/test_regress/t/t_sys_fread.out @@ -1,7 +1,7 @@ Dump: r_i: 00010203 r_upb: 0e 0d 0c 0b 0a 09 08 07 06 05 04 - r_dnb: 19 18 17 16 15 14 13 12 11 10 0f + r_dnb: 0f 10 11 12 13 14 15 16 17 18 19 r_ups: 2e2f 2c2d 2a2b 2829 2627 2425 2223 2021 1e1f 1c1d 1a1b r_dns: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0041 0243 0445 r_upi: 6e6f7071 6a6b6c6d 66676869 62636465 5e5f6061 5a5b5c5d 56575859 52535455 4e4f5051 4a4b4c4d 46474849 @@ -13,9 +13,9 @@ Dump: Dump: r_i: ffffffff - r_upb: 05 04 03 02 01 00 ff ff ff ff ff - r_dnb: ff ff ff ff ff ff ff ff ff ff ff - r_ups: 3fff 3fff 3fff 3fff 0809 0607 3fff 3fff 3fff 3fff 3fff + r_upb: 05 04 03 02 01 00 ff 0e ff ff 0c + r_dnb: 0d ff ff 0f ff 06 07 08 09 0a 0b + r_ups: 3fff 3fff 3fff 3fff 1213 1011 3fff 3fff 3fff 3fff 3fff r_dns: 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff r_upi: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff r_dni: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff diff --git a/test_regress/t/t_sys_fread.v b/test_regress/t/t_sys_fread.v index eb9d2a233..078a1be31 100644 --- a/test_regress/t/t_sys_fread.v +++ b/test_regress/t/t_sys_fread.v @@ -4,87 +4,99 @@ // any use, without warranty, 2019 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off `define STRINGIFY(x) `"x`" `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); +// verilog_format: on //====================================================================== module t; - integer file; - integer r_i; - byte r_upb[20:10]; - byte r_dnb[20:10]; - reg [13:0] r_ups[20:10]; - reg [13:0] r_dns[10:20]; - reg [30:0] r_upi[20:10]; - reg [30:0] r_dni[10:20]; - reg [61:0] r_upq[20:10]; - reg [61:0] r_dnq[10:20]; - reg [71:0] r_upw[20:10]; - reg [71:0] r_dnw[10:20]; + integer file; + integer r_i; + byte r_upb[20:10]; + byte r_dnb[10:20]; + reg [13:0] r_ups[20:10]; + reg [13:0] r_dns[10:20]; + reg [30:0] r_upi[20:10]; + reg [30:0] r_dni[10:20]; + reg [61:0] r_upq[20:10]; + reg [61:0] r_dnq[10:20]; + reg [71:0] r_upw[20:10]; + reg [71:0] r_dnw[10:20]; - task clear; - // Initialize memories to zero, - // avoid differences between 2-state and 4-state. - r_i = ~0; - foreach (r_upb[i]) r_upb[i] = ~0; - foreach (r_dnb[i]) r_dnb[i] = ~0; - foreach (r_ups[i]) r_ups[i] = ~0; - foreach (r_dns[i]) r_dns[i] = ~0; - foreach (r_upi[i]) r_upi[i] = ~0; - foreach (r_dni[i]) r_dni[i] = ~0; - foreach (r_upq[i]) r_upq[i] = ~0; - foreach (r_dnq[i]) r_dnq[i] = ~0; - foreach (r_upw[i]) r_upw[i] = ~0; - foreach (r_dnw[i]) r_dnw[i] = ~0; + task clear; + // Initialize memories to zero, + // avoid differences between 2-state and 4-state. + r_i = ~0; + foreach (r_upb[i]) r_upb[i] = ~0; + foreach (r_dnb[i]) r_dnb[i] = ~0; + foreach (r_ups[i]) r_ups[i] = ~0; + foreach (r_dns[i]) r_dns[i] = ~0; + foreach (r_upi[i]) r_upi[i] = ~0; + foreach (r_dni[i]) r_dni[i] = ~0; + foreach (r_upq[i]) r_upq[i] = ~0; + foreach (r_dnq[i]) r_dnq[i] = ~0; + foreach (r_upw[i]) r_upw[i] = ~0; + foreach (r_dnw[i]) r_dnw[i] = ~0; - // Open file - $fclose(file); - file = $fopen({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_fread.mem"}, "r"); - if ($feof(file)) $stop; - endtask + // Open file + $fclose(file); + file = $fopen({`STRINGIFY(`TEST_OBJ_DIR), "/t_sys_fread.mem"}, "r"); + if ($feof(file)) $stop; + endtask - task dump; - $write("Dump:"); - $write("\n r_i:"); $write(" %x",r_i); - $write("\n r_upb:"); foreach (r_upb[i]) $write(" %x", r_upb[i]); - $write("\n r_dnb:"); foreach (r_dnb[i]) $write(" %x", r_dnb[i]); - $write("\n r_ups:"); foreach (r_ups[i]) $write(" %x", r_ups[i]); - $write("\n r_dns:"); foreach (r_dns[i]) $write(" %x", r_dns[i]); - $write("\n r_upi:"); foreach (r_upi[i]) $write(" %x", r_upi[i]); - $write("\n r_dni:"); foreach (r_dni[i]) $write(" %x", r_dni[i]); - $write("\n r_upq:"); foreach (r_upq[i]) $write(" %x", r_upq[i]); - $write("\n r_dnq:"); foreach (r_dnq[i]) $write(" %x", r_dnq[i]); - $write("\n r_upw:"); foreach (r_upw[i]) $write(" %x", r_upw[i]); - $write("\n r_dnw:"); foreach (r_dnw[i]) $write(" %x", r_dnw[i]); - $write("\n\n"); - endtask + task dump; + // verilog_format: off + $write("Dump:"); + $write("\n r_i:"); $write(" %x",r_i); + $write("\n r_upb:"); foreach (r_upb[i]) $write(" %x", r_upb[i]); + $write("\n r_dnb:"); foreach (r_dnb[i]) $write(" %x", r_dnb[i]); + $write("\n r_ups:"); foreach (r_ups[i]) $write(" %x", r_ups[i]); + $write("\n r_dns:"); foreach (r_dns[i]) $write(" %x", r_dns[i]); + $write("\n r_upi:"); foreach (r_upi[i]) $write(" %x", r_upi[i]); + $write("\n r_dni:"); foreach (r_dni[i]) $write(" %x", r_dni[i]); + $write("\n r_upq:"); foreach (r_upq[i]) $write(" %x", r_upq[i]); + $write("\n r_dnq:"); foreach (r_dnq[i]) $write(" %x", r_dnq[i]); + $write("\n r_upw:"); foreach (r_upw[i]) $write(" %x", r_upw[i]); + $write("\n r_dnw:"); foreach (r_dnw[i]) $write(" %x", r_dnw[i]); + $write("\n\n"); + // verilog_format: on + endtask - integer code; + integer code; - initial begin - clear; - code = $fread(r_i, file); `checkd(code, 4); - code = $fread(r_upb, file); `checkd(code, 11); - code = $fread(r_dnb, file); `checkd(code, 11); - code = $fread(r_ups, file); `checkd(code, 22); - code = $fread(r_dns, file); `checkd(code, 22); - code = $fread(r_upi, file); `checkd(code, 44); - code = $fread(r_dni, file); `checkd(code, 44); - code = $fread(r_upq, file); `checkd(code, 88); - code = $fread(r_dnq, file); `checkd(code, 88); - code = $fread(r_upw, file); `checkd(code, 99); - code = $fread(r_dnw, file); `checkd(code, 99); - dump; + initial begin + // verilog_format: off + clear; + code = $fread(r_i, file); `checkd(code, 4); + code = $fread(r_upb, file); `checkd(code, 11); + code = $fread(r_dnb, file); `checkd(code, 11); + code = $fread(r_ups, file); `checkd(code, 22); + code = $fread(r_dns, file); `checkd(code, 22); + code = $fread(r_upi, file); `checkd(code, 44); + code = $fread(r_dni, file); `checkd(code, 44); + code = $fread(r_upq, file); `checkd(code, 88); + code = $fread(r_dnq, file); `checkd(code, 88); + code = $fread(r_upw, file); `checkd(code, 99); + code = $fread(r_dnw, file); `checkd(code, 99); + dump; - clear; - code = $fread(r_upb, file, 15); `checkd(code, 6); - // Bug where fread in if() broke. - if ($fread(r_ups, file, 15, 2) != 4) $stop; - dump; + clear; + code = $fread(r_upb, file, 15); `checkd(code, 6); + code = $fread(r_dnb, file, 15); `checkd(code, 6); + code = $fread(r_upb, file, , 1); `checkd(code, 1); + code = $fread(r_dnb, file, , 1); `checkd(code, 1); + code = $fread(r_upb, file, 13, 1); `checkd(code, 1); + code = $fread(r_dnb, file, 13, 1); `checkd(code, 1); + // verilog_format: on - $write("*-* All Finished *-*\n"); - $finish; - end + // Bug where fread in if() broke. + if ($fread(r_ups, file, 15, 2) != 4) $stop; + dump; + + $write("*-* All Finished *-*\n"); + $finish; + end endmodule From 327d55d13d8bf89613ee2da9b7232d411c5636fe Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 21 Aug 2025 09:43:37 +0100 Subject: [PATCH 206/252] Internals: Fix remaining cppcheck errors (#6319) Fixed the non const-related issue and added suppressions for the const ones. With that `make cppcheck` should be clean. --- include/verilated_types.h | 2 +- src/V3Ast.cpp | 5 +- src/V3Ast.h | 4 +- src/V3AstNodeExpr.h | 1 + src/V3EmitCBase.h | 1 + src/V3EmitCImp.cpp | 2 +- src/V3EmitCModel.cpp | 4 +- src/V3EmitCSyms.cpp | 11 +-- src/V3EmitMk.cpp | 2 +- src/V3EmitMkJson.cpp | 6 +- src/V3Error.cpp | 9 +- src/V3Error.h | 8 +- src/V3File.cpp | 3 +- src/V3FileLine.cpp | 2 +- src/V3Force.cpp | 3 +- src/V3Fork.cpp | 2 +- src/V3Global.cpp | 2 +- src/V3Graph.cpp | 5 +- src/V3Graph.h | 7 +- src/V3GraphAcyc.cpp | 3 + src/V3GraphAlg.cpp | 12 +-- src/V3HierBlock.cpp | 8 +- src/V3Inline.cpp | 6 +- src/V3Inst.cpp | 4 +- src/V3LinkCells.cpp | 4 +- src/V3LinkLValue.cpp | 3 +- src/V3LinkLevel.cpp | 2 + src/V3LinkResolve.cpp | 8 +- src/V3Number.cpp | 12 +-- src/V3Number.h | 2 +- src/V3Options.cpp | 3 + src/V3OrderParallel.cpp | 6 +- src/V3Param.cpp | 4 +- src/V3ParseGrammar.cpp | 2 +- src/V3ParseImp.cpp | 4 +- src/V3ParseImp.h | 8 +- src/V3PreLex.l | 4 +- src/V3PreProc.cpp | 9 +- src/V3PreShell.cpp | 2 +- src/V3Premit.cpp | 2 +- src/V3ProtectLib.cpp | 8 +- src/V3Sched.cpp | 5 +- src/V3SchedTiming.cpp | 2 +- src/V3SchedVirtIface.cpp | 2 +- src/V3Scope.cpp | 4 +- src/V3Scoreboard.cpp | 2 +- src/V3SplitVar.cpp | 3 +- src/V3StackCount.cpp | 2 +- src/V3StatsReport.cpp | 2 +- src/V3String.cpp | 8 +- src/V3SymTable.h | 4 +- src/V3TSP.cpp | 2 + src/V3Table.cpp | 2 + src/V3TraceDecl.cpp | 1 + src/V3Tristate.cpp | 5 -- src/V3Undriven.cpp | 3 +- src/V3Unknown.cpp | 2 +- src/V3VariableOrder.cpp | 1 + src/V3Waiver.cpp | 2 +- src/V3Width.cpp | 35 ++++---- src/VlcTest.h | 2 +- src/cppcheck-suppressions.txt | 113 +++++++++++++++++++++++++ src/verilog.y | 2 +- test_regress/t/t_dist_warn_coverage.py | 1 - 64 files changed, 266 insertions(+), 134 deletions(-) diff --git a/include/verilated_types.h b/include/verilated_types.h index d1d1e8119..41dc67587 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -495,7 +495,7 @@ public: private: // MEMBERS Deque m_deque; // State of the assoc array - T_Value m_defaultValue; // Default value + T_Value m_defaultValue{}; // Default value public: // CONSTRUCTORS diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index dea6e32f5..51051f330 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1477,7 +1477,7 @@ string AstNode::instanceStr() const { } return ""; } -void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { +void AstNode::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { // Don't look for instance name when warning is disabled. // In case of large number of warnings, this can // take significant amount of time @@ -1497,7 +1497,8 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s(). m_fileline->v3errorEnd(nsstr, instanceStrExtra); } } -void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { +void AstNode::v3errorEndFatal(const std::ostringstream& str) const + VL_RELEASE(V3Error::s().m_mutex) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE VL_UNREACHABLE; diff --git a/src/V3Ast.h b/src/V3Ast.h index 1e5403b96..7e61686ef 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2439,8 +2439,8 @@ public: static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2); // METHODS - dump and error - void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); - void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN + void v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); + void v3errorEndFatal(const std::ostringstream& str) const VL_ATTR_NORETURN VL_RELEASE(V3Error::s().m_mutex); string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnContextPrimary(); diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ce7241113..e37848d22 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -657,6 +657,7 @@ public: dtypeFrom(dtp); } // cppcheck-suppress constParameterPointer + // cppcheck-suppress constParameterCallback AstCast(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) : ASTGEN_SUPER_Cast(fl) { this->fromp(fromp); diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 82fef00fa..e9baa02d0 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -98,6 +98,7 @@ public: // Sets ofp() and outFileNodep() to the given pointers, without closing a file these pointers // currently point to. void setOutputFile(V3OutCFile* ofp, AstCFile* nodep) { + // cppcheck-suppress danglingLifetime // ofp is often on stack in caller, it's fine. m_ofp = ofp; m_outFileNodep = nodep; } diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index cfe543dd4..febb88222 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -907,7 +907,7 @@ class EmitCTrace final : EmitCFunc { puts(");\n"); } - void emitTraceValue(AstTraceInc* nodep, int arrayindex) { + void emitTraceValue(const AstTraceInc* nodep, int arrayindex) { if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { const AstVar* const varp = varrefp->varp(); if (varp->isEvent()) puts("&"); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index dce801f25..0b9f300de 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -693,7 +693,7 @@ class EmitCModel final : public EmitCFunc { // VISITORS public: - explicit EmitCModel(AstNetlist* netlistp) { main(netlistp->topModulep()); } + explicit EmitCModel(AstNodeModule* topModulep) { main(topModulep); } }; //###################################################################### @@ -701,5 +701,5 @@ public: void V3EmitC::emitcModel() { UINFO(2, __FUNCTION__ << ":"); - { EmitCModel{v3Global.rootp()}; } + { EmitCModel{v3Global.rootp()->topModulep()}; } } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 7ad0a7f23..1f9f7cf28 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -91,8 +91,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { struct CmpDpi final { bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const { if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) { - // cppcheck-suppress comparisonOfFuncReturningBoolError - return lhsp->dpiImportPrototype() < rhsp->dpiImportPrototype(); + return rhsp->dpiImportPrototype(); } return lhsp->name() < rhsp->name(); } @@ -285,11 +284,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { if (v3Global.dpi()) { // add dpi scopes to m_scopeNames if not already there - for (const auto& scp : m_dpiScopeNames) { - if (m_scopeNames.find(scp.first) == m_scopeNames.end()) { - m_scopeNames.emplace(scp.first, scp.second); - } - } + for (const auto& scp : m_dpiScopeNames) m_scopeNames.emplace(scp.first, scp.second); } // Sort by names, so line/process order matters less @@ -358,8 +353,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { ScopeFuncData(nodep, m_cfuncp, m_modp)); } else { if (m_dpiScopeNames.find(nodep->scopeDpiName()) == m_dpiScopeNames.end()) { - // cppcheck-suppress stlFindInsert m_dpiScopeNames.emplace(nodep->scopeDpiName(), + // cppcheck-suppress stlFindInsert ScopeData{nodep, nodep->scopeDpiName(), nodep->scopePrettyDpiName(), "", timeunit, "SCOPE_OTHER"}); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 6b96ebcca..2b5834d0d 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -273,7 +273,7 @@ private: m_concatenableListsByDescSize.resize(totalBucketsNum); // Recalculate stats concatenableFilesTotalScore = 0; - for (WorkList* listp : m_concatenableListsByDescSize) { + for (const WorkList* const listp : m_concatenableListsByDescSize) { concatenableFilesTotalScore += listp->m_totalScore; } } diff --git a/src/V3EmitMkJson.cpp b/src/V3EmitMkJson.cpp index 843e7fe87..f272a31f4 100644 --- a/src/V3EmitMkJson.cpp +++ b/src/V3EmitMkJson.cpp @@ -125,9 +125,9 @@ class V3EmitMkJsonEmitter final { std::vector childDeps; std::vector sources; - for (const auto& childr : children) { - childDeps.emplace_back((childr)->hierPrefix()); - sources.emplace_back(makeDir + "/" + childr->hierWrapperFilename(true)); + for (const V3HierBlock* const childp : children) { + childDeps.emplace_back(childp->hierPrefix()); + sources.emplace_back(makeDir + "/" + childp->hierWrapperFilename(true)); } const string vFile = hblockp->vFileIfNecessary(); diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 67c7566bf..662625feb 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -132,8 +132,8 @@ void V3ErrorGuarded::v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) { m_errorSuppressed = false; } -void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline) - VL_REQUIRES(m_mutex) { +void V3ErrorGuarded::v3errorEnd(const std::ostringstream& sstr, const string& extra, + FileLine* fileline) VL_REQUIRES(m_mutex) { static bool s_firedTooMany = false; v3errorEndGuts(sstr, extra, fileline); m_message.clear(); @@ -151,7 +151,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra, F } // cppcheck-has-bug-suppress constParameter -void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extra, +void V3ErrorGuarded::v3errorEndGuts(const std::ostringstream& sstr, const string& extra, FileLine* fileline) VL_REQUIRES(m_mutex) { // 'extra' is appended to the message, and is is excluded in check for // duplicate messages. Currently used for reporting instance name. @@ -260,6 +260,7 @@ void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extr } if (m_message.code().severityFatal()) { static bool inFatal = false; + // cppcheck-suppress duplicateConditionalAssign // Used by VlcMain.cpp if (!inFatal) { inFatal = true; #ifndef V3ERROR_NO_GLOBAL_ @@ -380,7 +381,7 @@ std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* f v3errorPrep(code) << file << ":" << std::dec << line << ": "; return v3errorStr(); } -void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline) +void V3Error::v3errorEnd(const std::ostringstream& sstr, const string& extra, FileLine* fileline) VL_RELEASE(s().m_mutex) { s().v3errorEnd(sstr, extra, fileline); V3Error::s().m_mutex.unlock(); diff --git a/src/V3Error.h b/src/V3Error.h index 1df93054c..7e5ab7d30 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -393,9 +393,9 @@ private: // METHODS 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, FileLine* fileline) + void v3errorEnd(const std::ostringstream& sstr, const string& extra, FileLine* fileline) VL_REQUIRES(m_mutex); - void v3errorEndGuts(std::ostringstream& sstr, const string& extra, FileLine* fileline) + void v3errorEndGuts(const std::ostringstream& sstr, const string& extra, FileLine* fileline) VL_REQUIRES(m_mutex); public: @@ -586,7 +586,7 @@ public: VL_ACQUIRE(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, FileLine* fileline) + static void v3errorEnd(const std::ostringstream& sstr, const string& extra, FileLine* fileline) VL_RELEASE(s().m_mutex); static void vlAbort(); }; @@ -728,7 +728,7 @@ void v3errorEndFatal(std::ostringstream& sstr) static int level = -1; \ if (VL_UNLIKELY(level < 0)) { \ std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \ - tag[0] = std::tolower(tag[0]); \ + if (!tag.empty()) tag[0] = std::tolower(tag[0]); \ const unsigned debugTag = v3Global.opt.debugLevel(tag); \ const unsigned debugSrc = v3Global.opt.debugSrcLevel(__FILE__); \ const unsigned debugLevel = debugTag >= debugSrc ? debugTag : debugSrc; \ diff --git a/src/V3File.cpp b/src/V3File.cpp index d2c7cf973..a0731db43 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -32,7 +32,7 @@ #include // clang-format off -#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(VL_CPPCHECK) # define INFILTER_PIPE // Allow pipe filtering. Needs fork() #endif @@ -389,6 +389,7 @@ private: close(fd); return true; } + // cppcheck-suppress constParameterReference bool readContentsFilter(const string& filename, StrList& outl) { (void)filename; // Prevent unused variable warning (void)outl; // Prevent unused variable warning diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index d65db10da..7e7cae443 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -499,9 +499,9 @@ string FileLine::prettySource() const VL_MT_SAFE { string FileLine::warnContext() const { if (!v3Global.opt.context()) return ""; - string out; if (firstLineno() == lastLineno() && firstColumn()) { const string sourceLine = prettySource(); + string out; // Don't show super-long lines as can fill screen and unlikely to help user if (!sourceLine.empty() && sourceLine.length() < SHOW_SOURCE_MAX_LENGTH && sourceLine.length() >= static_cast(lastColumn() - 1)) { diff --git a/src/V3Force.cpp b/src/V3Force.cpp index d2e5955a4..6042033d9 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -336,7 +336,8 @@ class ForceConvertVisitor final : public VNVisitor { public: // CONSTRUCTOR - explicit ForceConvertVisitor(AstNetlist* nodep, ForceState& state) + // cppcheck-suppress constParameterCallback + ForceConvertVisitor(AstNetlist* nodep, ForceState& state) : m_state{state} { // Transform all force and release statements iterateAndNextNull(nodep->modulesp()); diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index c5faa2110..4f9e7d1da 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -548,7 +548,7 @@ class ForkVisitor final : public VNVisitor { return varp; } - AstTask* makeTask(FileLine* fl, AstNode* stmtsp, string name) { + AstTask* makeTask(FileLine* fl, AstNode* stmtsp, const std::string& name) { stmtsp = AstNode::addNext(static_cast(m_capturedVarsp), stmtsp); AstTask* const taskp = new AstTask{fl, name, stmtsp}; return taskp; diff --git a/src/V3Global.cpp b/src/V3Global.cpp index bd1c5e30a..2d797aed8 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -63,7 +63,7 @@ void V3Global::readFiles() { "work", "Cannot find verilated_std_waiver.vlt containing built-in lint waivers: "); } // Read .vlt files - for (auto& filelib : v3Global.opt.vltFiles()) { + for (const VFileLibName& filelib : v3Global.opt.vltFiles()) { parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), false, filelib.libname(), "Cannot find file containing .vlt file: "); } diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 7efb029b1..feb3d327c 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -105,7 +105,8 @@ template V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3Gr template V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3GraphVertex*); // cppcheck-has-bug-suppress constParameter -void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { +void V3GraphVertex::v3errorEnd(const std::ostringstream& str) const + VL_RELEASE(V3Error::s().m_mutex) { std::ostringstream nsstr; nsstr << str.str(); if (debug()) nsstr << "\n-vertex: " << this << '\n'; @@ -115,7 +116,7 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error V3Error::v3errorEnd(nsstr, "", nullptr); } } -void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const +void V3GraphVertex::v3errorEndFatal(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE diff --git a/src/V3Graph.h b/src/V3Graph.h index 6a8d10b42..0a3a3bb47 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -197,8 +197,6 @@ public: class V3GraphVertex VL_NOT_FINAL { VL_RTTI_IMPL_BASE(V3GraphVertex) - // Vertices may be a 'gate'/wire statement OR a variable -protected: friend class V3Graph; friend class V3GraphEdge; friend class GraphAcyc; @@ -307,8 +305,9 @@ public: bool outSize1() const { return m_outs.hasSingleElement(); } // METHODS /// Error reporting - void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; - void v3errorEndFatal(std::ostringstream& str) const + void v3errorEnd(const std::ostringstream& str) const + VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; + void v3errorEndFatal(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; /// Edges are routed around this vertex to point from "from" directly to "to" void rerouteEdges(V3Graph* graphp) VL_MT_DISABLED; diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index 91387c49e..b9330290e 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -58,8 +58,11 @@ public: V3GraphVertex* origVertexp() const { return m_origVertexp; } void setDelete() { m_deleted = true; } bool isDelete() const { return m_deleted; } + // cppcheck-suppress uselessOverride // cppcheck is wrong ... string name() const override { return m_origVertexp->name(); } + // cppcheck-suppress uselessOverride // cppcheck is wrong ... string dotColor() const override { return m_origVertexp->dotColor(); } + // cppcheck-suppress uselessOverride // cppcheck is wrong ... FileLine* fileline() const override { return m_origVertexp->fileline(); } }; diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index fc307f141..085a7291d 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -428,13 +428,13 @@ struct GraphSortEdgeCmp final { void V3Graph::sortVertices() { // Sort list of vertices by rank, then fanout - std::vector vertices; - for (V3GraphVertex& vertex : m_vertices) vertices.push_back(&vertex); - std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp()); + std::vector vertexps; + for (V3GraphVertex& vertex : m_vertices) vertexps.push_back(&vertex); + std::stable_sort(vertexps.begin(), vertexps.end(), GraphSortVertexCmp()); // Re-insert in sorted order - for (V3GraphVertex* const ip : vertices) { - m_vertices.unlink(ip); - m_vertices.linkBack(ip); + for (V3GraphVertex* const vertexp : vertexps) { + m_vertices.unlink(vertexp); + m_vertices.linkBack(vertexp); } } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index d97ebf035..a142df2fa 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -129,7 +129,7 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of V3HierBlock::StrGParams V3HierBlock::stringifyParams(const V3HierBlockParams::GParams& gparams, bool forGOption) { StrGParams strParams; - for (const auto& gparam : gparams) { + for (const AstVar* const gparam : gparams) { if (const AstConst* const constp = VN_CAST(gparam->valuep(), Const)) { string s; // Only constant parameter needs to be set to -G because already checked in @@ -262,7 +262,7 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const { *of << "--cc\n"; if (!forCMake) { - for (const auto& hierblockp : m_children) { + for (const V3HierBlock* const hierblockp : m_children) { *of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapperFilename(true) << "\n"; } *of << "-Mdir " << v3Global.opt.makeDir() << "/" << hierPrefix() << " \n"; @@ -271,7 +271,9 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const { const V3StringList& commandOpts = commandArgs(false); for (const string& opt : commandOpts) *of << opt << "\n"; *of << hierBlockArgs().front() << "\n"; - for (const auto& hierblockp : m_children) *of << hierblockp->hierBlockArgs().front() << "\n"; + for (const V3HierBlock* const hierblockp : m_children) { + *of << hierblockp->hierBlockArgs().front() << "\n"; + } *of << v3Global.opt.allArgsStringForHierBlock(false) << "\n"; } diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 82db5844d..e1bd41da2 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -383,7 +383,7 @@ class InlineRelinkVisitor final : public VNVisitor { iterateChildren(nodep); } void visit(AstNodeAssign* nodep) override { - if (AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) { + if (const AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) { if (m_initialStatic && varrefp->varp()->user2() && varrefp->varp()->user4()) { // Initial assignment to i/o we are overriding, can remove UINFO(9, "Remove InitialStatic " << nodep); @@ -400,7 +400,7 @@ class InlineRelinkVisitor final : public VNVisitor { && !VN_IS(nodep->backp(), AssignAlias) // Forced signals do not use aliases && !nodep->varp()->isForced()) { - AstVar* const varp = nodep->varp(); + const AstVar* const varp = nodep->varp(); if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) { nodep->replaceWith(constp->cloneTree(false)); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -426,7 +426,7 @@ class InlineRelinkVisitor final : public VNVisitor { if (pos == string::npos || pos == 0) { break; } else { - tryname = tryname.substr(0, pos); + tryname.resize(pos); } } iterateChildren(nodep); diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 828d4973f..aaa3d87dd 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -186,6 +186,7 @@ private: // VISITORS void visit(AstVar* nodep) override { + // cppcheck-suppress constVariablePointer AstNode* const dtp = nodep->dtypep()->skipRefp(); if (VN_IS(dtp, UnpackArrayDType) && VN_IS(VN_AS(dtp, UnpackArrayDType)->subDTypep()->skipRefp(), IfaceRefDType)) { @@ -236,6 +237,7 @@ private: m_cellRangep = nodep->rangep(); AstVar* const ifaceVarp = VN_CAST(nodep->nextp(), Var); + // cppcheck-suppress constVariablePointer AstNodeDType* const ifaceVarDtp = ifaceVarp ? ifaceVarp->dtypep()->skipRefp() : nullptr; const bool isIface @@ -579,7 +581,7 @@ public: class InstStatic final { InstStatic() = default; // Static class - static AstNodeExpr* extendOrSel(FileLine* fl, AstNodeExpr* rhsp, AstNode* cmpWidthp) { + static AstNodeExpr* extendOrSel(FileLine* fl, AstNodeExpr* rhsp, const AstNode* cmpWidthp) { if (cmpWidthp->width() > rhsp->width()) { rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS{fl, rhsp}) : static_cast(new AstExtend{fl, rhsp})); diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 381eb75a7..eb2d71b6d 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -637,14 +637,14 @@ class LinkCellsVisitor final : public VNVisitor { } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); - } else if (!libFoundp && globalFoundp && globalFoundp != nodep) { + } else if (globalFoundp && globalFoundp != nodep) { // ...__LIB__ stripped by prettyName const string newName = nodep->libname() + "__LIB__" + nodep->origName(); UINFO(9, "Module rename as in multiple libraries " << newName << " <- " << nodep); insertModInLib(nodep->origName(), nodep->libname(), nodep); // Original name nodep->name(newName); insertModInLib(nodep->name(), "__GLOBAL", nodep); - } else if (!libFoundp) { + } else { insertModInLib(nodep->origName(), nodep->libname(), nodep); insertModInLib(nodep->name(), "__GLOBAL", nodep); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 39de33ef1..6194bb573 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -99,7 +99,7 @@ class LinkLValueVisitor final : public VNVisitor { { m_setRefLvalue = VAccess::WRITE; m_setContinuously = VN_IS(nodep, AssignW) || VN_IS(nodep, AssignAlias); - if (AstAssignW* assignwp = VN_CAST(nodep, AssignW)) { + if (const AstAssignW* const assignwp = VN_CAST(nodep, AssignW)) { if (assignwp->strengthSpecp()) m_setStrengthSpecified = true; } { @@ -250,6 +250,7 @@ class LinkLValueVisitor final : public VNVisitor { iterateAndNextNull(nodep->rhsp()); iterateAndNextNull(nodep->thsp()); } + // cppcheck-suppress constParameterPointer void prepost_visit(AstNodeTriop* nodep) { VL_RESTORER(m_setRefLvalue); m_setRefLvalue = VAccess::NOCHANGE; diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index e5b834eb9..6dd093cf3 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -95,6 +95,7 @@ void V3LinkLevel::timescaling(const ModVec& mods) { for (AstNodeModule* modp : mods) { for (AstNode *nextp, *childp = modp->stmtsp(); childp; childp = nextp) { nextp = childp->nextp(); + // cppcheck-suppress constVariablePointer if (AstPragma* pragp = VN_CAST(childp, Pragma)) { if (pragp->pragType() == VPragmaType::TIMEUNIT_SET) { modp->timeunit(pragp->timescale()); @@ -205,6 +206,7 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { NameSet ioNames; NameSet dupNames; // For all modules, skipping over new top + // cppcheck-suppress constVariablePointer for (AstNodeModule* oldmodp = VN_AS(rootp->modulesp()->nextp(), NodeModule); oldmodp && oldmodp->level() <= 2; oldmodp = VN_AS(oldmodp->nextp(), NodeModule)) { for (AstNode* subnodep = oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 0ad9e5e1d..7d7552171 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -141,10 +141,11 @@ class LinkResolveVisitor final : public VNVisitor { nodep->varp()->usedParam(true); // Look for where genvar is valid bool ok = false; - for (AstGenFor* forp : m_underGenFors) { + // cppcheck-suppress constVariablePointer + for (AstGenFor* const forp : m_underGenFors) { if (ok) break; if (forp->initsp()) - forp->initsp()->foreach([&](AstVarRef* refp) { // + forp->initsp()->foreach([&](const AstVarRef* refp) { // if (refp->varp() == nodep->varp()) ok = true; }); } @@ -199,6 +200,7 @@ class LinkResolveVisitor final : public VNVisitor { } // UINFOTREE(1, letp, "", "let-let"); // UINFOTREE(1, nodep, "", "let-ref"); + // cppcheck-suppress constVariablePointer AstStmtExpr* const letStmtp = VN_AS(letp->stmtsp(), StmtExpr); AstNodeExpr* const newp = letStmtp->exprp()->cloneTree(false); const V3TaskConnects tconnects = V3Task::taskConnects(nodep, letp->stmtsp()); @@ -398,7 +400,7 @@ class LinkResolveVisitor final : public VNVisitor { return newFormat; } - static void expectDescriptor(AstNode* /*nodep*/, AstNodeVarRef* filep) { + static void expectDescriptor(AstNode* /*nodep*/, const AstNodeVarRef* filep) { // This might fail on complex expressions like arrays // We use attrFileDescr() only for lint suppression, so that's ok if (filep && filep->varp()) filep->varp()->attrFileDescr(true); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index ea9be8a29..16e4e097b 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -556,7 +556,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const VL_MT_STABLE { if (prefixed) { if (sized()) { out << width() << "'"; - } else if (autoExtend() && !sized() && width() == 1) { + } else if (autoExtend() && width() == 1) { out << "'"; if (bitIs0(0)) { out << '0'; @@ -636,7 +636,7 @@ string V3Number::displayPad(size_t fmtsize, char pad, bool left, const string& i return left ? (in + padding) : (padding + in); } -string V3Number::displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE { +string V3Number::displayed(const AstNode* nodep, const string& vformat) const VL_MT_STABLE { return displayed(nodep->fileline(), vformat); } @@ -2040,7 +2040,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); setZero(); // Find MSB and check for zero. - const int words = lhs.words(); + const int lWords = lhs.words(); const int umsbp1 = lhs.mostSetBitP1(); // dividend const int vmsbp1 = rhs.mostSetBitP1(); // divisor if (VL_UNLIKELY(vmsbp1 == 0) // rwp==0 so division by zero. Return 0. @@ -2077,8 +2077,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 < lWords; ++i) m_data.num()[i].m_value = 0; + for (int i = 0; i < lWords + 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 @@ -2155,7 +2155,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool 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 < lWords; ++i) m_data.num()[i].m_value = 0; opCleanThis(); UINFO(9, " opmoddiv-mod " << lhs << " " << rhs << " now=" << *this); return *this; diff --git a/src/V3Number.h b/src/V3Number.h index c43678bea..5d882e8ae 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -590,7 +590,7 @@ public: // ACCESSORS string ascii(bool prefixed = true, bool cleanVerilog = false) const VL_MT_STABLE; - string displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE; + string displayed(const AstNode* nodep, const string& vformat) const VL_MT_STABLE; string displayed(FileLine* fl, 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(); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 2aac40cc3..96d57f204 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -419,6 +419,7 @@ string V3Options::allArgsString() const VL_MT_SAFE { // Delete some options for Verilation of the hierarchical blocks. string V3Options::allArgsStringForHierBlock(bool forTop) const { + // cppcheck-suppress shadowFunction std::set vFiles; for (const auto& vFile : m_vFiles) vFiles.insert(vFile.filename()); string out; @@ -813,6 +814,7 @@ string V3Options::getSupported(const string& var) { // If update below, also update V3Options::showVersion() if (var == "COROUTINES" && coroutineSupport()) { return "1"; + // cppcheck-suppress knownConditionTrueFalse } else if (var == "SYSTEMC" && systemCFound()) { return "1"; } else { @@ -1762,6 +1764,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } }); for (int i = V3ErrorCode::EC_FIRST_WARN; i < V3ErrorCode::_ENUM_MAX; ++i) { + // cppcheck-suppress shadowFunction for (const string prefix : {"-Wno-", "-Wwarn-"}) parser.addSuggestionCandidate(prefix + V3ErrorCode{i}.ascii()); } diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index c640df090..f6ee2f506 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -357,6 +357,7 @@ private: public: // CONSTRUCTORS + // cppcheck-suppress constParameterCallback LogicMTask(V3Graph* graphp, OrderMoveVertex* mVtxp) : V3GraphVertex{graphp} , m_id{s_nextId++} { @@ -676,6 +677,7 @@ void SiblingMC::unlinkA() { void SiblingMC::unlinkB() { m_bp->bSiblingMCs().unlink(this); } +// cppcheck-suppress duplInheritedMember bool SiblingMC::mergeWouldCreateCycle() const { return (LogicMTask::pathExistsFrom(m_ap, m_bp, nullptr) || LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr)); @@ -696,6 +698,7 @@ LogicMTask* MTaskEdge::furtherMTaskp() const { LogicMTask* MTaskEdge::fromMTaskp() const { return static_cast(fromp()); } LogicMTask* MTaskEdge::toMTaskp() const { return static_cast(top()); } +// cppcheck-suppress duplInheritedMember bool MTaskEdge::mergeWouldCreateCycle() const { return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this); } @@ -1204,6 +1207,7 @@ public: mergeCanp->rescore(); const uint64_t actualScore = mergeCanp->score(); + // cppcheck-suppress knownConditionTrueFalse // they are in fact different if (actualScore > cachedScore) { // Cached score is out-of-date. // Mark this elem as in need of a rescore and continue. @@ -1322,7 +1326,7 @@ public: private: template - NewCp newCp(LogicMTask* mtaskp, LogicMTask* otherp, MTaskEdge* mergeEdgep) { + NewCp newCp(const LogicMTask* mtaskp, const LogicMTask* otherp, const MTaskEdge* mergeEdgep) { constexpr GraphWay way{N_Way}; // Return new wayward-CP for mtaskp reflecting its upcoming merge // with otherp. Set 'result.propagate' if mtaskp's wayward diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 34412bad3..55ae606e1 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -374,10 +374,10 @@ class ParamProcessor final { // equivalence predicate function. if (AstRefDType* const refp = VN_CAST(nodep, RefDType)) nodep = refp->skipRefToNonRefp(); const string paramStr = paramValueString(nodep); - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable V3Hash hash = V3Hasher::uncachedHash(nodep) + paramStr; // Force hash collisions -- for testing only - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash{paramStr}; int num; const auto pair = m_valueMap.emplace(hash, 0); diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 08b0fd4b4..e04993af2 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -282,7 +282,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name, return nodep; } -string V3ParseGrammar::unquoteString(FileLine* fileline, string text) { +string V3ParseGrammar::unquoteString(FileLine* fileline, const std::string& text) { string errMsg; string res = VString::unquoteSVString(text, errMsg); if (!errMsg.empty()) fileline->v3error(errMsg.c_str()); diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 89e1f8245..71a9ba6c4 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -259,7 +259,7 @@ size_t V3ParseImp::ppInputToLex(char* buf, size_t max_size) { size_t len = front.length(); if (len > (max_size - got)) { // Front string too big const string remainder = front.substr(max_size - got); - front = front.substr(0, max_size - got); + front.resize(max_size - got); m_ppBuffers.push_front(remainder); // Put back remainder for next time len = (max_size - got); } @@ -383,7 +383,7 @@ void V3ParseImp::dumpInputsFile() { void V3ParseImp::dumpTokensAhead(int line) { // LCOV_EXCL_START int i = 0; UINFO(1, "dumpTokensAhead @ " << line << " [ ] " << yylval); - for (auto t : m_tokensAhead) { + for (const V3ParseBisonYYSType& t : m_tokensAhead) { UINFO(1, "dumpTokensAhead @ " << line << " [" << i << "] " << t); ++i; } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 617344d12..598322aab 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -107,9 +107,9 @@ struct VMemberQualifiers final { // We can't use bison's %union as we want to pass the fileline with all tokens struct V3ParseBisonYYSType final { - FileLine* fl; - int token; // Read token, aka tok - VBaseOverride baseOverride; + FileLine* fl = nullptr; + int token = 0; // Read token, aka tok + VBaseOverride baseOverride{}; bool flag = false; // Passed up some rules union { V3Number* nump; @@ -157,7 +157,7 @@ class V3ParseImp final { int m_lexPrevToken = 0; // previous parsed token (for lexer) bool m_afterColonColon = false; // The previous token was '::' - V3ParseBisonYYSType m_tokenLastBison; // Token we last sent to Bison + V3ParseBisonYYSType m_tokenLastBison{}; // Token we last sent to Bison std::deque m_tokensAhead; // Tokens we parsed ahead of parser std::deque m_stringps; // Created strings for later cleanup diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 21daa46c1..8e62cdd5b 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -57,7 +57,7 @@ void yyourtext(const char* textp, size_t size) { static void linenoInc() { LEXP->linenoInc(); } static bool pedantic() { return LEXP->m_pedantic; } -static void yyerror(char* msg) { LEXP->curFilelinep()->v3error(msg); } +static void yyerror(const char* msg) { LEXP->curFilelinep()->v3error(msg); } static void yyerrorf(const char* msg) { LEXP->curFilelinep()->v3error(msg); } static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l); } @@ -548,7 +548,7 @@ again: if (len > (max_size - got)) { // Front string too big len = (max_size - got); string remainder = front.substr(len); - front = front.substr(0, len); + front.resize(len); streamp->m_buffers.push_front(remainder); // Put back remainder for next time } strncpy(buf + got, front.c_str(), len); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index a29d7c750..7e90a459b 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -124,7 +124,6 @@ public: // STATE const V3PreProc* m_preprocp = nullptr; ///< Object we're holding data for V3PreLex* m_lexp = nullptr; ///< Current lexer state (nullptr = closed) - std::stack m_includeStack; ///< Stack of includers above current m_lexp int m_lastLineno = 0; // Last line number (stall detection) int m_tokensOnLine = 0; // Number of tokens on line (stall detection) @@ -275,6 +274,7 @@ public: // CONSTRUCTORS V3PreProcImp() { m_states.push(ps_TOP); } + // cppcheck-suppress duplInheritedMember void configure(FileLine* filelinep) { // configure() separate from constructor to avoid calling abstract functions m_preprocp = this; // Silly, but to make code more similar to Verilog-Perl @@ -721,9 +721,10 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) { backslashesc = false; } if (!quote && !triquote) { - if (std::isalpha(*cp) || *cp == '_' + if (std::isalpha(*cp) // + || *cp == '_' // || *cp == '$' // Won't replace system functions, since no $ in argValueByName - || (argName != "" && (std::isdigit(*cp) || *cp == '$'))) { + || (argName != "" && std::isdigit(*cp))) { argName += *cp; continue; } @@ -737,7 +738,7 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) { // Normally `` is removed later, but with no token after, we're // otherwise stuck, so remove proceeding `` if (out.size() >= 2 && out.substr(out.size() - 2) == "``") { - out = out.substr(0, out.size() - 2); + out.resize(out.size() - 2); } } else { out += subst; diff --git a/src/V3PreShell.cpp b/src/V3PreShell.cpp index e8c41a8e9..63a48b1d6 100644 --- a/src/V3PreShell.cpp +++ b/src/V3PreShell.cpp @@ -100,7 +100,7 @@ protected: // from the V3LangCode to the various Lex BEGIN states. The language // of this source file is updated here, in case there have been any // intervening +ext+ options since it was first encountered. - FileLine* const modfileline = new FileLine{modfilename}; + const FileLine* const modfileline = new FileLine{modfilename}; modfileline->language(v3Global.opt.fileLanguage(modfilename)); V3Parse::ppPushText( parsep, ("`begin_keywords \""s + modfileline->language().ascii() + "\"\n")); diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index f8ac42c0f..1318c47d2 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -146,7 +146,7 @@ class PremitVisitor final : public VNVisitor { checkNode(nodep); } - static bool rhsReadsLhs(AstNodeAssign* nodep) { + static bool rhsReadsLhs(const AstNodeAssign* nodep) { const VNUser3InUse user3InUse; nodep->lhsp()->foreach([](const AstVarRef* refp) { if (refp->access().isWriteOrRW()) refp->varp()->user3(true); diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index ebec94043..b996b6c6d 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -89,7 +89,7 @@ class ProtectVisitor final : public VNVisitor { iterateChildren(nodep); - // cppcheck-has-bug-suppress unreadVariable + // cppcheck-suppress unreadVariable const V3Hash hash = V3Hasher::uncachedHash(m_cfilep); m_hashValuep->addText(fl, cvtToStr(hash.value()) + ";\n"); m_cHashValuep->addText(fl, cvtToStr(hash.value()) + "U;\n"); @@ -481,7 +481,7 @@ class ProtectVisitor final : public VNVisitor { void handleInput(AstVar* varp) { m_modPortsp->addNodesp(varp->cloneTree(false)); } - static void addLocalVariable(AstTextBlock* textp, AstVar* varp, const char* suffix) { + static void addLocalVariable(AstTextBlock* textp, const AstVar* varp, const char* suffix) { AstVar* const newVarp = new AstVar{varp->fileline(), VVarType::VAR, varp->name() + suffix, varp->dtypep()}; textp->addNodesp(newVarp); @@ -520,8 +520,8 @@ class ProtectVisitor final : public VNVisitor { } } - static bool checkIfClockExists(AstNodeModule* modp) { - for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + static bool checkIfClockExists(const AstNodeModule* modp) { + for (const AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (const AstVar* const varp = VN_CAST(stmtp, Var)) { if (varp->direction() == VDirection::INPUT && (varp->isUsedClock() diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 8e4c4f528..9b58281f0 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -89,7 +89,7 @@ std::vector getSenTreesUsedBy(const std::vector senTreeMap) { + const std::unordered_map& senTreeMap) { for (const auto& pair : lbs) { AstActive* const activep = pair.second; AstSenTree* const senTreep = activep->sentreep(); @@ -111,9 +111,10 @@ AstSenTree* findTriggeredIface(const AstVarScope* vscp, const auto ifaceIt = vifTrigged.find(vscp->varp()->sensIfacep()); if (ifaceIt != vifTrigged.end()) return ifaceIt->second; for (const auto& memberIt : vifMemberTriggered) { - if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) { return memberIt.second; } + if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) return memberIt.second; } vscp->v3fatalSrc("Did not find virtual interface trigger"); + return nullptr; // unreachable, appease MSVC } //============================================================================ diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 962048244..8fa275e2b 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -342,7 +342,7 @@ void transformForks(AstNetlist* const netlistp) { m_funcp = nodep; m_awaitMoved = false; iterateChildren(nodep); - // cppcheck-has-bug-suppress knownConditionTrueFalse + // cppcheck-suppress knownConditionTrueFalse if (nodep->isCoroutine() && m_awaitMoved && !nodep->stmtsp()->exists([](AstCAwait*) { return true; })) { // co_return at the end (either that or a co_await is required in a coroutine diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index 996dc9357..67e7d8957 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -57,7 +57,7 @@ private: AstAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment AstIface* m_trigAssignIfacep = nullptr; // Interface type whose trigger is assigned // by m_trigAssignp - AstVar* m_trigAssignMemberVarp; // Member pointer whose trigger is assigned + AstVar* m_trigAssignMemberVarp = nullptr; // Member pointer whose trigger is assigned V3UniqueNames m_vifTriggerNames{"__VvifTrigger"}; // Unique names for virt iface // triggers VirtIfaceTriggers m_triggers; // Interfaces and corresponding trigger vars diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index ea29b1fa2..560e1b40e 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -269,9 +269,9 @@ class ScopeVisitor final : public VNVisitor { // Make new scope variable if (!nodep->user1p()) { AstScope* scopep = m_scopep; - if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) { + if (const AstIfaceRefDType* const ifrefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) { // Attach every non-virtual interface variable its inner scope - if (ifacerefp->cellp()) scopep = VN_AS(ifacerefp->cellp()->user2p(), Scope); + if (ifrefp->cellp()) scopep = VN_AS(ifrefp->cellp()->user2p(), Scope); } AstVarScope* const varscp = new AstVarScope{nodep->fileline(), scopep, nodep}; UINFO(6, " New scope " << varscp); diff --git a/src/V3Scoreboard.cpp b/src/V3Scoreboard.cpp index c0f72da01..9864ae410 100644 --- a/src/V3Scoreboard.cpp +++ b/src/V3Scoreboard.cpp @@ -42,7 +42,7 @@ public: static uint32_t s_serial = 0; m_key.m_id = ++s_serial; } - ScoreboardTestElem() = default; + ScoreboardTestElem() = delete; uint64_t id() const { return m_key.m_id; } void rescore() { m_key.m_score = m_newScore; } diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 1b0ca79fc..4c62430d5 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -437,6 +437,7 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl { iterate(nodep); } } + // cppcheck-suppress duplInheritedMember void pushDeletep(AstNode* nodep) { // overriding VNVisitor::pusDeletep() UASSERT_OBJ(m_modp, nodep, "Must not nullptr"); m_forPackedSplit.m_refs[m_modp].remove(nodep); @@ -889,7 +890,7 @@ public: UASSERT(m_dedupDone, "cannot read before dedup()"); return m_rhs; } - explicit PackedVarRef(AstVar* varp) + explicit PackedVarRef(const AstVar* varp) : m_basicp{varp->dtypep()->basicp()} {} void append(const PackedVarRefEntry& e, const VAccess& access) { UASSERT(!m_dedupDone, "cannot add after dedup()"); diff --git a/src/V3StackCount.cpp b/src/V3StackCount.cpp index f8bf2d952..8dc28c114 100644 --- a/src/V3StackCount.cpp +++ b/src/V3StackCount.cpp @@ -79,7 +79,7 @@ private: m_stackSize = 0; return savedCount; } - void endVisitBase(uint32_t savedCount, AstNode* nodep) { + void endVisitBase(uint32_t savedCount, const AstNode* nodep) { UINFO(8, "cost " << std::setw(6) << std::left << m_stackSize << " " << nodep); if (!m_ignoreRemaining) m_stackSize += savedCount; } diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 3efe8e947..32350cae6 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -164,7 +164,7 @@ public: static double getStatSum(const string& name) { // O(n^2) if called a lot; present assumption is only a small call count - for (auto& itr : s_allStats) { + for (const V3Statistic& itr : s_allStats) { const V3Statistic* const repp = &itr; if (repp->name() == name) return repp->value(); } diff --git a/src/V3String.cpp b/src/V3String.cpp index 4ce8482e7..6658ba003 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -510,7 +510,7 @@ void VHashSha256::insertFile(const string& filename) { if (fd < 0) return; std::array buf; - while (const size_t got = ::read(fd, &buf, BUFFER_SIZE)) { + while (const ssize_t got = ::read(fd, &buf, BUFFER_SIZE)) { if (got <= 0) break; insert(&buf, got); } @@ -761,7 +761,7 @@ VSpellCheck::EditDistance VSpellCheck::cutoffDistance(size_t goal_len, size_t ca } string VSpellCheck::bestCandidateInfo(const string& goal, EditDistance& distancer) const { - string bestCandidate; + string best; const size_t gLen = goal.length(); distancer = LENGTH_LIMIT * 10; for (const string& candidate : m_candidates) { @@ -779,13 +779,13 @@ string VSpellCheck::bestCandidateInfo(const string& goal, EditDistance& distance << " candidate=" << candidate); if (dist < distancer && dist <= cutoff) { distancer = dist; - bestCandidate = candidate; + best = candidate; } } // If goal matches candidate avoid suggesting replacing with self if (distancer == 0) return ""; - return bestCandidate; + return best; } void VSpellCheck::selfTestDistanceOne(const string& a, const string& b, EditDistance expected) { diff --git a/src/V3SymTable.h b/src/V3SymTable.h index c2bf5028f..2785d071f 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -261,11 +261,11 @@ public: } } } - string cellErrorScopes(AstNode* lookp, string prettyName = "") { + string cellErrorScopes(const AstNode* lookp, string prettyName = "") { if (prettyName == "") prettyName = lookp->prettyName(); string scopes; for (IdNameMap::iterator it = m_idNameMap.begin(); it != m_idNameMap.end(); ++it) { - AstNode* const itemp = it->second->nodep(); + const AstNode* const itemp = it->second->nodep(); if (VN_IS(itemp, Cell) || (VN_IS(itemp, Module) && VN_AS(itemp, Module)->isTop())) { if (scopes != "") scopes += ", "; scopes += AstNode::prettyName(it->first); diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index 5d768d54c..bf53ca2ee 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -112,6 +112,7 @@ public: // the edge user field. We also want easy access to the 'id' // which uniquely identifies a single bidir edge. Luckily we // can do both efficiently. + // cppcheck-suppress badBitmaskCheck const uint64_t userValue = (static_cast(cost) << 32) | edgeId; (new V3GraphEdge{this, fp, tp, cost})->user(userValue); (new V3GraphEdge{this, tp, fp, cost})->user(userValue); @@ -121,6 +122,7 @@ public: return static_cast(edgep->user()); } + // cppcheck-suppress duplInheritedMember bool empty() const { return m_vertices.empty(); } const std::list keysToVertexList(const std::vector& odds) { diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 3f9fabd4b..e2d1b8cd8 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -213,12 +213,14 @@ private: if (chkvis.isCoverage()) { chkvis.clearOptimizable(nodep, "Table removes coverage points"); } + // cppcheck-suppress knownConditionTrueFalse // set by TableSimulateVisitor if (!m_outWidthBytes || !m_inWidthBits) { chkvis.clearOptimizable(nodep, "Table has no outputs"); } if (chkvis.instrCount() < TABLE_MIN_NODE_COUNT) { chkvis.clearOptimizable(nodep, "Table has too few nodes involved"); } + // cppcheck-suppress knownConditionTrueFalse if (space > TABLE_MAX_BYTES) { chkvis.clearOptimizable(nodep, "Table takes too much space"); } diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index bbc2d4e47..d244f1d19 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -316,6 +316,7 @@ class TraceDeclVisitor final : public VNVisitor { void removeRedundantPrefixPushPop() { for (const auto& pair : m_scopeInitFuncps) { + // cppcheck-suppress constVariablePointer for (AstCFunc* const funcp : pair.second) { AstNode* prevp = nullptr; AstNode* currp = funcp->stmtsp(); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 36ebdd123..256dfe5ab 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -862,11 +862,6 @@ class TristateVisitor final : public TristateBaseVisitor { if (varRefp->varp()->isNet()) { m_assigns[varRefp->varp()].push_back(nodep); } else if (nodep->strengthSpecp()) { - if (!varRefp->varp()->isNet()) - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Signal strengths are unsupported " - "on the following variable type: " - << varRefp->varp()->varType()); - nodep->strengthSpecp()->unlinkFrBack()->deleteTree(); } } else if (nodep->strengthSpecp()) { diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index d1abee768..1aaff7580 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -435,8 +435,7 @@ class UndrivenVisitor final : public VNVisitorConst { && entryp->getNodep()) { if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() - || (entryp->isDrivenAlwaysCombWhole() - && m_alwaysCombp != entryp->getAlwCombp() + || (m_alwaysCombp != entryp->getAlwCombp() && m_alwaysCombp->fileline() != entryp->getAlwCombFileLinep()))) { nodep->v3warn( MULTIDRIVEN, diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 657905af8..3a83bd043 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -469,7 +469,7 @@ class UnknownVisitor final : public VNVisitor { nodep->bitp()->cloneTreePure(false)}; // Note below has null backp(); the Edit function knows how to deal with that. condp = V3Const::constifyEdit(condp); - AstNodeDType* nodeDtp = nodep->dtypep()->skipRefp(); + const AstNodeDType* const nodeDtp = nodep->dtypep()->skipRefp(); if (condp->isOne()) { // We don't need to add a conditional; we know the existing expression is ok VL_DO_DANGLING(condp->deleteTree(), condp); diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index 0ab4188f1..b2f83c7d1 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -210,6 +210,7 @@ class VariableOrder final { sortAndAppend(m2v[emptyVec]); } + // cppcheck-suppress constParameterPointer void orderModuleVars(AstNodeModule* modp) { // Unlink all module variables from the module, compute attributes for (AstNode *nodep = modp->stmtsp(), *nextp; nodep; nodep = nextp) { diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp index 41ef8c44b..9b1a4727c 100644 --- a/src/V3Waiver.cpp +++ b/src/V3Waiver.cpp @@ -34,7 +34,7 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, cons string trimmsg = msg; if (!v3Global.opt.waiverMultiline()) { const size_t pos = trimmsg.find('\n'); - trimmsg = trimmsg.substr(0, pos); + trimmsg.resize(pos); if (pos != std::string::npos) trimmsg += '*'; } { // Remove line numbers and context "\n [0-9] | ", "\n ^[~]+" diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 0753c96d5..694dc36e0 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4138,7 +4138,6 @@ class WidthVisitor final : public VNVisitor { // All variables in the dot hierarchy must be randomizable if (randVarp && !randVarp->isRand()) randVarp->rand(VRandAttr::RAND_INLINE); } - if (!argp) continue; // Errored out, bail // randVarp is now the leftmost element from the dot hierarchy in argp->exprp() if (randVarp == fromVarp) { // The passed in variable is MemberSel'ected from the MethodCall target @@ -4523,9 +4522,9 @@ class WidthVisitor final : public VNVisitor { if (nodep->isScoped()) { // = ClassOrPackage::new UASSERT_OBJ(nodep->classOrPackagep(), nodep, "Unlinked classOrPackage"); warnp = nodep->classOrPackagep(); - if (AstClass* const classp = VN_CAST(warnp, Class)) { + if (AstClass* const clsp = VN_CAST(warnp, Class)) { AstClassRefDType* const adtypep - = new AstClassRefDType{nodep->fileline(), classp, nullptr}; + = new AstClassRefDType{nodep->fileline(), clsp, nullptr}; v3Global.rootp()->typeTablep()->addTypesp(adtypep); refp = adtypep; } @@ -5908,16 +5907,15 @@ class WidthVisitor final : public VNVisitor { if (nodep->modVarp() && nodep->modVarp()->isGParam()) { // Widthing handled as special init() case bool didWidth = false; - if (auto* const patternp = VN_CAST(nodep->exprp(), Pattern)) { - if (const AstVar* const modVarp = nodep->modVarp()) { - // Convert BracketArrayDType - userIterate(modVarp->childDTypep(), - WidthVP{SELF, BOTH}.p()); // May relink pointed to node - AstNodeDType* const setDtp = modVarp->childDTypep()->cloneTree(false); - if (!patternp->childDTypep()) patternp->childDTypep(setDtp); - userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p()); - didWidth = true; - } + if (AstPattern* const patternp = VN_CAST(nodep->exprp(), Pattern)) { + const AstVar* const modVarp = nodep->modVarp(); + // Convert BracketArrayDType + userIterate(modVarp->childDTypep(), + WidthVP{SELF, BOTH}.p()); // May relink pointed to node + AstNodeDType* const setDtp = modVarp->childDTypep()->cloneTree(false); + if (!patternp->childDTypep()) patternp->childDTypep(setDtp); + userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p()); + didWidth = true; } if (!didWidth) userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); } else if (!m_paramsOnly) { @@ -6151,14 +6149,14 @@ class WidthVisitor final : public VNVisitor { break; } else { AstVar* const portp = ports[i]; - AstAttrOf* const protop = protos[i]; + AstAttrOf* const rprotop = protos[i]; AstNodeDType* const declDtp = portp->dtypep(); - AstNodeDType* const protoDtp = protop->fromp()->dtypep(); - if (portp->name() != protop->name()) { + AstNodeDType* const protoDtp = rprotop->fromp()->dtypep(); + if (portp->name() != rprotop->name()) { protoDtp->v3warn(PROTOTYPEMIS, "In prototype for " << nodep->prettyNameQ() << ", argument " << (i + 1) - << " named " << protop->prettyNameQ() + << " named " << rprotop->prettyNameQ() << " mismatches out-of-block argument name " << portp->prettyNameQ() << " (IEEE 1800-2023 8.24)\n" << protoDtp->warnContextPrimary() << '\n' @@ -6466,9 +6464,8 @@ class WidthVisitor final : public VNVisitor { } else { argp->v3error("Non-variable arguments for 'std::randomize()'."); } - if (!argp) continue; } - if (nullp) { nullp->v3error("'std::randomize()' does not accept 'null' as arguments."); } + if (nullp) nullp->v3error("'std::randomize()' does not accept 'null' as arguments."); } void visit(AstNodeFTaskRef* nodep) override { // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign diff --git a/src/VlcTest.h b/src/VlcTest.h index d8c9c751c..49006ef8b 100644 --- a/src/VlcTest.h +++ b/src/VlcTest.h @@ -111,7 +111,7 @@ public: void dump(bool bucketsToo) { UINFO(2, "dumpTests..."); VlcTest::dumpHeader(); - for (const auto& testp : m_tests) testp->dump(bucketsToo); + for (const VlcTest* const testp : m_tests) testp->dump(bucketsToo); } VlcTest* newTest(const string& name, uint64_t testrun, double comp) { VlcTest* const testp = new VlcTest{name, testrun, comp}; diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt index c83e0c03e..063bbb243 100644 --- a/src/cppcheck-suppressions.txt +++ b/src/cppcheck-suppressions.txt @@ -15,6 +15,8 @@ unusedScopedObject useInitializationList useStlAlgorithm +// Do not warn that analysis failed due to complexity +checkLevelNormal // Seems useless missingIncludeSystem // Tedious @@ -22,6 +24,12 @@ unmatchedSuppression // Not our code *:include/gtkwave/* *:include/vltstd/* +// flex/bison output +*:verilog.c +*:V3Lexer_pregen.yy.cpp +*:V3PreLex_pregen.yy.cpp +// Just check the hand written code +missingInclude:examples/* // We intentionally redefine AstNode methods to improve type safety duplInheritedMember:src/V3AstNode*.h duplInheritedMember:src/obj_*/V3Ast__gen_impl.h @@ -32,3 +40,108 @@ unusedPrivateFunction:src/V3Const.cpp // These are all inappropriate constVariablePointer:src/V3DfgPeephole.cpp constParameterPointer:src/V3DfgPeephole.cpp + + +// TODO: these need fixing/signing off in source +constParameterPointer:src/V3EmitCFunc.cpp +constVariablePointer:src/V3EmitCFunc.cpp +constParameterPointer:src/V3EmitCFunc.h +constVariablePointer:src/V3EmitCFunc.h +constParameterPointer:src/V3EmitCImp.cpp +constParameterPointer:src/V3EmitCModel.cpp +constVariablePointer:src/V3EmitCModel.cpp +constVariablePointer:src/V3EmitCSyms.cpp +constVariablePointer:src/V3EmitV.cpp +constParameterReference:src/V3Error.cpp +constParameterPointer:src/V3ExecGraph.cpp +constVariablePointer:src/V3ExecGraph.cpp +constVariableReference:src/V3ExecGraph.cpp +constParameterPointer:src/V3Expand.cpp +constParameterPointer:src/V3FileLine.cpp +constParameterReference:src/V3FileLine.cpp +constParameterPointer:src/V3Force.cpp +constParameterPointer:src/V3Fork.cpp +constVariablePointer:src/V3Fork.cpp +constParameterPointer:src/V3FuncOpt.cpp +constVariablePointer:src/V3FuncOpt.cpp +constParameterPointer:src/V3Gate.cpp +constVariablePointer:src/V3Gate.cpp +constVariableReference:src/V3Gate.cpp +constParameterPointer:src/V3GraphAcyc.cpp +constVariableReference:src/V3GraphAcyc.cpp +constVariablePointer:src/V3GraphAlg.cpp +constVariableReference:src/V3GraphAlg.cpp +constParameterPointer:src/V3Graph.cpp +constVariableReference:src/V3Graph.cpp +constParameterPointer:src/V3LinkDot.cpp +constVariablePointer:src/V3LinkDot.cpp +constVariablePointer:src/V3LinkInc.cpp +constVariablePointer:src/V3LinkJump.cpp +constVariablePointer:src/V3LinkLevel.cpp +constParameterPointer:src/V3LinkParse.cpp +constVariablePointer:src/V3LinkParse.cpp +constVariablePointer:src/V3MergeCond.cpp +constParameterPointer:src/V3OrderCFuncEmitter.h +constVariablePointer:src/V3OrderMoveGraph.cpp +constParameterPointer:src/V3OrderParallel.cpp +constVariablePointer:src/V3OrderParallel.cpp +constVariableReference:src/V3OrderParallel.cpp +constParameterPointer:src/V3OrderProcessDomains.cpp +constVariablePointer:src/V3OrderProcessDomains.cpp +constVariablePointer:src/V3OrderSerial.cpp +constParameterPointer:src/V3Param.cpp +constVariablePointer:src/V3Param.cpp +constParameterPointer:src/V3ParseImp.cpp +constVariableReference:src/V3ParseImp.cpp +constVariableReference:src/V3PreProc.cpp +constParameterPointer:src/V3Randomize.cpp +constVariablePointer:src/V3Randomize.cpp +constVariablePointer:src/V3SchedAcyclic.cpp +constVariableReference:src/V3SchedAcyclic.cpp +constParameterPointer:src/V3Sched.cpp +constParameterReference:src/V3Sched.cpp +constVariablePointer:src/V3Sched.cpp +constParameterPointer:src/V3SchedPartition.cpp +constVariablePointer:src/V3SchedPartition.cpp +constVariableReference:src/V3SchedPartition.cpp +constParameterPointer:src/V3SchedReplicate.cpp +constParameterReference:src/V3SchedReplicate.cpp +constVariableReference:src/V3SchedReplicate.cpp +constVariablePointer:src/V3SchedTiming.cpp +constVariableReference:src/V3SchedTiming.cpp +constParameterPointer:src/V3SchedVirtIface.cpp +constVariablePointer:src/V3SchedVirtIface.cpp +constVariablePointer:src/V3SenExprBuilder.h +constVariablePointer:src/V3Slice.cpp +constParameterPointer:src/V3Split.cpp +constVariablePointer:src/V3Split.cpp +constParameterPointer:src/V3SplitVar.cpp +constParameterReference:src/V3SplitVar.cpp +constVariablePointer:src/V3SplitVar.cpp +constParameterPointer:src/V3Subst.cpp +constParameterPointer:src/V3Task.cpp +constParameterReference:src/V3Task.cpp +constVariablePointer:src/V3Task.cpp +constVariableReference:src/V3Task.cpp +constParameterPointer:src/V3Timing.cpp +constVariablePointer:src/V3Timing.cpp +constVariableReference:src/V3Timing.cpp +constVariablePointer:src/V3Trace.cpp +constParameterPointer:src/V3Tristate.cpp +constParameterReference:src/V3Tristate.cpp +constVariablePointer:src/V3Tristate.cpp +constVariableReference:src/V3Tristate.cpp +constVariablePointer:src/V3TSP.cpp +constVariableReference:src/V3TSP.cpp +constParameterPointer:src/V3Undriven.cpp +constVariablePointer:src/V3Undriven.cpp +constParameterPointer:src/V3Unroll.cpp +constVariablePointer:src/V3Unroll.cpp +constParameterPointer:src/V3WidthCommit.cpp +constVariablePointer:src/V3WidthCommit.cpp +constParameterPointer:src/V3Width.cpp +constParameterReference:src/V3Width.cpp +constVariablePointer:src/V3Width.cpp +constVariableReference:src/V3Width.cpp +constVariablePointer:src/V3WidthSel.cpp +constParameterPointer:verilog.y diff --git a/src/verilog.y b/src/verilog.y index 0befaa115..ccb9c1b20 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -250,7 +250,7 @@ public: return createArray(dtypep, rangearraysp, isPacked); } } - string unquoteString(FileLine* fileline, string text) VL_MT_DISABLED; + string unquoteString(FileLine* fileline, const std::string& text) VL_MT_DISABLED; void checkDpiVer(FileLine* fileline, const string& str) { if (str != "DPI-C" && !v3Global.opt.bboxSys()) { fileline->v3error("Unsupported DPI type '" << str << "': Use 'DPI-C'"); diff --git a/test_regress/t/t_dist_warn_coverage.py b/test_regress/t/t_dist_warn_coverage.py index 4f338cf12..5633d45f8 100755 --- a/test_regress/t/t_dist_warn_coverage.py +++ b/test_regress/t/t_dist_warn_coverage.py @@ -93,7 +93,6 @@ for s in [ 'Unsupported: Public functions with >64 bit outputs; ', 'Unsupported: Replication to form ', 'Unsupported: Shifting of by over 32-bit number isn\'t supported.', - 'Unsupported: Signal strengths are unsupported ', 'Unsupported: Size-changing cast on non-basic data type', 'Unsupported: Slice of non-constant bounds', 'Unsupported: Unclocked assertion', From 631714c50a99b5bb96387d3d74b82c99fdf59efd Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Thu, 21 Aug 2025 12:26:30 +0200 Subject: [PATCH 207/252] Fix expression type comparison (#6316) --- src/V3Width.cpp | 15 +++++---- test_regress/t/t_type_expression_compare.py | 18 +++++++++++ test_regress/t/t_type_expression_compare.v | 36 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100755 test_regress/t/t_type_expression_compare.py create mode 100644 test_regress/t/t_type_expression_compare.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 694dc36e0..7ce8d1477 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1832,8 +1832,11 @@ class WidthVisitor final : public VNVisitor { break; } case VAttrType::TYPEID: - // Soon to be handled in AstEqT - nodep->dtypeSetSigned32(); + if (AstNodeDType* dtypep = VN_CAST(nodep->fromp(), NodeDType)) { + nodep->dtypep(dtypep); + } else { + nodep->dtypep(VN_AS(nodep->fromp(), NodeExpr)->dtypep()); + } break; case VAttrType::VAR_BASE: // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf @@ -5199,7 +5202,7 @@ class WidthVisitor final : public VNVisitor { // Deal with case(type(data_type)) if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) { if (exprap->attrType() == VAttrType::TYPEID) { - AstNodeDType* const exprDtp = VN_AS(exprap->fromp(), NodeDType); + const AstNodeDType* const exprDtp = exprap->dtypep(); UINFO(9, "case type exprDtp " << exprDtp); // V3Param may have a pointer to this case statement, and we need // dotted references to remain properly named, so rather than @@ -5218,7 +5221,7 @@ class WidthVisitor final : public VNVisitor { condp->v3error( "Case(type) statement requires items that have type() items"); } else { - AstNodeDType* const condDtp = VN_AS(condAttrp->fromp(), NodeDType); + AstNodeDType* const condDtp = condAttrp->dtypep(); if (AstNode::computeCastable(exprDtp, condDtp, nodep) == VCastable::SAMEISH) { hit = true; @@ -7021,8 +7024,8 @@ class WidthVisitor final : public VNVisitor { "Type compare expects type reference"); UASSERT_OBJ(rhsap->attrType() == VAttrType::TYPEID, rhsap, "Type compare expects type reference"); - AstNodeDType* const lhsDtp = VN_AS(lhsap->fromp(), NodeDType); - AstNodeDType* const rhsDtp = VN_AS(rhsap->fromp(), NodeDType); + const AstNodeDType* const lhsDtp = lhsap->dtypep(); + const AstNodeDType* const rhsDtp = rhsap->dtypep(); UINFO(9, "==type lhsDtp " << lhsDtp); UINFO(9, "==type rhsDtp " << lhsDtp); const bool invert = VN_IS(nodep, NeqT); diff --git a/test_regress/t/t_type_expression_compare.py b/test_regress/t/t_type_expression_compare.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_type_expression_compare.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_type_expression_compare.v b/test_regress/t/t_type_expression_compare.v new file mode 100644 index 000000000..d94e9fc70 --- /dev/null +++ b/test_regress/t/t_type_expression_compare.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +typedef int Int_T; + +module t; + initial begin + Int_T value1 = 7; + int value2 = 13; + real r; + if (type(value1) != type(value2)) $stop; + if (type(value1 + value2) != type(value2 + 18)) $stop; + case (type(value1)) + type(value2): ; + type(r): $stop; + type(chandle): $stop; + type(logic): $stop; + default: $stop; + endcase + case (type(value1 + value2 + 13)) + type(type(value2 + 18 - 40)): ; + type(r): $stop; + type(chandle): $stop; + default: $stop; + endcase + if (type(value1) == type(value2) && type(value1 + value2) == type(value2 + 18)) begin + $write("*-* All Finished *-*\n"); + $finish; + end else begin + $stop; + end + end +endmodule From 997d5ecdf1144131b61dc040e1566363a2055986 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 21 Aug 2025 20:06:43 -0400 Subject: [PATCH 208/252] Internals: Cleanup cppcheck c casts. No functional change intended. --- Makefile.in | 2 +- include/verilated.cpp | 6 +++--- include/verilated_save.h | 2 +- include/verilated_types.h | 2 +- include/verilated_vpi.cpp | 12 ++++++------ src/V3Ast.cpp | 2 +- src/V3AstNodeExpr.h | 10 ++++++---- src/V3AstNodeStmt.h | 3 ++- src/V3String.cpp | 4 ++-- src/V3Tristate.cpp | 4 ++-- src/cppcheck-suppressions.txt | 1 - 11 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Makefile.in b/Makefile.in index fb315cb70..42ce2987d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -410,7 +410,7 @@ CPPCHECK_FLAGS = --enable=all CPPCHECK_FLAGS += --inline-suppr CPPCHECK_FLAGS += --suppressions-list=$(srcdir)/src/cppcheck-suppressions.txt CPPCHECK_FLAGS += --cppcheck-build-dir=$(CPPCHECK_CACHE) -CPPCHECK_FLAGS += -DVL_DEBUG=1 -DVL_CPPCHECK=1 -D__GNUC__=1 +CPPCHECK_FLAGS += -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DINFILTER_PIPE=1 -D__GNUC__=1 CPPCHECK_FLAGS += -j$(CPPCHECK_JOBS) CPPCHECK_INC = -I$(srcdir)/include CPPCHECK_INC += -I$(srcdir)/include/gtkwave diff --git a/include/verilated.cpp b/include/verilated.cpp index 73cc45884..a43b00c6b 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1755,7 +1755,7 @@ uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE { uint64_t h = seed ^ (len * m); - const uint64_t* data = (const uint64_t*)key; + const uint64_t* data = reinterpret_cast(key); const uint64_t* end = data + (len / 8); while (data != end) { @@ -1769,7 +1769,7 @@ uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE { h *= m; } - const unsigned char* data2 = (const unsigned char*)data; + const unsigned char* data2 = reinterpret_cast(data); switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; /* fallthrough */ @@ -3379,7 +3379,7 @@ VerilatedModule::VerilatedModule(const char* namep) VerilatedModule::~VerilatedModule() { // Memory cleanup - not called during normal operation - // NOLINTNEXTLINE(google-readability-casting) + // cppcheck-suppress cstyleCast // NOLINTNEXTLINE(google-readability-casting) if (m_namep) VL_DO_CLEAR(free((void*)(m_namep)), m_namep = nullptr); } diff --git a/include/verilated_save.h b/include/verilated_save.h index c28fda329..2347e2d3c 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -298,7 +298,7 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r uint32_t len = 0; os >> len; rhs.resize(len); - // NOLINTNEXTLINE(google-readability-casting) + // cppcheck-suppress cstyleCast // NOLINTNEXTLINE(google-readability-casting) return os.read((void*)(rhs.data()), len); } VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp); diff --git a/include/verilated_types.h b/include/verilated_types.h index 41dc67587..b4e51caa8 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -499,7 +499,7 @@ private: public: // CONSTRUCTORS - // cppcheck-suppress uninitMember // m_defaultValue isn't defaulted, caller's constructor must + // cppcheck-suppress uninitMemberVar // m_defaultValue isn't defaulted, caller must VlQueue() = default; ~VlQueue() = default; VlQueue(const VlQueue&) = default; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 5cd59ffc6..8ee39197b 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -3147,7 +3147,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - PLI_INT16* shortintsp = (PLI_INT16*)out_data; + PLI_INT16* shortintsp = reinterpret_cast(out_data); arrayvalue_p->value.shortints = shortintsp; if (varp->vltype() == VLVT_UINT8) { @@ -3166,7 +3166,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - PLI_INT32* integersp = (PLI_INT32*)out_data; + PLI_INT32* integersp = reinterpret_cast(out_data); arrayvalue_p->value.integers = integersp; if (varp->vltype() == VLVT_UINT8) { @@ -3188,7 +3188,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - PLI_INT64* longintsp = (PLI_INT64*)out_data; + PLI_INT64* longintsp = reinterpret_cast(out_data); arrayvalue_p->value.longints = longintsp; if (varp->vltype() == VLVT_UINT8) { @@ -3213,7 +3213,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - p_vpi_vecval vectorsp = (p_vpi_vecval)out_data; + p_vpi_vecval vectorsp = reinterpret_cast(out_data); arrayvalue_p->value.vectors = vectorsp; if (varp->vltype() == VLVT_UINT8) { @@ -3241,7 +3241,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + PLI_BYTE8* valuep = reinterpret_cast(out_data); arrayvalue_p->value.rawvals = valuep; if (varp->vltype() == VLVT_UINT8) { @@ -3269,7 +3269,7 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P "increase and recompile"); } - PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + PLI_BYTE8* valuep = reinterpret_cast(out_data); arrayvalue_p->value.rawvals = valuep; if (varp->vltype() == VLVT_UINT8) { diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 51051f330..c202c2500 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1258,7 +1258,7 @@ char* AstNode::dumpTreeJsonGdb(const char* str) { return strdup(str); } // allow for passing pointer literals like 0x42.. without manual cast char* AstNode::dumpTreeJsonGdb(intptr_t nodep) { if (!nodep) return strdup("{\"addr\":\"NULL\"}\n"); - return dumpTreeJsonGdb((const AstNode*)nodep); + return dumpTreeJsonGdb(reinterpret_cast(nodep)); } // cppcheck-suppress unusedFunction // Debug only void AstNode::dumpGdb(const AstNode* nodep) { // For GDB only // LCOV_EXCL_LINE diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index e37848d22..3c1bf40d0 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -503,7 +503,7 @@ public: AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable void varp(AstVar* varp) { m_varp = varp; - dtypeFrom((AstNode*)varp); + dtypeFrom(reinterpret_cast(varp)); } AstVarScope* varScopep() const { return m_varScopep; } void varScopep(AstVarScope* varscp) { m_varScopep = varscp; } @@ -767,7 +767,9 @@ public: AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; } void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; } AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); } - void classOrPackagep(AstNodeModule* nodep) { m_classOrPackageNodep = (AstNode*)nodep; } + void classOrPackagep(AstNodeModule* nodep) { + m_classOrPackageNodep = reinterpret_cast(nodep); + } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -4474,7 +4476,7 @@ class AstFuncRef final : public AstNodeFTaskRef { public: inline AstFuncRef(FileLine* fl, AstFunc* taskp, AstNodeExpr* pinsp); AstFuncRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp) - : ASTGEN_SUPER_FuncRef(fl, (AstNode*)namep, pinsp) {} + : ASTGEN_SUPER_FuncRef(fl, reinterpret_cast(namep), pinsp) {} AstFuncRef(FileLine* fl, const string& name, AstNodeExpr* pinsp) : ASTGEN_SUPER_FuncRef(fl, name, pinsp) {} ASTGEN_MEMBERS_AstFuncRef; @@ -4525,7 +4527,7 @@ class AstTaskRef final : public AstNodeFTaskRef { public: inline AstTaskRef(FileLine* fl, AstTask* taskp, AstNodeExpr* pinsp); AstTaskRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp) - : ASTGEN_SUPER_TaskRef(fl, (AstNode*)namep, pinsp) { + : ASTGEN_SUPER_TaskRef(fl, reinterpret_cast(namep), pinsp) { dtypeSetVoid(); } AstTaskRef(FileLine* fl, const string& name, AstNodeExpr* pinsp) diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 447441684..ca7753c63 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -1092,7 +1092,8 @@ class AstAssignAlias final : public AstNodeAssign { // If both sides are wires, there's no LHS vs RHS, public: AstAssignAlias(FileLine* fl, AstVarRef* lhsp, AstVarRef* rhsp) - : ASTGEN_SUPER_AssignAlias(fl, (AstNodeExpr*)lhsp, (AstNodeExpr*)rhsp) {} + : ASTGEN_SUPER_AssignAlias(fl, reinterpret_cast(lhsp), + reinterpret_cast(rhsp)) {} ASTGEN_MEMBERS_AstAssignAlias; AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { V3ERROR_NA_RETURN(nullptr); diff --git a/src/V3String.cpp b/src/V3String.cpp index 6658ba003..3d34bd2d4 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -364,7 +364,7 @@ uint64_t VString::hashMurmur(const string& str) VL_PURE { uint64_t h = seed ^ (len * m); - const uint64_t* data = (const uint64_t*)key; + const uint64_t* data = reinterpret_cast(key); const uint64_t* end = data + (len / 8); while (data != end) { @@ -378,7 +378,7 @@ uint64_t VString::hashMurmur(const string& str) VL_PURE { h *= m; } - const unsigned char* data2 = (const unsigned char*)data; + const unsigned char* data2 = reinterpret_cast(data); switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; /* fallthrough */ diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 256dfe5ab..2845049f0 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -308,11 +308,11 @@ public: if (AstNode* const refp = nodep->op4p()) deleteVerticesFromSubtreeRecurse(refp); } void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); } - bool isTristate(AstNode* nodep) { + bool isTristate(const AstNode* nodep) { const TristateVertex* const vertexp = reinterpret_cast(nodep->user4p()); return vertexp && vertexp->isTristate(); } - bool feedsTri(AstNode* nodep) { + bool feedsTri(const AstNode* nodep) { const TristateVertex* const vertexp = reinterpret_cast(nodep->user4p()); return vertexp && vertexp->feedsTri(); } diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt index 063bbb243..3d416bba9 100644 --- a/src/cppcheck-suppressions.txt +++ b/src/cppcheck-suppressions.txt @@ -4,7 +4,6 @@ // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -cstyleCast ctunullpointer derefInvalidIteratorRedundantCheck nullPointer From b96f35b8fe54d67d592cf87559e0565add25738d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 21 Aug 2025 21:09:10 -0400 Subject: [PATCH 209/252] Internals: Add AstToStringN (#4732 prep) --- src/V3AstNodeExpr.h | 14 ++++++++++++++ src/V3Simulate.h | 7 +++++++ src/V3Width.cpp | 13 ++++++------- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 3c1bf40d0..be535e86d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -5534,6 +5534,20 @@ public: bool cleanLhs() const override { return true; } bool sizeMattersLhs() const override { return false; } }; +class AstToStringN final : public AstNodeUniop { +public: + AstToStringN(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_ToStringN(fl, lhsp) { + dtypeSetString(); + } + ASTGEN_MEMBERS_AstToStringN; + void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; } + string emitVerilog() override { return "$sformatf(\"%p\", %l)"; } + string emitC() override { return isWide() ? "VL_TO_STRING_W(%nw, %li)" : "VL_TO_STRING(%li)"; } + bool cleanOut() const override { return true; } + bool cleanLhs() const override { return true; } + bool sizeMattersLhs() const override { return false; } +}; class AstToUpperN final : public AstNodeUniop { // string.toupper() public: diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 65b58cf7b..30e3468d9 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -1260,6 +1260,13 @@ private: } } } + void visit(AstToStringN* nodep) override { + if (jumpingOver()) return; + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + iterateChildrenConst(nodep); + clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE + } void visit(AstCoverInc* nodep) override { m_isCoverage = true; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 7ce8d1477..22ce31ba7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5525,6 +5525,11 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->fmtp(), WidthVP{SELF, BOTH}.p()); userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); } + void visit(AstToStringN* nodep) override { + // Inserted by V3Width only so we know has been resolved + nodep->dtypeSetString(); + userIterateAndNext(nodep->lhsp(), WidthVP{nodep->lhsp()->dtypep(), BOTH}.p()); + } void visit(AstSFormatF* nodep) override { // Excludes NodeDisplay, see below if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function @@ -5611,13 +5616,7 @@ class WidthVisitor final : public VNVisitor { VNRelinker handle; argp->unlinkFrBack(&handle); FileLine* const flp = nodep->fileline(); - AstCExpr* const newp = new AstCExpr{flp, nullptr}; - newp->addExprsp(new AstText{flp, "VL_TO_STRING(", true}); - newp->addExprsp(argp); - newp->addExprsp(new AstText{flp, ")", true}); - newp->dtypeSetString(); - newp->pure(true); - newp->protect(false); + AstNodeExpr* const newp = new AstToStringN{flp, argp}; handle.relink(newp); // Set argp to what we replaced it with, as we will keep processing the // next argument. From e202cb31d86ee121b93fb84cac350001c5eed894 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 21 Aug 2025 21:33:05 -0400 Subject: [PATCH 210/252] Change `$display("%p")` to remove space after `}`. --- Changes | 1 + include/verilated_types.h | 6 +-- test_regress/t/t_array_method.v | 38 ++++++++--------- test_regress/t/t_assoc.v | 4 +- test_regress/t/t_assoc_method.v | 28 ++++++------ test_regress/t/t_assoc_wildcard_method.v | 14 +++--- test_regress/t/t_class_format.out | 4 +- test_regress/t/t_dynarray.v | 2 +- test_regress/t/t_dynarray_concat.v | 20 ++++----- test_regress/t/t_dynarray_method.v | 44 +++++++++---------- test_regress/t/t_interface_virtual.out | 2 +- test_regress/t/t_queue.v | 2 +- test_regress/t/t_queue_method.v | 52 +++++++++++------------ test_regress/t/t_queue_slice.v | 26 ++++++------ test_regress/t/t_stream_bitqueue.v | 44 +++++++++---------- test_regress/t/t_stream_crc_example.v | 6 +-- test_regress/t/t_stream_dynamic.v | 12 +++--- test_regress/t/t_stream_unpack.v | 30 ++++++------- test_regress/t/t_struct_nest_uarray.v | 4 +- test_regress/t/t_typename_min.v | 6 +-- test_regress/t/t_unpacked_array_p_fmt.out | 4 +- 21 files changed, 175 insertions(+), 174 deletions(-) diff --git a/Changes b/Changes index 58c07fc5d..01781f87b 100644 --- a/Changes +++ b/Changes @@ -41,6 +41,7 @@ Verilator 5.039 devel * Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. +* Change `$display("%p")` to remove space after `}`. * Improve `--skip-identical` to skip on identical input file contents (#6109). * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] diff --git a/include/verilated_types.h b/include/verilated_types.h index b4e51caa8..0fa30d2ca 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -917,7 +917,7 @@ public: out += comma + VL_TO_STRING(i); comma = ", "; } - return out + "} "; + return out + "}"; } }; @@ -1256,7 +1256,7 @@ public: comma = ", "; } // Default not printed - maybe random init data - return out + "} "; + return out + "}"; } }; @@ -1629,7 +1629,7 @@ public: out += comma + VL_TO_STRING(m_storage[i]); comma = ", "; } - return out + "} "; + return out + "}"; } private: diff --git a/test_regress/t/t_array_method.v b/test_regress/t/t_array_method.v index 0ad0d177f..4f5021dad 100644 --- a/test_regress/t/t_array_method.v +++ b/test_regress/t/t_array_method.v @@ -19,41 +19,41 @@ module t; string v; q = '{1, 2, 2, 4, 3}; - `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3}"); // NOT tested: with ... selectors q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); q.sort with (item == 2); - `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); + `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2}"); q.sort(x) with (x == 3); - `checkp(q, "'{'h1, 'h4, 'h2, 'h2, 'h3} "); + `checkp(q, "'{'h1, 'h4, 'h2, 'h2, 'h3}"); q.rsort; - `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); q.rsort with (item == 2); - `checkp(q, "'{'h2, 'h2, 'h4, 'h3, 'h1} "); + `checkp(q, "'{'h2, 'h2, 'h4, 'h3, 'h1}"); qv = q.unique; - `checkp(qv, "'{'h2, 'h4, 'h3, 'h1} "); + `checkp(qv, "'{'h2, 'h4, 'h3, 'h1}"); qi = q.unique_index; qi.sort; - `checkp(qi, "'{'h0, 'h2, 'h3, 'h4} "); + `checkp(qi, "'{'h0, 'h2, 'h3, 'h4}"); q.reverse; - `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); + `checkp(q, "'{'h1, 'h3, 'h4, 'h2, 'h2}"); q.shuffle(); q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = q.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); + `checkp(qv, "'{'h2, 'h2}"); qv = q.find_first with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find with (item == 20); `checkp(qv, "'{}"); @@ -64,11 +64,11 @@ module t; qi = q.find_index with (item == 2); qi.sort; - `checkp(qi, "'{'h1, 'h2} "); + `checkp(qi, "'{'h1, 'h2}"); qi = q.find_first_index with (item == 2); - `checkp(qi, "'{'h1} "); + `checkp(qi, "'{'h1}"); qi = q.find_last_index with (item == 2); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); qi = q.find_index with (item == 20); qi.sort; @@ -79,9 +79,9 @@ module t; `checkp(qi, "'{}"); qv = q.min; - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); qv = q.max; - `checkp(qv, "'{'h4} "); + `checkp(qv, "'{'h4}"); // Reduction methods @@ -99,7 +99,7 @@ module t; `checkh(i, 32'ha); q = '{1, 2, 2, 4, 3}; - // `checkp(q, "'{1, 2, 2, 4, 3} "); + // `checkp(q, "'{1, 2, 2, 4, 3}"); i = q.sum with (item + 1); `checkh(i, 32'h11); diff --git a/test_regress/t/t_assoc.v b/test_regress/t/t_assoc.v index 24760e758..c8cae1b37 100644 --- a/test_regress/t/t_assoc.v +++ b/test_regress/t/t_assoc.v @@ -46,7 +46,7 @@ module t (/*AUTOARG*/ i = a.last(k); `checkh(i, 1); `checks(k, 4'd3); i = a.prev(k); `checkh(i, 1); `checks(k, 4'd2); i = a.prev(k); `checkh(i, 0); - `checkp(a, "'{'h2:\"bared\", 'h3:\"fooed\"} "); + `checkp(a, "'{'h2:\"bared\", 'h3:\"fooed\"}"); a.first(k); `checks(k, 4'd2); a.next(k); `checks(k, 4'd3); @@ -82,7 +82,7 @@ module t (/*AUTOARG*/ i = a.prev(k); `checkh(i, 1); `checks(k, "bar"); i = a.prev(k); `checkh(i, 0); `checkp(a["foo"], "\"fooed\""); - `checkp(a, "'{\"bar\":\"bared\", \"foo\":\"fooed\"} "); + `checkp(a, "'{\"bar\":\"bared\", \"foo\":\"fooed\"}"); a.delete("bar"); i = a.size(); `checkh(i, 1); diff --git a/test_regress/t/t_assoc_method.v b/test_regress/t/t_assoc_method.v index 5ae0e73fa..e78370d51 100644 --- a/test_regress/t/t_assoc_method.v +++ b/test_regress/t/t_assoc_method.v @@ -27,7 +27,7 @@ module t (/*AUTOARG*/); bit b; q = '{10:1, 11:2, 12:2, 13:4, 14:3}; - `checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3} "); + `checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3}"); // NOT tested: with ... selectors @@ -38,11 +38,11 @@ module t (/*AUTOARG*/); `checkp(qe, "'{}"); qv = q.unique; - `checkp(qv, "'{'h1, 'h2, 'h4, 'h3} "); + `checkp(qv, "'{'h1, 'h2, 'h4, 'h3}"); qv = qe.unique; `checkp(qv, "'{}"); qi = q.unique_index; qi.sort; - `checkp(qi, "'{'ha, 'hb, 'hd, 'he} "); + `checkp(qi, "'{'ha, 'hb, 'hd, 'he}"); qi = qe.unique_index; `checkp(qi, "'{}"); @@ -54,16 +54,16 @@ module t (/*AUTOARG*/); `checkh(points_qv.size, 2); qi = points_q.unique_index(p) with (p.x + p.y); qi.sort; - `checkp(qi, "'{'h0, 'h1, 'h5} "); + `checkp(qi, "'{'h0, 'h1, 'h5}"); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = q.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); + `checkp(qv, "'{'h2, 'h2}"); qv = q.find_first with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find with (item == 20); `checkp(qv, "'{}"); @@ -73,11 +73,11 @@ module t (/*AUTOARG*/); `checkp(qv, "'{}"); qi = q.find_index with (item == 2); qi.sort; - `checkp(qi, "'{'hb, 'hc} "); + `checkp(qi, "'{'hb, 'hc}"); qi = q.find_first_index with (item == 2); - `checkp(qi, "'{'hb} "); + `checkp(qi, "'{'hb}"); qi = q.find_last_index with (item == 2); - `checkp(qi, "'{'hc} "); + `checkp(qi, "'{'hc}"); qi = q.find_index with (item == 20); qi.sort; `checkp(qi, "'{}"); @@ -87,17 +87,17 @@ module t (/*AUTOARG*/); `checkp(qi, "'{}"); qi = q.find_index with (item.index == 12); - `checkp(qi, "'{'hc} "); + `checkp(qi, "'{'hc}"); qi = q.find with (item.index == 12); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); qv = q.min; - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); points_qv = points_q.min(p) with (p.x + p.y); if (points_qv[0].x != 1 || points_qv[0].y != 2) $stop; qv = q.max; - `checkp(qv, "'{'h4} "); + `checkp(qv, "'{'h4}"); points_qv = points_q.max(p) with (p.x + p.y); if (points_qv[0].x != 2 || points_qv[0].y != 4) $stop; diff --git a/test_regress/t/t_assoc_wildcard_method.v b/test_regress/t/t_assoc_wildcard_method.v index c8d51d6c6..29d374fee 100644 --- a/test_regress/t/t_assoc_wildcard_method.v +++ b/test_regress/t/t_assoc_wildcard_method.v @@ -26,7 +26,7 @@ module t (/*AUTOARG*/); string v; q = '{"a":1, "b":2, "c":2, "d":4, "e":3}; - `checkp(q, "'{\"a\":'h1, \"b\":'h2, \"c\":'h2, \"d\":'h4, \"e\":'h3} "); + `checkp(q, "'{\"a\":'h1, \"b\":'h2, \"c\":'h2, \"d\":'h4, \"e\":'h3}"); // NOT tested: with ... selectors @@ -37,7 +37,7 @@ module t (/*AUTOARG*/); `checkp(qe, "'{}"); qv = q.unique; - `checkp(qv, "'{'h1, 'h2, 'h4, 'h3} "); + `checkp(qv, "'{'h1, 'h2, 'h4, 'h3}"); qv = qe.unique; `checkp(qv, "'{}"); @@ -45,11 +45,11 @@ module t (/*AUTOARG*/); // These require an with clause or are illegal qv = q.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); + `checkp(qv, "'{'h2, 'h2}"); qv = q.find_first with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = q.find with (item == 20); `checkp(qv, "'{}"); @@ -63,9 +63,9 @@ module t (/*AUTOARG*/); //q.find_last_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad qv = q.min; - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); qv = q.max; - `checkp(qv, "'{'h4} "); + `checkp(qv, "'{'h4}"); qv = qe.min; `checkp(qv, "'{}"); diff --git a/test_regress/t/t_class_format.out b/test_regress/t/t_class_format.out index 4fef2ba25..219ddc86b 100644 --- a/test_regress/t/t_class_format.out +++ b/test_regress/t/t_class_format.out @@ -1,4 +1,4 @@ -''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' -''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' +''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44}, cwide:'{'h0, 'h0}, name:"object_name", r:2.2}' +''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944}, cwide:'{'h0, 'h0}, name:"object_name", r:2.2}' DEBUG: object_name (@0) message *-* All Finished *-* diff --git a/test_regress/t/t_dynarray.v b/test_regress/t/t_dynarray.v index 01b53d37d..2323ade0a 100644 --- a/test_regress/t/t_dynarray.v +++ b/test_regress/t/t_dynarray.v @@ -57,7 +57,7 @@ module t (/*AUTOARG*/ `checkh(a[0], 10); `checkh(a[1], 11); `checkh(a[2], 12); - `checkp(a, "'{'ha, 'hb, 'hc} "); + `checkp(a, "'{'ha, 'hb, 'hc}"); a.delete; `checkh(a.size, 0); diff --git a/test_regress/t/t_dynarray_concat.v b/test_regress/t/t_dynarray_concat.v index ad41498ca..fd21b3780 100644 --- a/test_regress/t/t_dynarray_concat.v +++ b/test_regress/t/t_dynarray_concat.v @@ -37,26 +37,26 @@ module t(/*AUTOARG*/); initial begin `checkp(da, "'{}"); - `checkp(da2, "'{'{'h1, 'h2} } "); + `checkp(da2, "'{'{'h1, 'h2}}"); `checkp(dd, "'{}"); - `checkp(dd1, "'{'{'h1} } "); - `checkp(dd2, "'{'{'h1, 'h2} } "); + `checkp(dd1, "'{'{'h1}}"); + `checkp(dd2, "'{'{'h1, 'h2}}"); `checkp(dq, "'{}"); - `checkp(dq1, "'{'{'h1} } "); - `checkp(dq2, "'{'{'h1, 'h2} } "); + `checkp(dq1, "'{'{'h1}}"); + `checkp(dq2, "'{'{'h1, 'h2}}"); `checkp(qa, "'{}"); - `checkp(qa2, "'{'{'h1, 'h2} } "); + `checkp(qa2, "'{'{'h1, 'h2}}"); `checkp(qd, "'{}"); - `checkp(qd1, "'{'{'h1} } "); - `checkp(qd2, "'{'{'h1, 'h2} } "); + `checkp(qd1, "'{'{'h1}}"); + `checkp(qd2, "'{'{'h1, 'h2}}"); `checkp(qq, "'{}"); - `checkp(qq1, "'{'{'h1} } "); - `checkp(qq2, "'{'{'h1, 'h2} } "); + `checkp(qq1, "'{'{'h1}}"); + `checkp(qq2, "'{'{'h1, 'h2}}"); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_dynarray_method.v b/test_regress/t/t_dynarray_method.v index 847cf1a69..97c25ca63 100644 --- a/test_regress/t/t_dynarray_method.v +++ b/test_regress/t/t_dynarray_method.v @@ -22,53 +22,53 @@ module t (/*AUTOARG*/); int i; d = '{1, 2, 2, 4, 3}; - `checkp(d, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h4, 'h3}"); d = {1, 2, 2, 4, 3}; - `checkp(d, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h4, 'h3}"); // sort/rsort with clause is the field to use for the sorting d.sort; - `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); d.sort with (10 - item); - `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); d.sort(x) with (10 - x); - `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); de.sort(x) with (10 - x); `checkp(de, "'{}"); d.rsort; - `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(d, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); d.rsort with (10 - item); - `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); de.rsort(x) with (10 - x); - `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); d = '{2, 2, 4, 1, 3}; qv = d.unique; - `checkp(qv, "'{'h2, 'h4, 'h1, 'h3} "); + `checkp(qv, "'{'h2, 'h4, 'h1, 'h3}"); qv = de.unique; `checkh(qv.size(), 0); qi = d.unique_index; qv.sort; - `checkp(qi, "'{'h0, 'h2, 'h3, 'h4} "); + `checkp(qi, "'{'h0, 'h2, 'h3, 'h4}"); qi = de.unique_index; `checkh(qi.size(), 0); d.reverse; - `checkp(d, "'{'h3, 'h1, 'h4, 'h2, 'h2} "); + `checkp(d, "'{'h3, 'h1, 'h4, 'h2, 'h2}"); de.reverse; `checkh(de.size(), 0); d.shuffle(); d.sort; - `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(d, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); de.shuffle(); `checkh(de.size(), 0); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = d.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); + `checkp(qv, "'{'h2, 'h2}"); qv = d.find_first with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = d.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); qv = d.find with (item == 20); `checkh(qv.size, 0); @@ -81,15 +81,15 @@ module t (/*AUTOARG*/); qvunused = d.find with (item == 20); qi = d.find_index with (item == 2); - qi.sort; `checkp(qi, "'{'h1, 'h2} "); + qi.sort; `checkp(qi, "'{'h1, 'h2}"); qi = d.find_first_index with (item == 2); - `checkp(qi, "'{'h1} "); + `checkp(qi, "'{'h1}"); qi = d.find_last_index with (item == 2); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); i = 2; qi = d.find_index with (item == i); - qi.sort; `checkp(qi, "'{'h1, 'h2} "); + qi.sort; `checkp(qi, "'{'h1, 'h2}"); qi = d.find_index with (item == 20); qi.sort; `checkh(qi.size, 0); @@ -99,12 +99,12 @@ module t (/*AUTOARG*/); `checkh(qi.size, 0); qi = d.find_index with (item.index == 2); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); qv = d.min; - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); qv = d.max; - `checkp(qv, "'{'h4} "); + `checkp(qv, "'{'h4}"); qv = de.min; `checkp(qv, "'{}"); qv = de.max; diff --git a/test_regress/t/t_interface_virtual.out b/test_regress/t/t_interface_virtual.out index 315bffb65..121116c8c 100644 --- a/test_regress/t/t_interface_virtual.out +++ b/test_regress/t/t_interface_virtual.out @@ -7,5 +7,5 @@ vb.addr=bb vb.data=22 ib.addr=bb ib.data=22 ca.fa.addr=a0 ca.fa.data=11 ca.fa.addr=b0 ca.fb.data=22 cb.fa.addr=b0 cb.fa.data=22 cb.fa.addr=a0 cb.fb.data=11 gen.x[0].addr=a0 gen.x[1].addr=b0 -gen='{x:'{top.t.ia, top.t.ib, null, null} } +gen='{x:'{top.t.ia, top.t.ib, null, null}} *-* All Finished *-* diff --git a/test_regress/t/t_queue.v b/test_regress/t/t_queue.v index 20660e2c4..140561649 100644 --- a/test_regress/t/t_queue.v +++ b/test_regress/t/t_queue.v @@ -149,7 +149,7 @@ module t (/*AUTOARG*/ v = q[4]; `checks(v, ""); //Unsup: `checkh(q[$], "b2"); - `checkp(q, "'{\"f2\", \"f1\", \"b1\", \"b2\"} "); + `checkp(q, "'{\"f2\", \"f1\", \"b1\", \"b2\"}"); `checkp(p, "'{}"); //Unsup: q.delete(1); diff --git a/test_regress/t/t_queue_method.v b/test_regress/t/t_queue_method.v index d5d8dfe4a..40798c065 100644 --- a/test_regress/t/t_queue_method.v +++ b/test_regress/t/t_queue_method.v @@ -52,27 +52,27 @@ module t (/*AUTOARG*/); string_q.push_back("b"); q = '{1, 2, 2, 4, 3}; - `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h4, 'h3}"); // sort/rsort with clause is the field to use for the sorting q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); q.sort with (10 - item); - `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); q.sort(x) with (10 - x); - `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); qe.sort(x) with (10 - x); `checkp(qe, "'{}"); q.rsort; - `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + `checkp(q, "'{'h4, 'h3, 'h2, 'h2, 'h1}"); q.rsort with (10 - item); - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); qe.rsort(x) with (10 - x); - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); q = '{2, 2, 4, 1, 3}; qv = q.unique; - `checkp(qv, "'{'h2, 'h4, 'h1, 'h3} "); + `checkp(qv, "'{'h2, 'h4, 'h1, 'h3}"); qv = qe.unique; `checkh(qv.size(), 0); qv = q.unique(x) with (x % 2); @@ -83,7 +83,7 @@ module t (/*AUTOARG*/); // According to IEEE 1800-2023 7.12.1, it is not specified which index of duplicated value should be returned `checkh(qi.size(), 4); qi.delete(1); - `checkp(qi, "'{'h0, 'h3, 'h4} "); + `checkp(qi, "'{'h0, 'h3, 'h4}"); qi = qe.unique_index; `checkh(qi.size(), 0); qi = q.unique_index(x) with (x % 3); qv.sort; @@ -94,31 +94,31 @@ module t (/*AUTOARG*/); `checkh(cls_qv.size(), 1); qi = cls_q.unique_index with (item.x % 2); qi.sort; - `checkp(qi, "'{'h0, 'h1} "); + `checkp(qi, "'{'h0, 'h1}"); q.reverse; - `checkp(q, "'{'h3, 'h1, 'h4, 'h2, 'h2} "); + `checkp(q, "'{'h3, 'h1, 'h4, 'h2, 'h2}"); qe.reverse; `checkh(qe.size(), 0); q.shuffle(); q.sort; - `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + `checkp(q, "'{'h1, 'h2, 'h2, 'h3, 'h4}"); qe.shuffle(); `checkh(qe.size(), 0); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = q.find with (item == 2); - `checkp(qv, "'{'h2, 'h2} "); + `checkp(qv, "'{'h2, 'h2}"); qv = q.find with (item[0] == 1); - `checkp(qv, "'{'h1, 'h3} "); + `checkp(qv, "'{'h1, 'h3}"); qv = q.find_first with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); points_qv = points_q.find_first with (item.z == 5); `checkh(points_qv[0].p.y, 3); points_qv = points_q.find_first with (item.p.x == 1); `checkh(points_qv[0].p.y, 2); qv = q.find_last with (item == 2); - `checkp(qv, "'{'h2} "); + `checkp(qv, "'{'h2}"); string_qv = string_q.find_last(s) with (s.tolower() == "a"); `checks(string_qv[0], "A"); @@ -133,15 +133,15 @@ module t (/*AUTOARG*/); qvunused = q.find with (item == 20); qi = q.find_index with (item == 2); - qi.sort; `checkp(qi, "'{'h1, 'h2} "); + qi.sort; `checkp(qi, "'{'h1, 'h2}"); qi = q.find_first_index with (item == 2); - `checkp(qi, "'{'h1} "); + `checkp(qi, "'{'h1}"); qi = q.find_last_index with (item == 2); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); i = 2; qi = q.find_index with (item == i); - qi.sort; `checkp(qi, "'{'h1, 'h2} "); + qi.sort; `checkp(qi, "'{'h1, 'h2}"); qi = q.find_index with (item == 20); qi.sort; `checkh(qi.size, 0); @@ -151,18 +151,18 @@ module t (/*AUTOARG*/); `checkh(qi.size, 0); qi = q.find_index with (item.index == 2); - `checkp(qi, "'{'h2} "); + `checkp(qi, "'{'h2}"); qi = q.find_index with (item.index == item); - `checkp(qi, "'{'h2, 'h3, 'h4} "); + `checkp(qi, "'{'h2, 'h3, 'h4}"); qv = q.min; - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); qv = q.min(x) with (x + 1); - `checkp(qv, "'{'h1} "); + `checkp(qv, "'{'h1}"); qv = q.max; - `checkp(qv, "'{'h4} "); + `checkp(qv, "'{'h4}"); qv = q.max(x) with ((x % 4) + 100); - `checkp(qv, "'{'h3} "); + `checkp(qv, "'{'h3}"); qv = qe.min; `checkp(qv, "'{}"); qv = qe.max; diff --git a/test_regress/t/t_queue_slice.v b/test_regress/t/t_queue_slice.v index d7eec445a..bc622669e 100644 --- a/test_regress/t/t_queue_slice.v +++ b/test_regress/t/t_queue_slice.v @@ -20,46 +20,46 @@ module t (/*AUTOARG*/); q.push_front("non-empty"); i = q.size(); `checkh(i, 1); - `checkp(q, "'{\"non-empty\"} "); + `checkp(q, "'{\"non-empty\"}"); q = '{}; i = q.size(); `checkh(i, 0); q = '{"q"}; - `checkp(q, "'{\"q\"} "); + `checkp(q, "'{\"q\"}"); q = {}; i = q.size(); `checkh(i, 0); q = '{"q", "b", "c", "d", "e", "f"}; if (q[0] !== "q") $stop; - `checkp(q, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); + `checkp(q, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"}"); q = {"q", "b", "c", "d", "e", "f"}; - `checkp(q, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); + `checkp(q, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"}"); q.delete(1); v = q[1]; `checks(v, "c"); - `checkp(q, "'{\"q\", \"c\", \"d\", \"e\", \"f\"} "); + `checkp(q, "'{\"q\", \"c\", \"d\", \"e\", \"f\"}"); q.insert(0, "ins0"); q.insert(2, "ins2"); v = q[0]; `checks(v, "ins0"); v = q[2]; `checks(v, "ins2"); - `checkp(q, "'{\"ins0\", \"q\", \"ins2\", \"c\", \"d\", \"e\", \"f\"} "); + `checkp(q, "'{\"ins0\", \"q\", \"ins2\", \"c\", \"d\", \"e\", \"f\"}"); // Slicing q = '{"q", "b", "c", "d", "e", "f"}; q = q[-1:0]; - `checkp(q, "'{\"q\"} "); + `checkp(q, "'{\"q\"}"); q = '{"q", "b", "c", "d", "e", "f"}; q = q[2:3]; - `checkp(q, "'{\"c\", \"d\"} "); + `checkp(q, "'{\"c\", \"d\"}"); q = '{"q", "b", "c", "d", "e", "f"}; q = q[3:$]; - `checkp(q, "'{\"d\", \"e\", \"f\"} "); + `checkp(q, "'{\"d\", \"e\", \"f\"}"); q = q[$:$]; - `checkp(q, "'{\"f\"} "); + `checkp(q, "'{\"f\"}"); // Similar using implied notation q = '{"f"}; @@ -67,14 +67,14 @@ module t (/*AUTOARG*/); q = {q, "f2"}; // push_front q = {"b1", q}; // push_back q = {"b2", q}; // push_back - `checkp(q, "'{\"b2\", \"b1\", \"f\", \"f1\", \"f2\"} "); + `checkp(q, "'{\"b2\", \"b1\", \"f\", \"f1\", \"f2\"}"); q = {q[0], q[2:$]}; // delete element 1 - `checkp(q, "'{\"b2\", \"f\", \"f1\", \"f2\"} "); + `checkp(q, "'{\"b2\", \"f\", \"f1\", \"f2\"}"); q = {"a", "b"}; q = {q, q}; - `checkp(q, "'{\"a\", \"b\", \"a\", \"b\"} "); + `checkp(q, "'{\"a\", \"b\", \"a\", \"b\"}"); begin string ai[$] = '{ "Foo", "Bar" }; diff --git a/test_regress/t/t_stream_bitqueue.v b/test_regress/t/t_stream_bitqueue.v index c44bdf749..a9fc0ed91 100644 --- a/test_regress/t/t_stream_bitqueue.v +++ b/test_regress/t/t_stream_bitqueue.v @@ -263,16 +263,16 @@ module t ( /*AUTOARG*/ `checkh(bytq_init[0], 8'h84); `checkh(bytq_init[1], 8'haa); s = $sformatf("bytq_init=%p", bytq_init); - `checks(s, "bytq_init='{'h84, 'haa} "); + `checks(s, "bytq_init='{'h84, 'haa}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bitq=%p", bitq); `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1}"); s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa} "); + `checks(s, "bytq='{'h84, 'haa}"); /* Generalized block-reversal semantics for the outer left-stream when blockSize > 1. @@ -298,12 +298,12 @@ module t ( /*AUTOARG*/ bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bitq=%p", bitq); `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1} "); + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1}"); `checkh(bytq[0], 8'h84); `checkh(bytq[1], 8'haa); `checkh(bytq[2], 8'h80); s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h80} "); + `checks(s, "bytq='{'h84, 'haa, 'h80}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -312,9 +312,9 @@ module t ( /*AUTOARG*/ bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bitq=%p", bitq); `checks(s, - "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1} "); + "bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1}"); s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'hc0} "); + `checks(s, "bytq='{'h84, 'haa, 'hc0}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -323,7 +323,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b1); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'he0} "); + `checks(s, "bytq='{'h84, 'haa, 'he0}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -333,7 +333,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b0); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h70} "); + `checks(s, "bytq='{'h84, 'haa, 'h70}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -344,7 +344,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b1); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'hb8} "); + `checks(s, "bytq='{'h84, 'haa, 'hb8}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -356,7 +356,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b0); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h5c} "); + `checks(s, "bytq='{'h84, 'haa, 'h5c}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -369,7 +369,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b0); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h2e} "); + `checks(s, "bytq='{'h84, 'haa, 'h2e}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -383,7 +383,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b1); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h97} "); + `checks(s, "bytq='{'h84, 'haa, 'h97}"); bytq = bytq_init; bitq = {<<8{bit_q_t'({<<{bytq}})}}; @@ -398,7 +398,7 @@ module t ( /*AUTOARG*/ bitq.push_back(1'b1); bytq = {<<8{bit_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h84, 'haa, 'h97, 'h80} "); + `checks(s, "bytq='{'h84, 'haa, 'h97, 'h80}"); end // Test StreamR (>>) operations - fairly simple since this should maintain left-to-right order. @@ -409,13 +409,13 @@ module t ( /*AUTOARG*/ bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; bitq = {>>4{bit_q_t'({<<{bitq}})}}; s = $sformatf("bitq=%p", bitq); - `checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + `checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1}"); bytq = {8'h84, 8'haa}; bitq = {>>{bit_q_t'({<<{bytq}})}}; s = $sformatf("bitq=%p", bitq); `checks(s, - "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1} "); + "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1}"); bitq = { 1'b1, @@ -437,23 +437,23 @@ module t ( /*AUTOARG*/ }; bytq = {>>2{byte_q_t'({<<{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h43, 'h55} "); + `checks(s, "bytq='{'h43, 'h55}"); bytq = {8'h12, 8'h34, 8'h56}; bytq = {>>{byte_q_t'({<<{bytq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h6a, 'h2c, 'h48} "); + `checks(s, "bytq='{'h6a, 'h2c, 'h48}"); bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0}; bitq = {>>6{bit_q_t'({>>{bitq}})}}; s = $sformatf("bitq=%p", bitq); - `checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + `checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0}"); bytq = {8'h84, 8'haa}; bitq = {>>{bit_q_t'({>>{bytq}})}}; s = $sformatf("bitq=%p", bitq); `checks(s, - "bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} "); + "bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0}"); bitq = { 1'b1, @@ -475,12 +475,12 @@ module t ( /*AUTOARG*/ }; bytq = {>>8{byte_q_t'({>>{bitq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'haa, 'hc2} "); + `checks(s, "bytq='{'haa, 'hc2}"); bytq = {8'h12, 8'h34, 8'h56}; bytq = {>>{byte_q_t'({>>{bytq}})}}; s = $sformatf("bytq=%p", bytq); - `checks(s, "bytq='{'h12, 'h34, 'h56} "); + `checks(s, "bytq='{'h12, 'h34, 'h56}"); end $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_stream_crc_example.v b/test_regress/t/t_stream_crc_example.v index 7f09a08d4..6b15bb2d1 100644 --- a/test_regress/t/t_stream_crc_example.v +++ b/test_regress/t/t_stream_crc_example.v @@ -53,14 +53,14 @@ module t ( /*AUTOARG*/ po = {<<8{bit_q_t'({<<{bits}})}}; s = $sformatf("p=%p", p); - `checks(s, "p='{'h84, 'haa} "); + `checks(s, "p='{'h84, 'haa}"); s = $sformatf("bits=%p", bits); `checks(s, - "bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} "); + "bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1}"); s = $sformatf("po=%p", po); - `checks(s, "po='{'h8, 'h55, 'h80} "); + `checks(s, "po='{'h8, 'h55, 'h80}"); end always_ff @(posedge clk) begin diff --git a/test_regress/t/t_stream_dynamic.v b/test_regress/t/t_stream_dynamic.v index e9bdc97d9..ab6cfaa4d 100644 --- a/test_regress/t/t_stream_dynamic.v +++ b/test_regress/t/t_stream_dynamic.v @@ -36,7 +36,7 @@ module t (/*AUTOARG*/); arr160 = '{2{160'h0123456789abcdef0123456789abcdef01234567}}; { >> bit {arr}} = bit6; - `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0}"); ans = { >> bit {arr} }; `checkh(ans, bit6); @@ -44,7 +44,7 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit {arr}} = bit6; - `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1}"); ans = { << bit {arr} }; `checkh(ans, bit6); @@ -55,7 +55,7 @@ module t (/*AUTOARG*/); `ifdef VERILATOR // This set flags errors on other simulators { >> bit[1:0] {arr2}} = bit6; - `checkp(arr2, "'{'h3, 'h2, 'h0} "); + `checkp(arr2, "'{'h3, 'h2, 'h0}"); ans = { >> bit[1:0] {arr2} }; `checkh(ans, bit6); @@ -64,7 +64,7 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit[1:0] {arr2}} = bit6; - `checkp(arr2, "'{'h0, 'h2, 'h3} "); + `checkp(arr2, "'{'h0, 'h2, 'h3}"); ans = { << bit[1:0] {arr2} }; `checkh(ans, bit6); @@ -73,7 +73,7 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { >> bit [5:0] {arr6} } = bit6; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); ans = { >> bit[5:0] {arr6} }; `checkh(ans, bit6); @@ -82,7 +82,7 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit [5:0] {arr6} } = bit6; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); ans = { << bit[5:0] {arr6} }; `checkh(ans, bit6); diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v index ee6514123..571c96eda 100644 --- a/test_regress/t/t_stream_unpack.v +++ b/test_regress/t/t_stream_unpack.v @@ -43,10 +43,10 @@ module t (/*AUTOARG*/); logic [127:0] p[]; { >> bit {arr}} = bit6; - `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0}"); arr = { >> bit {bit6}}; - `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + `checkp(arr, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0}"); ans = { >> bit {arr} }; `checkh(ans, bit6); @@ -64,10 +64,10 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit {arr}} = bit6; - `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1}"); arr = { << bit {bit6}}; - `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + `checkp(arr, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1}"); ans = { << bit {arr} }; `checkh(ans, bit6); @@ -85,10 +85,10 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { >> bit[1:0] {arr2}} = bit6; - `checkp(arr2, "'{'h3, 'h2, 'h0} "); + `checkp(arr2, "'{'h3, 'h2, 'h0}"); arr2 = { >> bit[1:0] {bit6}}; - `checkp(arr2, "'{'h3, 'h2, 'h0} "); + `checkp(arr2, "'{'h3, 'h2, 'h0}"); ans = { >> bit[1:0] {arr2} }; `checkh(ans, bit6); @@ -106,7 +106,7 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit[1:0] {arr2}} = bit6; - `checkp(arr2, "'{'h0, 'h2, 'h3} "); + `checkp(arr2, "'{'h0, 'h2, 'h3}"); ans = { << bit[1:0] {arr2} }; `checkh(ans, bit6); @@ -124,10 +124,10 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { >> bit [5:0] {arr6} } = bit6; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); arr6 = { >> bit [5:0] {bit6}}; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); ans = { >> bit[5:0] {arr6} }; `checkh(ans, bit6); @@ -145,10 +145,10 @@ module t (/*AUTOARG*/); `checkh(ans_enum, bit6); { << bit [5:0] {arr6} } = bit6; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); arr6 = { << bit [5:0] {bit6}}; - `checkp(arr6, "'{'h38} "); + `checkp(arr6, "'{'h38}"); ans = { << bit[5:0] {arr6} }; `checkh(ans, bit6); @@ -211,15 +211,15 @@ module t (/*AUTOARG*/); `checkh(d, 16'b0100110110001100); { >> {e, f}} = d; - `checkp(e, "'{'h4, 'hd} "); - `checkp(f, "'{'h1, 'h0, 'h0, 'h0, 'h1, 'h1, 'h0, 'h0} "); + `checkp(e, "'{'h4, 'hd}"); + `checkp(f, "'{'h1, 'h0, 'h0, 'h0, 'h1, 'h1, 'h0, 'h0}"); d = { << 4 {a, b, c}}; `checkh(d, 16'b1100100011010100); { << 2 {e, f}} = d; - `checkp(e, "'{'h1, 'h7} "); - `checkp(f, "'{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h1, 'h1} "); + `checkp(e, "'{'h1, 'h7}"); + `checkp(f, "'{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h1, 'h1}"); g = { << 8 {16'hABCD}}; `checkh(g, 16'hCDAB); diff --git a/test_regress/t/t_struct_nest_uarray.v b/test_regress/t/t_struct_nest_uarray.v index e3f8cda88..a1884398e 100644 --- a/test_regress/t/t_struct_nest_uarray.v +++ b/test_regress/t/t_struct_nest_uarray.v @@ -30,10 +30,10 @@ module t (/*AUTOARG*/); initial begin str.el[0].val.next = 6; - `checkp(str, "'{el:'{'{val:'{next:'h6}}} }"); + `checkp(str, "'{el:'{'{val:'{next:'h6}}}}"); pstr.el[0].val.next = 6; - `checkp(str, "'{el:'{'{val:'{next:'h6}}} }"); + `checkp(str, "'{el:'{'{val:'{next:'h6}}}}"); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_typename_min.v b/test_regress/t/t_typename_min.v index 8d39ce5cc..49499924f 100644 --- a/test_regress/t/t_typename_min.v +++ b/test_regress/t/t_typename_min.v @@ -17,17 +17,17 @@ module t; s = $typename(array.min); `checks(s, "int$[$]"); s = $sformatf("%p", array.min); - `checks(s, "'{'h1} "); + `checks(s, "'{'h1}"); s = $typename(queue.min); `checks(s, "int$[$]"); s = $sformatf("%p", queue.min); - `checks(s, "'{'h1} "); + `checks(s, "'{'h1}"); s = $typename(assoc.min); `checks(s, "int$[$]"); s = $sformatf("%p", assoc.min); - `checks(s, "'{'h1} "); + `checks(s, "'{'h1}"); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_unpacked_array_p_fmt.out b/test_regress/t/t_unpacked_array_p_fmt.out index 6872828ed..33a1ca411 100644 --- a/test_regress/t/t_unpacked_array_p_fmt.out +++ b/test_regress/t/t_unpacked_array_p_fmt.out @@ -1,3 +1,3 @@ -%p='{'h0, 'h1, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h1, 'h0} -%p='{'{'h0, 'h1, 'h1, 'h0} , '{'h1, 'h0, 'h0, 'h1} , '{'h1, 'h0, 'h0, 'h1} , '{'h0, 'h1, 'h1, 'h0} } +%p='{'h0, 'h1, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h1, 'h0} +%p='{'{'h0, 'h1, 'h1, 'h0}, '{'h1, 'h0, 'h0, 'h1}, '{'h1, 'h0, 'h0, 'h1}, '{'h0, 'h1, 'h1, 'h0}} *-* All Finished *-* From 457fcc267b7364aab05478e984ab5efaff036b9a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 21 Aug 2025 21:44:31 -0400 Subject: [PATCH 211/252] Support elaboration-time printing of unpacked array with `%p` (#4732). --- Changes | 1 + src/V3Const.cpp | 17 +++++++ src/V3Simulate.h | 41 +++++++++++++++- test_regress/t/t_display_p_elab.py | 18 +++++++ test_regress/t/t_display_p_elab.v | 77 ++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_display_p_elab.py create mode 100644 test_regress/t/t_display_p_elab.v diff --git a/Changes b/Changes index 01781f87b..00801af05 100644 --- a/Changes +++ b/Changes @@ -100,6 +100,7 @@ Verilator 5.038 2025-07-08 * Support redeclaring type as non-type; major parsing change (#2412) (#6020) (#6042) (#6044). * Support scoped `new` (#4199). +* Support elaboration-time printing of unpacked array with `%p` (#4732). * Support constrained random for associative arrays (#5985) (#5986). [Yilou Wang] * Support assignments to concatenations with impure RHS (#6002). [Ryszard Rozak, Antmicro Ltd.] * Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 8c851fae5..77a81b6d0 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2521,6 +2521,22 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } + bool matchToStringNConst(AstToStringN* nodep) { + iterateChildren(nodep); + if (AstInitArray* const initp = VN_CAST(nodep->lhsp(), InitArray)) { + if (!(m_doExpensive || m_params)) return false; + // At present only support 1D unpacked arrays + const auto initOfConst = [](const AstNode* const nodep) -> bool { // + return VN_IS(nodep, Const) || VN_IS(nodep, InitItem); + }; + if (initp->initsp() && !initp->initsp()->forall(initOfConst)) return false; + if (initp->defaultp() && !initp->defaultp()->forall(initOfConst)) return false; + } else if (!VN_IS(nodep->lhsp(), Const)) { + return false; + } + replaceWithSimulation(nodep); + return true; + } int operandConcatMove(const AstConcat* nodep) { // CONCAT under concat (See moveConcat) // Return value: true indicates to do it; 2 means move to LHS @@ -3892,6 +3908,7 @@ class ConstVisitor final : public VNVisitor { TREEOPA("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); TREEOPA("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); TREEOPA("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_AS(nodep->lhsp(), Const)->num().toString())"); + TREEOP ("AstToStringN{matchToStringNConst(nodep)}", "DONE"); // Custom // Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)"); TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)"); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 30e3468d9..e3b1d0545 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -410,6 +410,40 @@ private: } } + string toStringRecurse(AstNodeExpr* nodep) { + // Return string representation, or clearOptimizable + const AstNodeExpr* const valuep = fetchValue(nodep); + if (const AstConst* const avaluep = VN_CAST(valuep, Const)) { + return "'h"s + avaluep->num().displayed(nodep, "%0x"); + } + if (const AstInitArray* const avaluep = VN_CAST(valuep, InitArray)) { + string result = "'{"; + string comma; + if (VN_IS(nodep->dtypep(), AssocArrayDType)) { + if (avaluep->defaultp()) { + result += comma + "default:" + toStringRecurse(avaluep->defaultp()); + comma = ", "; + } + const auto& mapr = avaluep->map(); + for (const auto& itr : mapr) { + result += comma + cvtToStr(itr.first) + ":" + + toStringRecurse(itr.second->valuep()); + comma = ", "; + } + } else if (const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep(), UnpackArrayDType)) { + for (int n = 0; n < dtypep->elementsConst(); ++n) { + result += comma + toStringRecurse(avaluep->getIndexDefaultedValuep(n)); + comma = ", "; + } + } + result += "}"; + return result; + } + clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE + return ""; + } + // VISITORS void visit(AstAlways* nodep) override { if (jumpingOver()) return; @@ -1265,7 +1299,12 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); iterateChildrenConst(nodep); - clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE + if (!optimizable()) return; + std::string result = toStringRecurse(nodep->lhsp()); + if (!optimizable()) return; + AstConst* const resultConstp = new AstConst{nodep->fileline(), AstConst::String{}, result}; + setValue(nodep, resultConstp); + m_reclaimValuesp.push_back(resultConstp); } void visit(AstCoverInc* nodep) override { m_isCoverage = true; } diff --git a/test_regress/t/t_display_p_elab.py b/test_regress/t/t_display_p_elab.py new file mode 100755 index 000000000..2b4837fcc --- /dev/null +++ b/test_regress/t/t_display_p_elab.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_st') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_display_p_elab.v b/test_regress/t/t_display_p_elab.v new file mode 100644 index 000000000..4affaf3b1 --- /dev/null +++ b/test_regress/t/t_display_p_elab.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`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); +`ifdef verilator + `define no_optimize(v) $c(v) +`else + `define no_optimize(v) (v) +`endif +// verilog_format: on + + +module t; + + // $sformatf is not supported as parameter by some simulators + + parameter int I = 234; + parameter string IS = $sformatf(">%p<", I); + initial `checks(IS, "> 234<"); + + parameter real R = 1.234; + parameter string RS = $sformatf(">%p<", R); + initial `checks(RS, ">1.234<"); + + int u[2]; + parameter int U[2] = '{5, 6}; + parameter string US = $sformatf(">%p<", U); + initial `checks(US, ">'{'h5, 'h6}<"); + +`ifndef VERILATOR + // Generally not supported by others + parameter int Q[$] = '{1, 2}; + parameter string QS = $sformatf(">%p<", Q); + initial `checks(QS, "x"); + + // Generally not supported by others + parameter int D[] = '{1, 2}; + parameter string DS = $sformatf(">%p<", D); + initial `checks(DS, ">'{1, 2}<"); + + parameter int A[int] = '{1: 10, 2: 20}; + parameter string AS = $sformatf(">%p<", A); + initial `checks(AS, "x"); + + typedef struct {int f, s;} s_t; + parameter s_t S = '{f: 10, s: 20}; + parameter string SS = $sformatf(">%p<", S); + initial `checks(SS, ">'{f:10, s:20}<"); +`endif + + // Original issue + function integer infofunc(); + $info("%p", U); + return 0; + endfunction + localparam integer return_val = infofunc(); + + string s; + + initial begin + #1; + u[0] = `no_optimize(5); + u[1] = `no_optimize(6); + s = $sformatf(">%p<", u); + `checks(s, ">'{'h5, 'h6}<"); + + `checks(US, ">'{'h5, 'h6}<"); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From b19215770b8478df2713d21f2ad4f14796bfffff Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Fri, 22 Aug 2025 12:21:13 +0200 Subject: [PATCH 212/252] Fix direct NBA to dynamically-sized variable (#6310) --- src/V3WidthCommit.cpp | 18 +++++++++- test_regress/t/t_assign_automatic_bad.out | 40 ++++++++++++++++------- test_regress/t/t_assign_automatic_bad.v | 20 ++++++++++-- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 53031bf2c..ccf3d80cd 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -43,6 +43,8 @@ class WidthCommitVisitor final : public VNVisitor { // STATE AstNodeModule* m_modp = nullptr; std::string m_contNba; // In continuous- or non-blocking assignment + bool m_dynsizedelem + = false; // Writing a dynamically-sized array element, not the array itself VMemberMap m_memberMap; // Member names cached for fast lookup bool m_underSel = false; // Whether is currently under AstMemberSel or AstSel @@ -138,7 +140,7 @@ private: else if (varp->isClassMember() && !varp->lifetime().isStatic() && !VN_IS(varDtp, IfaceRefDType)) varType = "Class non-static"; - else if (varDtp->isDynamicallySized()) + else if (varDtp->isDynamicallySized() && m_dynsizedelem) varType = "Dynamically-sized"; if (!varType.empty()) { UINFO(1, " Related var dtype: " << varDtp); @@ -395,6 +397,20 @@ private: // This check could go anywhere after V3Param nodep->v3fatalSrc("Presels should have been removed before this point"); } + void visit(AstCMethodHard* nodep) override { + VL_RESTORER(m_dynsizedelem); + if (nodep->name() == "atWrite" || nodep->name() == "atWriteAppend" + || nodep->name() == "at") + m_dynsizedelem = true; + iterateChildren(nodep); + editDType(nodep); + } + void visit(AstAssocSel* nodep) override { + VL_RESTORER(m_dynsizedelem); + m_dynsizedelem = true; + iterateChildren(nodep); + editDType(nodep); + } void visit(AstSel* nodep) override { { VL_RESTORER(m_underSel); diff --git a/test_regress/t/t_assign_automatic_bad.out b/test_regress/t/t_assign_automatic_bad.out index 6fb3bcec9..45e0790c1 100644 --- a/test_regress/t/t_assign_automatic_bad.out +++ b/test_regress/t/t_assign_automatic_bad.out @@ -1,26 +1,42 @@ -%Error: t/t_assign_automatic_bad.v:31:10: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_auto3' +%Error: t/t_assign_automatic_bad.v:37:10: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_auto3' : ... note: In instance 't' - 31 | assign bad_auto3 = 2; + 37 | assign bad_auto3 = 2; | ^~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_assign_automatic_bad.v:32:10: Dynamically-sized variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_dyn5' +%Error: t/t_assign_automatic_bad.v:38:10: Dynamically-sized variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'bad_dyn5' : ... note: In instance 't' - 32 | assign bad_dyn5 = empty_dyn; + 38 | assign bad_dyn5[0] = empty_dyn; | ^~~~~~~~ -%Error: t/t_assign_automatic_bad.v:33:12: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'm_bad1' +%Error: t/t_assign_automatic_bad.v:40:12: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'm_bad1' : ... note: In instance 't' - 33 | assign c.m_bad1 = 2; + 40 | assign c.m_bad1 = 2; | ^~~~~~ -%Error: t/t_assign_automatic_bad.v:43:5: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_auto4' +%Error: t/t_assign_automatic_bad.v:50:5: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_auto4' : ... note: In instance 't' - 43 | bad_auto4 <= 2; + 50 | bad_auto4 <= 2; | ^~~~~~~~~ -%Error: t/t_assign_automatic_bad.v:44:5: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_dyn6' +%Error: t/t_assign_automatic_bad.v:51:5: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_dyn6' : ... note: In instance 't' - 44 | bad_dyn6 <= empty_dyn; + 51 | bad_dyn6[0] <= 2; | ^~~~~~~~ -%Error: t/t_assign_automatic_bad.v:46:7: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'm_bad2' +%Error: t/t_assign_automatic_bad.v:53:5: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_queue' : ... note: In instance 't' - 46 | c.m_bad2 <= 2; + 53 | bad_queue[0] <= 2; + | ^~~~~~~~~ +%Error: t/t_assign_automatic_bad.v:55:5: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'bad_assoc' + : ... note: In instance 't' + 55 | bad_assoc[0] <= 2; + | ^~~~~~~~~ +%Error: t/t_assign_automatic_bad.v:58:7: Automatic lifetime variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 'm_bad2' + : ... note: In instance 't' + 58 | c.m_bad2 <= 2; | ^~~~~~ +%Error: t/t_assign_automatic_bad.v:60:10: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 's_dyn' + : ... note: In instance 't' + 60 | Cls::s_dyn[0] <= 2; + | ^~~~~ +%Error: t/t_assign_automatic_bad.v:62:26: Dynamically-sized variable not allowed in nonblocking assignment (IEEE 1800-2023 6.21): 's_dyn' + : ... note: In instance 't' + 62 | clist[bad_dyn6[0]++].s_dyn[0] <= '1; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_assign_automatic_bad.v b/test_regress/t/t_assign_automatic_bad.v index 98905ca97..b6e233b5f 100644 --- a/test_regress/t/t_assign_automatic_bad.v +++ b/test_regress/t/t_assign_automatic_bad.v @@ -13,6 +13,7 @@ class Cls; static int s_ok1; static int s_ok2; + static int s_dyn[]; int m_bad1; int m_bad2; endclass @@ -27,9 +28,15 @@ module t(clk); int bad_dyn5[]; int bad_dyn6[]; int empty_dyn[]; + int empty_queue[$]; + int empty_assoc[int]; + int bad_queue[$]; + int bad_assoc[int]; + Cls clist[1]; assign bad_auto3 = 2; // <--- Error: continuous automatic - assign bad_dyn5 = empty_dyn; // <--- Error: continuous dynarray + assign bad_dyn5[0] = empty_dyn; // <--- Error: continuous dynarray element + assign bad_dyn5 = empty_dyn; // <--- OK: continuous dynarray assignment, not to its element assign c.m_bad1 = 2; // <--- Error: continuous class non-static // Only one simulator fails on this, probably not legal // assign Cls::s_ok1 = 2; // OK: continuous class static @@ -41,9 +48,18 @@ module t(clk); always @(posedge clk) begin bad_auto4 <= 2; // <--- Error: nonblocking automatic - bad_dyn6 <= empty_dyn; // <--- Error: nonblocking dynarray + bad_dyn6[0] <= 2; // <--- Error: nonblocking dynarray element + bad_dyn6 <= empty_dyn; // <--- OK: nonblocking dynarray assignment, not to its element + bad_queue[0] <= 2; // Error: nonblocking queue element assignment + bad_queue <= empty_queue; // OK: nonblocking assignment to queue itself, not to its element + bad_assoc[0] <= 2; // Error: nonblocking associative array element assignment + bad_assoc <= empty_assoc; // OK: nonblocking assignment to associative array itself, not to its element Cls::s_ok2 <= 2; // OK: nonblocking class static c.m_bad2 <= 2; // <--- Error: nonblocking class automatic + Cls::s_dyn <= 2; // OK: nonblocking class static dynarray assignment, not to its element + Cls::s_dyn[0] <= 2; // Error: nonblocking class static dynarray element + clist[bad_dyn6[0]++].s_dyn <= '1; // OK: direct nonblocking assignment to dynamically-sized array + clist[bad_dyn6[0]++].s_dyn[0] <= '1; // Error: nonblocking assigment to dynamically-sized array element mt(ok_7); $stop; end From b95a974ff197d2400fd5f7f855b952ca28cb47d5 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Fri, 22 Aug 2025 12:44:35 +0200 Subject: [PATCH 213/252] Support generic interfaces (#6272) --- src/V3Ast.cpp | 1 + src/V3Ast.h | 4 +- src/V3AstNodeDType.h | 36 +++++ src/V3AstNodeOther.h | 4 + src/V3AstNodes.cpp | 6 + src/V3LinkDot.cpp | 142 +++++++++++++++++- src/V3LinkParse.cpp | 7 + src/V3Param.cpp | 45 +++++- src/verilog.y | 25 ++- test_regress/t/t_class_param_noinit_bad.out | 7 +- test_regress/t/t_constraint_json_only.out | 4 +- test_regress/t/t_dump_json.out | 6 +- test_regress/t/t_interface_generic.py | 18 +++ test_regress/t/t_interface_generic.v | 33 ++++ test_regress/t/t_interface_generic2.py | 18 +++ test_regress/t/t_interface_generic2.v | 35 +++++ test_regress/t/t_interface_generic_array.py | 18 +++ test_regress/t/t_interface_generic_array.v | 26 ++++ test_regress/t/t_interface_generic_bad.out | 13 ++ test_regress/t/t_interface_generic_bad.py | 16 ++ test_regress/t/t_interface_generic_bad.v | 33 ++++ test_regress/t/t_interface_generic_bad2.out | 24 +++ test_regress/t/t_interface_generic_bad2.py | 16 ++ test_regress/t/t_interface_generic_bad2.v | 28 ++++ test_regress/t/t_interface_generic_bad3.out | 9 ++ test_regress/t/t_interface_generic_bad3.py | 16 ++ test_regress/t/t_interface_generic_bad3.v | 27 ++++ test_regress/t/t_interface_generic_bad4.out | 5 + test_regress/t/t_interface_generic_bad4.py | 16 ++ test_regress/t/t_interface_generic_bad4.v | 33 ++++ .../t/t_interface_generic_function.py | 18 +++ test_regress/t/t_interface_generic_function.v | 33 ++++ .../t/t_interface_generic_function_bad.out | 7 + .../t/t_interface_generic_function_bad.py | 16 ++ .../t/t_interface_generic_function_bad.v | 26 ++++ .../t/t_interface_generic_iface_param.py | 18 +++ .../t/t_interface_generic_iface_param.v | 27 ++++ .../t/t_interface_generic_mod_param.py | 18 +++ .../t/t_interface_generic_mod_param.v | 26 ++++ test_regress/t/t_interface_generic_modport.py | 18 +++ test_regress/t/t_interface_generic_modport.v | 29 ++++ .../t/t_interface_generic_modport2.py | 18 +++ test_regress/t/t_interface_generic_modport2.v | 29 ++++ .../t/t_interface_generic_modport_bad.out | 5 + .../t/t_interface_generic_modport_bad.py | 16 ++ .../t/t_interface_generic_modport_bad.v | 26 ++++ .../t/t_interface_generic_modport_bad2.out | 6 + .../t/t_interface_generic_modport_bad2.py | 16 ++ .../t/t_interface_generic_modport_bad2.v | 29 ++++ .../t/t_interface_generic_modport_bad3.out | 7 + .../t/t_interface_generic_modport_bad3.py | 16 ++ .../t/t_interface_generic_modport_bad3.v | 30 ++++ .../t/t_interface_generic_modport_function.py | 18 +++ .../t/t_interface_generic_modport_function.v | 36 +++++ .../t_interface_generic_modport_function2.py | 18 +++ .../t/t_interface_generic_modport_function2.v | 36 +++++ ...interface_generic_modport_function_bad.out | 7 + ..._interface_generic_modport_function_bad.py | 16 ++ ...t_interface_generic_modport_function_bad.v | 36 +++++ .../t/t_interface_generic_modport_param.py | 18 +++ .../t/t_interface_generic_modport_param.v | 32 ++++ .../t/t_interface_generic_modport_task.py | 18 +++ .../t/t_interface_generic_modport_task.v | 33 ++++ .../t/t_interface_generic_modport_task2.py | 18 +++ .../t/t_interface_generic_modport_task2.v | 32 ++++ .../t_interface_generic_modport_task_bad.out | 7 + .../t/t_interface_generic_modport_task_bad.py | 16 ++ .../t/t_interface_generic_modport_task_bad.v | 36 +++++ test_regress/t/t_interface_generic_normal.py | 18 +++ test_regress/t/t_interface_generic_normal.v | 30 ++++ .../t/t_interface_generic_positional.py | 18 +++ .../t/t_interface_generic_positional.v | 26 ++++ .../t/t_interface_generic_submod_param.py | 18 +++ .../t/t_interface_generic_submod_param.v | 39 +++++ test_regress/t/t_interface_generic_task.py | 18 +++ test_regress/t/t_interface_generic_task.v | 33 ++++ test_regress/t/t_interface_generic_task2.py | 18 +++ test_regress/t/t_interface_generic_task2.v | 33 ++++ .../t/t_interface_generic_task_bad.out | 7 + .../t/t_interface_generic_task_bad.py | 16 ++ test_regress/t/t_interface_generic_task_bad.v | 30 ++++ test_regress/t/t_interface_param2.out | 8 - test_regress/t/t_interface_param2.py | 8 +- test_regress/t/t_interface_param2.v | 3 +- test_regress/t/t_json_only_begin_hier.out | 8 +- test_regress/t/t_json_only_debugcheck.out | 4 +- test_regress/t/t_json_only_first.out | 8 +- test_regress/t/t_json_only_flat.out | 4 +- .../t/t_json_only_flat_no_inline_mod.out | 4 +- test_regress/t/t_json_only_flat_pub_mod.out | 4 +- test_regress/t/t_json_only_flat_vlvbound.out | 4 +- test_regress/t/t_json_only_output.out | 4 +- test_regress/t/t_json_only_tag.out | 4 +- test_regress/t/t_var_port_json_only.out | 32 ++-- 94 files changed, 1813 insertions(+), 68 deletions(-) create mode 100755 test_regress/t/t_interface_generic.py create mode 100644 test_regress/t/t_interface_generic.v create mode 100755 test_regress/t/t_interface_generic2.py create mode 100644 test_regress/t/t_interface_generic2.v create mode 100755 test_regress/t/t_interface_generic_array.py create mode 100644 test_regress/t/t_interface_generic_array.v create mode 100755 test_regress/t/t_interface_generic_bad.out create mode 100755 test_regress/t/t_interface_generic_bad.py create mode 100644 test_regress/t/t_interface_generic_bad.v create mode 100755 test_regress/t/t_interface_generic_bad2.out create mode 100755 test_regress/t/t_interface_generic_bad2.py create mode 100644 test_regress/t/t_interface_generic_bad2.v create mode 100644 test_regress/t/t_interface_generic_bad3.out create mode 100755 test_regress/t/t_interface_generic_bad3.py create mode 100644 test_regress/t/t_interface_generic_bad3.v create mode 100644 test_regress/t/t_interface_generic_bad4.out create mode 100755 test_regress/t/t_interface_generic_bad4.py create mode 100644 test_regress/t/t_interface_generic_bad4.v create mode 100755 test_regress/t/t_interface_generic_function.py create mode 100644 test_regress/t/t_interface_generic_function.v create mode 100755 test_regress/t/t_interface_generic_function_bad.out create mode 100755 test_regress/t/t_interface_generic_function_bad.py create mode 100644 test_regress/t/t_interface_generic_function_bad.v create mode 100755 test_regress/t/t_interface_generic_iface_param.py create mode 100644 test_regress/t/t_interface_generic_iface_param.v create mode 100755 test_regress/t/t_interface_generic_mod_param.py create mode 100644 test_regress/t/t_interface_generic_mod_param.v create mode 100755 test_regress/t/t_interface_generic_modport.py create mode 100644 test_regress/t/t_interface_generic_modport.v create mode 100755 test_regress/t/t_interface_generic_modport2.py create mode 100644 test_regress/t/t_interface_generic_modport2.v create mode 100755 test_regress/t/t_interface_generic_modport_bad.out create mode 100755 test_regress/t/t_interface_generic_modport_bad.py create mode 100644 test_regress/t/t_interface_generic_modport_bad.v create mode 100755 test_regress/t/t_interface_generic_modport_bad2.out create mode 100755 test_regress/t/t_interface_generic_modport_bad2.py create mode 100644 test_regress/t/t_interface_generic_modport_bad2.v create mode 100755 test_regress/t/t_interface_generic_modport_bad3.out create mode 100755 test_regress/t/t_interface_generic_modport_bad3.py create mode 100644 test_regress/t/t_interface_generic_modport_bad3.v create mode 100755 test_regress/t/t_interface_generic_modport_function.py create mode 100644 test_regress/t/t_interface_generic_modport_function.v create mode 100755 test_regress/t/t_interface_generic_modport_function2.py create mode 100644 test_regress/t/t_interface_generic_modport_function2.v create mode 100755 test_regress/t/t_interface_generic_modport_function_bad.out create mode 100755 test_regress/t/t_interface_generic_modport_function_bad.py create mode 100644 test_regress/t/t_interface_generic_modport_function_bad.v create mode 100755 test_regress/t/t_interface_generic_modport_param.py create mode 100644 test_regress/t/t_interface_generic_modport_param.v create mode 100755 test_regress/t/t_interface_generic_modport_task.py create mode 100644 test_regress/t/t_interface_generic_modport_task.v create mode 100755 test_regress/t/t_interface_generic_modport_task2.py create mode 100644 test_regress/t/t_interface_generic_modport_task2.v create mode 100755 test_regress/t/t_interface_generic_modport_task_bad.out create mode 100755 test_regress/t/t_interface_generic_modport_task_bad.py create mode 100644 test_regress/t/t_interface_generic_modport_task_bad.v create mode 100755 test_regress/t/t_interface_generic_normal.py create mode 100644 test_regress/t/t_interface_generic_normal.v create mode 100755 test_regress/t/t_interface_generic_positional.py create mode 100644 test_regress/t/t_interface_generic_positional.v create mode 100755 test_regress/t/t_interface_generic_submod_param.py create mode 100644 test_regress/t/t_interface_generic_submod_param.v create mode 100755 test_regress/t/t_interface_generic_task.py create mode 100644 test_regress/t/t_interface_generic_task.v create mode 100755 test_regress/t/t_interface_generic_task2.py create mode 100644 test_regress/t/t_interface_generic_task2.v create mode 100755 test_regress/t/t_interface_generic_task_bad.out create mode 100755 test_regress/t/t_interface_generic_task_bad.py create mode 100644 test_regress/t/t_interface_generic_task_bad.v delete mode 100644 test_regress/t/t_interface_param2.out diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index c202c2500..607f42591 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -73,6 +73,7 @@ bool VFwdType::isNodeCompatible(const AstNode* nodep) const { case VFwdType::UNION: return VN_IS(defp, UnionDType); break; case VFwdType::INTERFACE_CLASS: // FALLTHRU // TODO: Over permissive for now case VFwdType::CLASS: return VN_IS(defp, ClassRefDType) || VN_IS(defp, Class); break; + case VFwdType::GENERIC_INTERFACE: return VN_IS(defp, IfaceRefDType); break; default: v3fatalSrc("Bad case"); } VL_UNREACHABLE; diff --git a/src/V3Ast.h b/src/V3Ast.h index 7e61686ef..1cf11dcf1 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -288,11 +288,11 @@ public: class VFwdType final { public: - enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS }; + enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS, GENERIC_INTERFACE }; enum en m_e; const char* ascii() const { static const char* const names[] - = {"none", "enum", "struct", "union", "class", "interface class"}; + = {"none", "enum", "struct", "union", "class", "interface class", "generic interface"}; return names[m_e]; } VFwdType() diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 861a1254d..f7379bf61 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -845,6 +845,40 @@ public: const TableMap& tableMap() const { return m_tableMap; } }; +class AstIfaceGenericDType final : public AstNodeDType { + // Generic interface that will be replaced with AstIfaceRefDType + FileLine* m_modportFileline; // Where modport token was + string m_modportName; // "" = no modport +public: + explicit AstIfaceGenericDType(FileLine* fl) + : ASTGEN_SUPER_IfaceGenericDType(fl) { + dtypep(this); + } + AstIfaceGenericDType(FileLine* fl, FileLine* modportFl, const string& modport) + : ASTGEN_SUPER_IfaceGenericDType(fl) + , m_modportFileline{modportFl} + , m_modportName{modport} { + dtypep(this); + } + ASTGEN_MEMBERS_AstIfaceGenericDType; + void dumpSmall(std::ostream& str) const override; + bool hasDType() const override VL_MT_SAFE { return true; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + bool undead() const override { return true; } + AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* virtRefDTypep() const override { return nullptr; } + void virtRefDTypep(AstNodeDType* nodep) override {} + bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + int widthAlignBytes() const override { return 1; } + int widthTotalBytes() const override { return 1; } + string modportName() const { return m_modportName; } + bool isModport() { return !m_modportName.empty(); } + bool isCompound() const override { return true; } + FileLine* modportFileline() const { return m_modportFileline; } + string name() const override { return m_modportName; } +}; + class AstIfaceRefDType final : public AstNodeDType { // Reference to an interface, either for a port, or inside parent cell // @astgen op1 := paramsp : List[AstPin] @@ -898,11 +932,13 @@ public: if (flag) v3Global.setHasVirtIfaces(); } FileLine* modportFileline() const { return m_modportFileline; } + void modportFileline(FileLine* const modportFileline) { m_modportFileline = modportFileline; } string cellName() const { return m_cellName; } void cellName(const string& name) { m_cellName = name; } string ifaceName() const { return m_ifaceName; } string ifaceNameQ() const { return "'" + prettyName(ifaceName()) + "'"; } void ifaceName(const string& name) { m_ifaceName = name; } + void modportName(const string& modportName) { m_modportName = modportName; } string modportName() const { return m_modportName; } AstIface* ifaceViaCellp() const; // Use cellp or ifacep AstIface* ifacep() const { return m_ifacep; } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index b69061703..d362f49e4 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2545,6 +2545,8 @@ class AstModule final : public AstNodeModule { // A module declaration const bool m_isChecker; // Module represents a checker const bool m_isProgram; // Module represents a program + bool m_hasGenericIface = false; // Module contains a generic interface + public: class Checker {}; // for constructor type-overload selection class Program {}; // for constructor type-overload selection @@ -2567,6 +2569,8 @@ public: bool timescaleMatters() const override { return true; } bool isChecker() const { return m_isChecker; } bool isProgram() const { return m_isProgram; } + bool hasGenericIface() const { return m_hasGenericIface; } + void hasGenericIface(bool hasGenericIface) { m_hasGenericIface = hasGenericIface; } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; }; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 014b1f155..920b5bd75 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2105,10 +2105,12 @@ void AstModule::dump(std::ostream& str) const { this->AstNodeModule::dump(str); if (isChecker()) str << " [CHECKER]"; if (isProgram()) str << " [PROGRAM]"; + if (hasGenericIface()) str << " [HASGENERICIFACE]"; } void AstModule::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, isChecker); dumpJsonBoolFunc(str, isProgram); + dumpJsonBoolFunc(str, hasGenericIface); dumpJsonGen(str); } void AstPin::dump(std::ostream& str) const { @@ -2557,6 +2559,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "void"; } +void AstIfaceGenericDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "generic_interface"; +} void AstStreamDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "stream"; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index e6987da00..00328fb4f 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2415,6 +2415,7 @@ class LinkDotResolveVisitor final : public VNVisitor { int m_indent = 0; // Indentation (tree depth) for debug bool m_inSens = false; // True if in senitem bool m_inWith = false; // True if in with + bool m_genericIfaceModule = false; // True if in module containing generic interface std::map m_ifClassImpNames; // Names imported from interface class std::set m_extendsParam; // Classes that have a parameterized super class // (except the default instances) @@ -2434,6 +2435,7 @@ class LinkDotResolveVisitor final : public VNVisitor { bool m_super; // Starts with super reference bool m_unresolvedCell; // Unresolved cell, needs help from V3Param bool m_unresolvedClass; // Unresolved class reference, needs help from V3Param + bool m_unresolvedGenericIface; // Unresolved generic interface, needs help from V3Param bool m_genBlk; // Contains gen block reference AstNode* m_unlinkedScopep; // Unresolved scope, needs corresponding VarXRef AstDisable* m_disablep; // Disable statement under which the reference is @@ -2450,6 +2452,7 @@ class LinkDotResolveVisitor final : public VNVisitor { m_dotText = ""; m_unresolvedCell = false; m_unresolvedClass = false; + m_unresolvedGenericIface = false; m_genBlk = false; m_unlinkedScopep = nullptr; m_disablep = nullptr; @@ -2467,6 +2470,7 @@ class LinkDotResolveVisitor final : public VNVisitor { if (m_super) sstr << " [super]"; if (m_unresolvedCell) sstr << " [unrCell]"; if (m_unresolvedClass) sstr << " [unrClass]"; + if (m_unresolvedGenericIface) sstr << " [unrGIface]"; if (m_genBlk) sstr << " [genBlk]"; sstr << " txt=" << m_dotText; return sstr.str(); @@ -2745,6 +2749,9 @@ class LinkDotResolveVisitor final : public VNVisitor { dtypep = adtypep->childDTypep()->skipRefp(); } else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) { dtypep = adtypep->childDTypep()->skipRefp(); + } else if (const AstUnpackArrayDType* const adtypep + = VN_CAST(dtypep, UnpackArrayDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); } else { break; } @@ -2776,6 +2783,81 @@ class LinkDotResolveVisitor final : public VNVisitor { } return nullptr; } + static const AstVar* getNextVarp(const AstNode* stmtsp) { + while (stmtsp) { + if (const AstVar* const varp = VN_CAST(stmtsp, Var)) return varp; + stmtsp = stmtsp->nextp(); + } + return nullptr; + } + // Introduce implicit parameters for modules with generic interafeces + void addImplicitParametersOfGenericIface(AstCell* const nodep, const AstModule* const modp) { + // Get Param number + int paramNum = 1; + for (AstPin* paramp = nodep->paramsp(); paramp; paramp = VN_CAST(paramp->nextp(), Pin)) { + ++paramNum; + } + + // Add each implicit parameter type + const AstVar* modIfaceVarp = getNextVarp(modp->stmtsp()); + for (const AstPin* pinp = nodep->pinsp(); pinp && modIfaceVarp; + pinp = VN_CAST(pinp->nextp(), Pin), + modIfaceVarp = getNextVarp(modIfaceVarp->nextp())) { + if (modIfaceVarp->varType() != VVarType::IFACEREF + || !VN_IS(modIfaceVarp->childDTypep(), IfaceGenericDType)) { + continue; + } + AstNode* exprp = pinp->exprp(); + if (!exprp) { + modIfaceVarp->v3error("Interface port " + << modIfaceVarp->prettyNameQ() + << " is not connected to interface/modport pin expression"); + continue; + } + while (const AstNodePreSel* const preSelp = VN_CAST(exprp, NodePreSel)) { + exprp = preSelp->fromp(); + } + if (const AstVarRef* const varRefp = VN_CAST(exprp, VarRef)) { + const AstVar* const varp = varRefp->varp(); + if (const AstIfaceRefDType* const refp + = VN_CAST(getElemDTypep(varp->childDTypep()), IfaceRefDType)) { + AstIface* const ifacep = VN_AS(refp->cellp()->modp(), Iface); + AstIfaceRefDType* newIfaceRefp; + if (refp->modportp()) { + newIfaceRefp = new AstIfaceRefDType{ + refp->fileline(), refp->modportFileline(), m_modp->name(), + ifacep->name(), refp->modportName()}; + } else { + newIfaceRefp = new AstIfaceRefDType{refp->fileline(), m_modp->name(), + ifacep->name()}; + } + newIfaceRefp->ifacep(ifacep); + if (refp->cellp()->paramsp()) { + newIfaceRefp->addParamsp(refp->cellp()->paramsp()->cloneTree(true)); + } + UASSERT_OBJ(pinp->name().find("__pinNumber") == 0 + || pinp->name() == modIfaceVarp->name(), + pinp, "Not found interface with such name"); + AstPin* const newPinp + = new AstPin{pinp->fileline(), paramNum, + "__VGIfaceParam" + modIfaceVarp->name(), newIfaceRefp}; + newPinp->param(true); + visit(newPinp); + nodep->addParamsp(newPinp); + } else { + varRefp->v3error("Generic interfaces can only connect to an interface and " + << varp->prettyNameQ() << " is " + << (varp->childDTypep() + ? "of type " + varp->childDTypep()->prettyDTypeNameQ() + : "not an interface")); + } + } else { + exprp->v3error("Expected an interface but " << exprp->prettyNameQ() + << " is not an interface"); + } + ++paramNum; + } + } #define LINKDOT_VISIT_START() \ VL_RESTORER(m_indent); \ @@ -2798,6 +2880,10 @@ class LinkDotResolveVisitor final : public VNVisitor { if (nodep->dead() || !m_statep->existsNodeSym(nodep)) return; LINKDOT_VISIT_START(); UINFO(8, indent() << "visit " << nodep); + VL_RESTORER(m_genericIfaceModule); + if (const AstModule* const modp = VN_CAST(nodep, Module)) { + m_genericIfaceModule = modp->hasGenericIface(); + } checkNoDot(nodep); m_ds.init(m_curSymp); m_ds.m_dotSymp = m_curSymp = m_modSymp @@ -2854,6 +2940,12 @@ class LinkDotResolveVisitor final : public VNVisitor { // UINFOTREE(1, nodep, "", "linkcell"); // UINFOTREE(1, nodep->modp(), "", "linkcemd"); iterateChildren(nodep); + + if (m_statep->forPrimary()) + if (const AstModule* const modp = VN_CAST(nodep->modp(), Module)) { + if (modp->hasGenericIface()) + addImplicitParametersOfGenericIface(nodep, modp); + } } } // Parent module inherits child's publicity @@ -2954,6 +3046,7 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(8, indent() << m_ds.ascii()); const DotStates lastStates = m_ds; const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed + const DotPosition initialDotPos = m_ds.m_dotPos; VL_RESTORER(m_randSymp); { if (start) { // Starting dot sequence @@ -3053,9 +3146,14 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(8, indent() << "iter.ldone " << m_ds.ascii() << " " << nodep); } else { m_ds.m_dotPos = DP_FIRST; + m_ds.m_unresolvedGenericIface = false; UINFO(8, indent() << "iter.lhs " << m_ds.ascii() << " " << nodep); iterateAndNextNull(nodep->lhsp()); UINFO(8, indent() << "iter.ldone " << m_ds.ascii() << " " << nodep); + if (m_ds.m_unresolvedGenericIface) { + m_ds.m_dotPos = initialDotPos; + return; + } // UINFOTREE(9, nodep, "", "dot-lho"); } if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) { @@ -3383,7 +3481,11 @@ class LinkDotResolveVisitor final : public VNVisitor { } else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) { AstIfaceRefDType* const ifacerefp = LinkDotState::ifaceRefFromArray(varp->subDTypep()); - if (ifacerefp && varp->isIfaceRef()) { + if (varp->isIfaceRef() && m_genericIfaceModule + && VN_IS(varp->childDTypep(), IfaceGenericDType)) { + ok = true; + m_ds.m_unresolvedGenericIface = true; + } else if (ifacerefp && varp->isIfaceRef()) { UASSERT_OBJ(ifacerefp->ifaceViaCellp(), ifacerefp, "Unlinked interface"); // Really this is a scope reference into an interface UINFO(9, indent() << "varref-ifaceref " << m_ds.m_dotText << " " << nodep); @@ -3787,8 +3889,44 @@ class LinkDotResolveVisitor final : public VNVisitor { } dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot, okSymp, true); // Maybe nullptr + bool modport = false; + if (const AstVar* varp = VN_CAST(dotSymp->nodep(), Var)) { + if (const AstIfaceRefDType* const ifaceRefp + = VN_CAST(varp->childDTypep(), IfaceRefDType)) { + if (ifaceRefp->modportp()) { + dotSymp = m_statep->getNodeSym(ifaceRefp->modportp()); + modport = true; + } else { + dotSymp = m_statep->getNodeSym(ifaceRefp->ifacep()); + } + } + } if (!m_statep->forScopeCreation()) { - VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true); + VSymEnt* foundp = nullptr; + if (modport) { + // This is a copy of findSymPrefixed with few modifications. + // This searches without a fallback like findSymPrefixed but if lookup fails it + // will check one fallback (fallback of modport shall be interface) and take + // only L and G PARAMs since they should be visible from a modport always. + string prefix = dotSymp->symPrefix(); + while (!foundp) { + foundp = dotSymp->findIdFlat(prefix + nodep->name()); + if (foundp) break; + UASSERT_OBJ(dotSymp->fallbackp(), nodep, "Modports shall have fallback"); + foundp = dotSymp->fallbackp()->findIdFlat(prefix + nodep->name()); + if (const AstVar* const varp + = foundToVarp(foundp, nodep, nodep->access())) { + if (!varp->isParam()) foundp = nullptr; + } + if (prefix.empty()) break; + string nextPrefix = LinkDotState::removeLastInlineScope(prefix); + if (prefix == nextPrefix) break; + prefix = std::move(nextPrefix); + } + baddot = nodep->name(); + } else { + foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true); + } if (m_inSens && foundp) { if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) { foundp = getCreateClockingEventSymEnt(clockingp); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 0e3901af1..17f7a4137 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -64,6 +64,7 @@ class LinkParseVisitor final : public VNVisitor { bool m_insideLoop = false; // True if the node is inside a loop bool m_lifetimeAllowed = false; // True to allow lifetime settings VDouble0 m_statModules; // Number of modules seen + bool m_moduleWithGenericIface = false; // If current module contains generic interface // METHODS void cleanFileline(AstNode* nodep) { @@ -345,6 +346,7 @@ class LinkParseVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); return; } + m_moduleWithGenericIface |= VN_IS(nodep->childDTypep(), IfaceGenericDType); // Maybe this variable has a signal attribute V3Control::applyVarAttr(m_modp, m_ftaskp, nodep); @@ -634,6 +636,7 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_implTypedef); VL_RESTORER(m_lifetime); VL_RESTORER(m_lifetimeAllowed); + VL_RESTORER(m_moduleWithGenericIface); { // Module: Create sim table for entire module and iterate cleanFileline(nodep); @@ -648,6 +651,7 @@ class LinkParseVisitor final : public VNVisitor { m_valueModp = nodep; m_lifetime = nodep->lifetime(); m_lifetimeAllowed = VN_IS(nodep, Class); + m_moduleWithGenericIface = false; if (m_lifetime.isNone()) { m_lifetime = VN_IS(nodep, Class) ? VLifetime::AUTOMATIC : VLifetime::STATIC; } @@ -657,6 +661,9 @@ class LinkParseVisitor final : public VNVisitor { "Verilator top-level internals"); } iterateChildren(nodep); + if (AstModule* const modp = VN_CAST(nodep, Module)) { + modp->hasGenericIface(m_moduleWithGenericIface); + } } m_valueModp = nodep; } diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 55ae606e1..527972e8d 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -818,6 +818,7 @@ class ParamProcessor final { bool& any_overridesr, IfaceRefRefs& ifaceRefRefs) { for (AstPin* pinp = pinsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { const AstVar* const modvarp = pinp->modVarp(); + if (modvarp && VN_IS(modvarp->subDTypep(), IfaceGenericDType)) continue; if (modvarp->isIfaceRef()) { AstIfaceRefDType* portIrefp = VN_CAST(modvarp->subDTypep(), IfaceRefDType); if (!portIrefp && arraySubDTypep(modvarp->subDTypep())) { @@ -888,6 +889,46 @@ class ParamProcessor final { } } + // Set interfaces types inside generic modules + // to the corresponding values of implicit parameters + void genericInterfaceVarSetup(const AstPin* const paramsp, const AstPin* const pinsp) { + std::unordered_map paramspMap; + for (const AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (VString::startsWith(pinp->name(), "__VGIfaceParam")) { + paramspMap.insert({pinp->name().substr(std::strlen("__VGIfaceParam")), pinp}); + } + } + + if (paramspMap.empty()) return; + + for (const AstNode* nodep = pinsp; nodep; nodep = nodep->nextp()) { + if (const AstPin* const pinp = VN_CAST(nodep, Pin)) { + if (AstVar* const varp = pinp->modVarp()) { + if (AstIfaceGenericDType* const ifaceGDTypep + = VN_CAST(varp->childDTypep(), IfaceGenericDType)) { + const auto iter = paramspMap.find(varp->name()); + if (iter == paramspMap.end()) continue; + ifaceGDTypep->unlinkFrBack(); + const AstPin* const paramp = iter->second; + paramspMap.erase(iter); + const AstIfaceRefDType* const ifacerefp + = VN_AS(paramp->exprp(), IfaceRefDType); + AstIfaceRefDType* const newIfacerefp = new AstIfaceRefDType{ + ifaceGDTypep->fileline(), ifaceGDTypep->modportFileline(), + ifaceGDTypep->name(), ifacerefp->ifaceName(), + ifaceGDTypep->modportName()}; + newIfacerefp->ifacep(ifacerefp->ifacep()); + varp->childDTypep(newIfacerefp); + VL_DO_DANGLING(m_deleter.pushDeletep(ifaceGDTypep), ifaceGDTypep); + if (paramspMap.empty()) return; + } + } + } + } + + UASSERT(paramspMap.empty(), "Not every generic interface implicit param is used"); + } + bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp, AstPin* pinsp, bool any_overrides) { // Make sure constification worked @@ -946,7 +987,7 @@ class ParamProcessor final { nodep->v3error( "Class parameter type without default value is never given value" << " (IEEE 1800-2023 6.20.1): " << dtypep->prettyNameQ()); - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + VL_DO_DANGLING(m_deleter.pushDeletep(nodep->unlinkFrBack()), nodep); } } if (AstVar* const varp = VN_CAST(stmtp, Var)) { @@ -957,6 +998,8 @@ class ParamProcessor final { } } + genericInterfaceVarSetup(paramsp, pinsp); + // Delete the parameters from the cell; they're not relevant any longer. if (paramsp) paramsp->unlinkFrBackWithNext()->deleteTree(); return any_overrides; diff --git a/src/verilog.y b/src/verilog.y index ccb9c1b20..b34af6ab9 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -295,6 +295,27 @@ public: forkp->addInitsp(varp); } } + + void createGenericIface(AstNode* const nodep, AstNodeRange* const rangep, + AstNode* sigAttrListp, FileLine* const modportFileline = nullptr, + const string& modportstrp = "") { + m_varDecl = VVarType::GPARAM; + m_varIO = VDirection::NONE; + setDType(new AstParseTypeDType{nodep->fileline(), VFwdType::GENERIC_INTERFACE}); + m_varDeclTyped = true; + const std::string uniqueName = "__VGIfaceParam" + nodep->name(); + AstNode::addNext(nodep, + createVariable(nodep->fileline(), uniqueName, rangep, sigAttrListp)); + m_varDecl = VVarType::IFACEREF; + AstIfaceGenericDType* const refdtypep + = new AstIfaceGenericDType{nodep->fileline(), modportFileline, modportstrp}; + setDType(refdtypep); + m_varDeclTyped = true; + m_varIO = VDirection::INPUT; + AstNode::addNext(nodep, + createVariable(nodep->fileline(), nodep->name(), rangep, sigAttrListp)); + m_varDecl = VVarType::VAR; + } }; const VBasicDTypeKwd LOGIC = VBasicDTypeKwd::LOGIC; // Shorthand "LOGIC" @@ -1561,9 +1582,9 @@ port: // ==IEEE: port VARDTYPE(dtp); VARIOANSI(); addNextNull($$, VARDONEP($$, $6, $7)); } | portDirNetE yINTERFACE portSig rangeListE sigAttrListE - { $$ = nullptr; BBUNSUP($2, "Unsupported: generic interfaces"); } + { $$ = $3; GRAMMARP->createGenericIface($3, $4, $5); } | portDirNetE yINTERFACE '.' idAny/*modport*/ portSig rangeListE sigAttrListE - { $$ = nullptr; BBUNSUP($2, "Unsupported: generic interfaces"); } + { $$ = $5; GRAMMARP->createGenericIface($5, $6, $7, $4, *$4); } // | portDirNetE yINTERCONNECT signingE rangeListE portSig variable_dimensionListE sigAttrListE { $$ = $5; diff --git a/test_regress/t/t_class_param_noinit_bad.out b/test_regress/t/t_class_param_noinit_bad.out index 1980a618e..6de7724b1 100644 --- a/test_regress/t/t_class_param_noinit_bad.out +++ b/test_regress/t/t_class_param_noinit_bad.out @@ -7,5 +7,8 @@ : ... note: In instance 't' 13 | Cls #(1) c; | ^~~ -%Error: Verilator internal fault, sorry. Suggest trying --debug --gdbbt -%Error: Command Failed +%Error: t/t_class_param_noinit_bad.v:8:32: Parameter type without default value is never given value (IEEE 1800-2023 6.20.1): 'T' + : ... note: In instance 't' + 8 | class Cls #(int A, int B, type T); + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_json_only.out b/test_regress/t/t_constraint_json_only.out index 0217c68b2..62f316418 100644 --- a/test_regress/t/t_constraint_json_only.out +++ b/test_regress/t/t_constraint_json_only.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"(E)","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"t","addr":"(F)","loc":"d,67:8,67:9","isChecker":false,"isProgram":false,"origName":"t","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"t","addr":"(F)","loc":"d,67:8,67:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"t","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"p","addr":"(G)","loc":"d,69:11,69:12","dtypep":"(H)","origName":"p","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"Packet","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"INITIAL","name":"","addr":"(I)","loc":"d,71:4,71:11","isSuspendable":false,"needProcess":false, @@ -77,7 +77,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(SB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(SB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(TB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(SB)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 63a123363..954284773 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"(E)","stdPackagep":"(F)","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"t","addr":"(G)","loc":"e,7:8,7:9","isChecker":false,"isProgram":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"t","addr":"(G)","loc":"e,7:8,7:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"PORT","name":"clk","addr":"(H)","loc":"e,9:4,9:7","exprp": []}, {"type":"VAR","name":"clk","addr":"(I)","loc":"e,11:10,11:13","dtypep":"UNLINKED","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"NONE","varType":"PORT","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", @@ -431,7 +431,7 @@ ]} ]} ]}, - {"type":"MODULE","name":"Test","addr":"(RB)","loc":"e,66:8,66:12","isChecker":false,"isProgram":false,"origName":"Test","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"Test","addr":"(RB)","loc":"e,66:8,66:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"Test","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"PORT","name":"out","addr":"(AH)","loc":"e,68:4,68:7","exprp": []}, {"type":"PORT","name":"clk","addr":"(BH)","loc":"e,70:4,70:7","exprp": []}, @@ -1082,7 +1082,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(VQ)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(VQ)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(WQ)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(VQ)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_interface_generic.py b/test_regress/t/t_interface_generic.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic.v b/test_regress/t/t_interface_generic.v new file mode 100644 index 000000000..729beebcd --- /dev/null +++ b/test_regress/t/t_interface_generic.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a, interface b); + initial begin + #1; + if (a.v != 7) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + inf inf_inst(); + inf2 inf_inst2(); + GenericModule genericModule (inf_inst, inf_inst2); + initial begin + inf_inst.v = 7; + inf_inst2.k = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic2.py b/test_regress/t/t_interface_generic2.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic2.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic2.v b/test_regress/t/t_interface_generic2.v new file mode 100644 index 000000000..ee3ad0997 --- /dev/null +++ b/test_regress/t/t_interface_generic2.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (logic[31:0] l1, interface a, logic[31:0] l2, interface b); + initial begin + #1; + if (l1 != 87) $stop; + if (a.v != 7) $stop; + if (l2 != 73) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + inf inf_inst(); + inf2 inf_inst2(); + GenericModule genericModule (87, inf_inst, 73, inf_inst2); + initial begin + inf_inst.v = 7; + inf_inst2.k = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_array.py b/test_regress/t/t_interface_generic_array.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_array.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_array.v b/test_regress/t/t_interface_generic_array.v new file mode 100644 index 000000000..cce0e00fd --- /dev/null +++ b/test_regress/t/t_interface_generic_array.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst[3](); + GenericModule genericModule (inf_inst[2]); + initial begin + inf_inst[2].v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_bad.out b/test_regress/t/t_interface_generic_bad.out new file mode 100755 index 000000000..d4be383fc --- /dev/null +++ b/test_regress/t/t_interface_generic_bad.out @@ -0,0 +1,13 @@ +%Warning-PINMISSING: t/t_interface_generic_bad.v:26:17: Instance has missing pin: 'b' + 26 | GenericModule genericModule (inf_inst); + | ^~~~~~~~~~~~~ + t/t_interface_generic_bad.v:15:46: ... Location of port declaration + 15 | module GenericModule (interface a, interface b); + | ^ + ... For warning description see https://verilator.org/warn/PINMISSING?v=latest + ... Use "/* verilator lint_off PINMISSING */" and lint_on around source to disable this message. +%Error: t/t_interface_generic_bad.v:15:46: Interface port 'b' is not connected to interface/modport pin expression + 15 | module GenericModule (interface a, interface b); + | ^ + ... 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_interface_generic_bad.py b/test_regress/t/t_interface_generic_bad.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_bad.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_bad.v b/test_regress/t/t_interface_generic_bad.v new file mode 100644 index 000000000..d9d9a49be --- /dev/null +++ b/test_regress/t/t_interface_generic_bad.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a, interface b); + initial begin + #1; + if (a.v != 7) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + inf inf_inst(); + inf2 inf_inst2(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + inf_inst2.k = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_bad2.out b/test_regress/t/t_interface_generic_bad2.out new file mode 100755 index 000000000..7a148a4a5 --- /dev/null +++ b/test_regress/t/t_interface_generic_bad2.out @@ -0,0 +1,24 @@ +%Error: t/t_interface_generic_bad2.v:15:9: Can't find definition of scope/variable: 'b' + 15 | if (b.k != 9) $stop; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_interface_generic_bad2.v:20:3: Cannot find file containing interface: 'inf' + 20 | inf inf_inst(); + | ^~~ +%Error: t/t_interface_generic_bad2.v:21:32: Found definition of 'inf_inst' as a CELL but expected a variable + 21 | GenericModule genericModule (inf_inst); + | ^~~~~~~~ +%Error: t/t_interface_generic_bad2.v:21:32: Expected an interface but 'inf_inst' is not an interface + 21 | GenericModule genericModule (inf_inst); + | ^~~~~~~~ +%Error: t/t_interface_generic_bad2.v:23:5: Dotted reference to instance that refers to missing module/interface: 'inf' + 23 | inf_inst.v = 7; + | ^~~~~~~~ +%Error: t/t_interface_generic_bad2.v:23:14: Can't find definition of 'v' in dotted variable/method: 'inf_inst.v' + 23 | inf_inst.v = 7; + | ^ +%Error: t/t_interface_generic_bad2.v:24:5: Can't find definition of scope/variable: 'inf_inst2' + : ... Suggested alternative: 'inf_inst' + 24 | inf_inst2.k = 9; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_interface_generic_bad2.py b/test_regress/t/t_interface_generic_bad2.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_bad2.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_bad2.v b/test_regress/t/t_interface_generic_bad2.v new file mode 100644 index 000000000..aefcecb1d --- /dev/null +++ b/test_regress/t/t_interface_generic_bad2.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class inf; + int v; +endclass + +module GenericModule (interface a); + initial begin + #1; + if (a.v != 7) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + inf_inst2.k = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_bad3.out b/test_regress/t/t_interface_generic_bad3.out new file mode 100644 index 000000000..1d6b83f9a --- /dev/null +++ b/test_regress/t/t_interface_generic_bad3.out @@ -0,0 +1,9 @@ +%Error: t/t_interface_generic_bad3.v:15:9: Can't find definition of scope/variable: 'b' + 15 | if (b.k != 9) $stop; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-PINNOTFOUND: t/t_interface_generic_bad3.v:21:42: Pin not found: '__pinNumber2' + 21 | GenericModule genericModule (inf_inst, inf_inst); + | ^~~~~~~~ + ... For error description see https://verilator.org/warn/PINNOTFOUND?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_interface_generic_bad3.py b/test_regress/t/t_interface_generic_bad3.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_bad3.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_bad3.v b/test_regress/t/t_interface_generic_bad3.v new file mode 100644 index 000000000..98e7e755c --- /dev/null +++ b/test_regress/t/t_interface_generic_bad3.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.v != 7) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst, inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_bad4.out b/test_regress/t/t_interface_generic_bad4.out new file mode 100644 index 000000000..11266ed5f --- /dev/null +++ b/test_regress/t/t_interface_generic_bad4.out @@ -0,0 +1,5 @@ +%Error: t/t_interface_generic_bad4.v:26:32: Generic interfaces can only connect to an interface and 'inf_inst' is of type 'int' + 26 | GenericModule genericModule (inf_inst, inf_inst2); + | ^~~~~~~~ + ... 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_interface_generic_bad4.py b/test_regress/t/t_interface_generic_bad4.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_bad4.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_bad4.v b/test_regress/t/t_interface_generic_bad4.v new file mode 100644 index 000000000..392357c9c --- /dev/null +++ b/test_regress/t/t_interface_generic_bad4.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a, interface b); + initial begin + #1; + if (a.v != 7) $stop; + if (b.k != 9) $stop; + end +endmodule + +module t; + int inf_inst; + inf2 inf_inst2(); + GenericModule genericModule (inf_inst, inf_inst2); + initial begin + inf_inst.v = 7; + inf_inst2.k = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_function.py b/test_regress/t/t_interface_generic_function.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_function.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_function.v b/test_regress/t/t_interface_generic_function.v new file mode 100644 index 000000000..4f52378a3 --- /dev/null +++ b/test_regress/t/t_interface_generic_function.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + function int get(); + return v; + endfunction +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.get() != 4) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 4; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_function_bad.out b/test_regress/t/t_interface_generic_function_bad.out new file mode 100755 index 000000000..b4f1e6ee3 --- /dev/null +++ b/test_regress/t/t_interface_generic_function_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_function_bad.v:14:11: Can't find definition of 'get' in dotted task/function: 'a.get' + : ... note: In instance 't.genericModule' + 14 | if (a.get() != 4) $stop; + | ^~~ + ... Known scopes under 'get': + ... 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_interface_generic_function_bad.py b/test_regress/t/t_interface_generic_function_bad.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_function_bad.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_function_bad.v b/test_regress/t/t_interface_generic_function_bad.v new file mode 100644 index 000000000..63bbc8b25 --- /dev/null +++ b/test_regress/t/t_interface_generic_function_bad.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.get() != 4) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 4; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param.py b/test_regress/t/t_interface_generic_iface_param.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param.v b/test_regress/t/t_interface_generic_iface_param.v new file mode 100644 index 000000000..6bdbe0022 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf #(PARAM); + logic[PARAM-1:0] v; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.v != 7) $stop; + if (a.PARAM != 13) $stop; + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_mod_param.py b/test_regress/t/t_interface_generic_mod_param.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_mod_param.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_mod_param.v b/test_regress/t/t_interface_generic_mod_param.v new file mode 100644 index 000000000..b41481c7a --- /dev/null +++ b/test_regress/t/t_interface_generic_mod_param.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule#(type T, type Y = int) (interface a); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule #(string) genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport.py b/test_regress/t/t_interface_generic_modport.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_modport.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport.v b/test_regress/t/t_interface_generic_modport.v new file mode 100644 index 000000000..d4e58dbd8 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + modport mp ( + input v + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport2.py b/test_regress/t/t_interface_generic_modport2.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport2.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport2.v b/test_regress/t/t_interface_generic_modport2.v new file mode 100644 index 000000000..59a32c0a2 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport2.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + modport mp ( + output v + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + a.v = 7; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if (inf_inst.v != 7) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_bad.out b/test_regress/t/t_interface_generic_modport_bad.out new file mode 100755 index 000000000..c6b0a02ea --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_interface_generic_modport_bad.v:11:33: Modport not found under interface 'inf': 'mp' + 11 | module GenericModule (interface.mp a); + | ^~ + ... 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_interface_generic_modport_bad.py b/test_regress/t/t_interface_generic_modport_bad.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_bad.v b/test_regress/t/t_interface_generic_modport_bad.v new file mode 100644 index 000000000..3fe1212a5 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_bad2.out b/test_regress/t/t_interface_generic_modport_bad2.out new file mode 100755 index 000000000..a7364efb9 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad2.out @@ -0,0 +1,6 @@ +%Error: t/t_interface_generic_modport_bad2.v:17:7: Attempt to drive input-only modport: 'v' + : ... note: In instance 't.genericModule' + 17 | a.v = 10; + | ^ + ... 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_interface_generic_modport_bad2.py b/test_regress/t/t_interface_generic_modport_bad2.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad2.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_bad2.v b/test_regress/t/t_interface_generic_modport_bad2.v new file mode 100644 index 000000000..c52c012cb --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad2.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + modport mp ( + input v + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + a.v = 10; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_bad3.out b/test_regress/t/t_interface_generic_modport_bad3.out new file mode 100755 index 000000000..a822cf676 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad3.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_modport_bad3.v:18:11: Can't find definition of 'v' in dotted signal: 'a.v' + : ... note: In instance 't.genericModule' + 18 | if (a.v != 7) $stop; + | ^ + ... Known scopes under '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_interface_generic_modport_bad3.py b/test_regress/t/t_interface_generic_modport_bad3.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad3.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_bad3.v b/test_regress/t/t_interface_generic_modport_bad3.v new file mode 100644 index 000000000..f43f1bc3e --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_bad3.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + int o; + modport mp ( + input o + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_function.py b/test_regress/t/t_interface_generic_modport_function.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_function.v b/test_regress/t/t_interface_generic_modport_function.v new file mode 100644 index 000000000..ccf4c889b --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + function int get(); + return v; + endfunction + modport mp( + import get + ); +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.get() != 4) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 4; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_function2.py b/test_regress/t/t_interface_generic_modport_function2.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function2.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_function2.v b/test_regress/t/t_interface_generic_modport_function2.v new file mode 100644 index 000000000..27c33b0db --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function2.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + function int get(); + return v; + endfunction + modport mp( + output v + ); +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface.mp a); + initial begin + a.v = 5; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if(inf_inst.get() != 5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_function_bad.out b/test_regress/t/t_interface_generic_modport_function_bad.out new file mode 100755 index 000000000..f261dcef5 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_modport_function_bad.v:24:11: Can't find definition of 'get' in dotted task/function: 'a.get' + : ... note: In instance 't.genericModule' + 24 | if (a.get() != 4) $stop; + | ^~~ + ... Known scopes under 'get': + ... 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_interface_generic_modport_function_bad.py b/test_regress/t/t_interface_generic_modport_function_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_function_bad.v b/test_regress/t/t_interface_generic_modport_function_bad.v new file mode 100644 index 000000000..8ad2d2061 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_function_bad.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + function int get(); + return v; + endfunction + modport mp( + input v + ); +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.get() != 4) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 4; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_param.py b/test_regress/t/t_interface_generic_modport_param.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_param.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_param.v b/test_regress/t/t_interface_generic_modport_param.v new file mode 100644 index 000000000..f76ab4fc6 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_param.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + localparam lparam = 12; + + int v; + modport mp ( + input v + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.lparam != 12) $stop; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_task.py b/test_regress/t/t_interface_generic_modport_task.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_task.v b/test_regress/t/t_interface_generic_modport_task.v new file mode 100644 index 000000000..36b5b54fb --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + task setup(); + v = 3; + endtask + modport mp( + input v, + import setup + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + a.setup(); + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if (inf_inst.v != 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_task2.py b/test_regress/t/t_interface_generic_modport_task2.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task2.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_task2.v b/test_regress/t/t_interface_generic_modport_task2.v new file mode 100644 index 000000000..958c7fb66 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task2.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + task setup(); + v = 3; + endtask + modport mp( + input v + ); +endinterface + +module GenericModule (interface.mp a); + initial begin + #1; + if (a.v != 3) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.setup(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_modport_task_bad.out b/test_regress/t/t_interface_generic_modport_task_bad.out new file mode 100755 index 000000000..6db503a3c --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_modport_task_bad.v:23:7: Can't find definition of 'setup' in dotted task/function: 'a.setup' + : ... note: In instance 't.genericModule' + 23 | a.setup(); + | ^~~~~ + ... Known scopes under 'setup': + ... 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_interface_generic_modport_task_bad.py b/test_regress/t/t_interface_generic_modport_task_bad.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task_bad.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_modport_task_bad.v b/test_regress/t/t_interface_generic_modport_task_bad.v new file mode 100644 index 000000000..c0b82d74c --- /dev/null +++ b/test_regress/t/t_interface_generic_modport_task_bad.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + task setup(); + v = 3; + endtask + modport mp( + input v + ); +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface.mp a); + initial begin + a.setup(); + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if (inf_inst.v != 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_normal.py b/test_regress/t/t_interface_generic_normal.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_normal.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_normal.v b/test_regress/t/t_interface_generic_normal.v new file mode 100644 index 000000000..f6e3f9a61 --- /dev/null +++ b/test_regress/t/t_interface_generic_normal.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface a, inf b, interface c); + initial begin + #1; + if (a.v != 7) $stop; + if (b.v != 8) $stop; + if (c.v != 9) $stop; + end +endmodule + +module t; + inf inf_inst[3](); + GenericModule genericModule (inf_inst[0], inf_inst[1], inf_inst[2]); + initial begin + inf_inst[0].v = 7; + inf_inst[1].v = 8; + inf_inst[2].v = 9; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_positional.py b/test_regress/t/t_interface_generic_positional.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_positional.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_positional.v b/test_regress/t/t_interface_generic_positional.v new file mode 100644 index 000000000..caf1c4a87 --- /dev/null +++ b/test_regress/t/t_interface_generic_positional.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +module GenericModule (interface a, interface b); + initial begin + #1; + if (a.v != 7) $stop; + end +endmodule + +module t; + inf inf_inst[3](); + GenericModule genericModule (.a(inf_inst[1]), .b(inf_inst[2])); + initial begin + inf_inst[1].v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_submod_param.py b/test_regress/t/t_interface_generic_submod_param.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_interface_generic_submod_param.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_submod_param.v b/test_regress/t/t_interface_generic_submod_param.v new file mode 100644 index 000000000..729bcd19d --- /dev/null +++ b/test_regress/t/t_interface_generic_submod_param.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module sub #(parameter P); +endmodule + +package pkg; + parameter A = 3; +endpackage + +class B; + int x; +endclass + +module Gm (interface a); + B b; + sub#(.P(pkg::A + $bits(b.x))) s(); + initial begin + a.v = s.P; + end +endmodule + +interface inf; + int v; +endinterface + +module t; + inf i(); + Gm g(.a(i)); + initial begin + #1; + if (i.v != 35) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_task.py b/test_regress/t/t_interface_generic_task.py new file mode 100755 index 000000000..fda93f1f5 --- /dev/null +++ b/test_regress/t/t_interface_generic_task.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(timing_loop=True, verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_task.v b/test_regress/t/t_interface_generic_task.v new file mode 100644 index 000000000..b75c8f164 --- /dev/null +++ b/test_regress/t/t_interface_generic_task.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + task setup(); + v = 3; + endtask +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a); + initial begin + a.setup(); + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if (inf_inst.v != 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_task2.py b/test_regress/t/t_interface_generic_task2.py new file mode 100755 index 000000000..5b1a2f8cc --- /dev/null +++ b/test_regress/t/t_interface_generic_task2.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_task2.v b/test_regress/t/t_interface_generic_task2.v new file mode 100644 index 000000000..3400e7a4b --- /dev/null +++ b/test_regress/t/t_interface_generic_task2.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; + task setup(); + v = 3; + endtask +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a); + initial begin + #1; + if (a.v != 3) $stop; + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.setup(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_task_bad.out b/test_regress/t/t_interface_generic_task_bad.out new file mode 100755 index 000000000..80eea455c --- /dev/null +++ b/test_regress/t/t_interface_generic_task_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_task_bad.v:17:7: Can't find definition of 'setup' in dotted task/function: 'a.setup' + : ... note: In instance 't.genericModule' + 17 | a.setup(); + | ^~~~~ + ... Known scopes under 'setup': + ... 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_interface_generic_task_bad.py b/test_regress/t/t_interface_generic_task_bad.py new file mode 100755 index 000000000..a7c6a2ca7 --- /dev/null +++ b/test_regress/t/t_interface_generic_task_bad.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('linter') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_task_bad.v b/test_regress/t/t_interface_generic_task_bad.v new file mode 100644 index 000000000..2b5b9ad81 --- /dev/null +++ b/test_regress/t/t_interface_generic_task_bad.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface inf; + int v; +endinterface + +interface inf2; + int k; +endinterface + +module GenericModule (interface a); + initial begin + a.setup(); + end +endmodule + +module t; + inf inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + #1; + if (inf_inst.v != 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_param2.out b/test_regress/t/t_interface_param2.out deleted file mode 100644 index 916fc7f29..000000000 --- a/test_regress/t/t_interface_param2.out +++ /dev/null @@ -1,8 +0,0 @@ -%Error-UNSUPPORTED: t/t_interface_param2.v:38:12: Unsupported: generic interfaces - 38 | module mem(interface a); - | ^~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_interface_param2.v:49:12: Unsupported: generic interfaces - 49 | module cpu(interface b); - | ^~~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_interface_param2.py b/test_regress/t/t_interface_param2.py index fb4c87343..d4f986441 100755 --- a/test_regress/t/t_interface_param2.py +++ b/test_regress/t/t_interface_param2.py @@ -11,12 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile( - # Verilator unsupported, bug1104 - fails=test.vlt_all, - expect_filename=test.golden_filename) +test.compile() -if not test.vlt_all: - test.execute() +test.execute() test.passes() diff --git a/test_regress/t/t_interface_param2.v b/test_regress/t/t_interface_param2.v index 7aaaee46e..324514a94 100644 --- a/test_regress/t/t_interface_param2.v +++ b/test_regress/t/t_interface_param2.v @@ -31,7 +31,7 @@ interface simple_bus #(AWIDTH = 8, DWIDTH = 8) output data); initial begin - if (DWIDTH != 16) $stop; + if (DWIDTH != 8 && DWIDTH != 16) $stop; end endinterface: simple_bus @@ -40,7 +40,6 @@ module mem(interface a); always @(posedge a.clk) a.gnt <= a.req & avail; initial begin - if ($bits(a.data) != 16) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_json_only_begin_hier.out b/test_regress/t/t_json_only_begin_hier.out index 6ec35fd4b..aa4dbd617 100644 --- a/test_regress/t/t_json_only_begin_hier.out +++ b/test_regress/t/t_json_only_begin_hier.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"test","addr":"(E)","loc":"d,22:8,22:12","isChecker":false,"isProgram":false,"origName":"test","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"test","addr":"(E)","loc":"d,22:8,22:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"test","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"N","addr":"(F)","loc":"d,24:12,24:13","dtypep":"(G)","origName":"N","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":true,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"GENVAR","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"BEGIN","name":"FOR_GENERATE","addr":"(H)","loc":"d,25:14,25:17","generate":true,"genfor":false,"implied":true,"needProcess":false,"unnamed":false,"genforp": [],"stmtsp": []}, @@ -23,7 +23,7 @@ {"type":"CELL","name":"submod_3","addr":"(S)","loc":"d,31:21,31:29","origName":"submod_3","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]} ]}, - {"type":"MODULE","name":"submod","addr":"(K)","loc":"d,10:8,10:14","isChecker":false,"isProgram":false,"origName":"submod","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"submod","addr":"(K)","loc":"d,10:8,10:14","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"submod","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"BEGIN","name":"submod_gen","addr":"(T)","loc":"d,12:19,12:29","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], "stmtsp": [ @@ -36,7 +36,7 @@ ]}, {"type":"CELL","name":"submod_l0","addr":"(AB)","loc":"d,19:13,19:22","origName":"submod_l0","recursive":false,"modp":"(Y)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, - {"type":"MODULE","name":"submod2","addr":"(Y)","loc":"d,7:8,7:15","isChecker":false,"isProgram":false,"origName":"submod2","level":4,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [],"stmtsp": []} + {"type":"MODULE","name":"submod2","addr":"(Y)","loc":"d,7:8,7:15","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"submod2","level":4,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [],"stmtsp": []} ],"filesp": [], "miscsp": [ {"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"UNLINKED", @@ -46,7 +46,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(BB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(BB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(CB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(BB)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_debugcheck.out b/test_regress/t/t_json_only_debugcheck.out index 779a57ae3..4fea97b0b 100644 --- a/test_regress/t/t_json_only_debugcheck.out +++ b/test_regress/t/t_json_only_debugcheck.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"(E)","stdPackagep":"UNLINKED","evalp":"(F)","evalNbap":"(G)","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"(H)", "modulesp": [ - {"type":"MODULE","name":"$root","addr":"(I)","loc":"d,11:8,11:9","isChecker":false,"isProgram":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"$root","addr":"(I)","loc":"d,11:8,11:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"clk","addr":"(J)","loc":"d,15:10,15:13","dtypep":"(K)","origName":"clk","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":true,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"clker","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"t.e","addr":"(L)","loc":"d,24:9,24:10","dtypep":"(M)","origName":"e","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"my_t","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -2987,7 +2987,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(URB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(URB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"TOP","addr":"(VRB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(URB)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_first.out b/test_regress/t/t_json_only_first.out index d0ea76704..e913805b9 100644 --- a/test_regress/t/t_json_only_first.out +++ b/test_regress/t/t_json_only_first.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"t","addr":"(E)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"t","addr":"(E)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"t","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"q","addr":"(F)","loc":"d,15:22,15:23","dtypep":"(G)","origName":"q","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"clk","addr":"(H)","loc":"d,13:10,13:13","dtypep":"(I)","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -37,7 +37,7 @@ ]} ],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, - {"type":"MODULE","name":"mod2","addr":"(X)","loc":"d,46:8,46:12","isChecker":false,"isProgram":false,"origName":"mod2","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mod2","addr":"(X)","loc":"d,46:8,46:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mod2","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"clk","addr":"(FB)","loc":"d,48:10,48:13","dtypep":"(I)","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"d","addr":"(Z)","loc":"d,49:16,49:17","dtypep":"(G)","origName":"d","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -50,7 +50,7 @@ {"type":"VARREF","name":"q","addr":"(JB)","loc":"d,53:13,53:14","dtypep":"(G)","access":"WR","varp":"(CB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"MODULE","name":"mod1__W4","addr":"(M)","loc":"d,31:8,31:12","isChecker":false,"isProgram":false,"origName":"mod1","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mod1__W4","addr":"(M)","loc":"d,31:8,31:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mod1","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"WIDTH","addr":"(KB)","loc":"d,32:15,32:20","dtypep":"(LB)","origName":"WIDTH","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"GPARAM","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":true,"isParam":true,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [], "valuep": [ @@ -93,7 +93,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(WB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(WB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(XB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(WB)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_flat.out b/test_regress/t/t_json_only_flat.out index 7cdddf973..6ce3beadd 100644 --- a/test_regress/t/t_json_only_flat.out +++ b/test_regress/t/t_json_only_flat.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"(E)", "modulesp": [ - {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"q","addr":"(G)","loc":"d,15:22,15:23","dtypep":"(H)","origName":"q","isSc":false,"isPrimaryIO":true,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"clk","addr":"(I)","loc":"d,13:10,13:13","dtypep":"(J)","origName":"clk","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"clker","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -146,7 +146,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(BD)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(BD)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(CD)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(BD)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_flat_no_inline_mod.out b/test_regress/t/t_json_only_flat_no_inline_mod.out index 8b82551b9..5c5072987 100644 --- a/test_regress/t/t_json_only_flat_no_inline_mod.out +++ b/test_regress/t/t_json_only_flat_no_inline_mod.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"(E)", "modulesp": [ - {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,11:8,11:11","isChecker":false,"isProgram":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,11:8,11:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"i_clk","addr":"(G)","loc":"d,11:24,11:29","dtypep":"(H)","origName":"i_clk","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"top.i_clk","addr":"(I)","loc":"d,11:24,11:29","dtypep":"(H)","origName":"i_clk","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -39,7 +39,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(U)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(U)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(V)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(U)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_flat_pub_mod.out b/test_regress/t/t_json_only_flat_pub_mod.out index 8b82551b9..5c5072987 100644 --- a/test_regress/t/t_json_only_flat_pub_mod.out +++ b/test_regress/t/t_json_only_flat_pub_mod.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"(E)", "modulesp": [ - {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,11:8,11:11","isChecker":false,"isProgram":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,11:8,11:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"i_clk","addr":"(G)","loc":"d,11:24,11:29","dtypep":"(H)","origName":"i_clk","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"top.i_clk","addr":"(I)","loc":"d,11:24,11:29","dtypep":"(H)","origName":"i_clk","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -39,7 +39,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(U)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(U)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(V)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(U)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_flat_vlvbound.out b/test_regress/t/t_json_only_flat_vlvbound.out index 8d7116053..ed297b222 100644 --- a/test_regress/t/t_json_only_flat_vlvbound.out +++ b/test_regress/t/t_json_only_flat_vlvbound.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"(E)", "modulesp": [ - {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,7:8,7:21","isChecker":false,"isProgram":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"$root","addr":"(F)","loc":"d,7:8,7:21","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"$root","level":1,"modPublic":true,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"i_a","addr":"(G)","loc":"d,9:25,9:28","dtypep":"(H)","origName":"i_a","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"i_b","addr":"(I)","loc":"d,10:25,10:28","dtypep":"(H)","origName":"i_b","isSc":false,"isPrimaryIO":true,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":true,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -307,7 +307,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(OF)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(OF)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(PF)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(OF)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_output.out b/test_regress/t/t_json_only_output.out index 6b48a8b04..8d4d048e7 100644 --- a/test_regress/t/t_json_only_output.out +++ b/test_regress/t/t_json_only_output.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"m","addr":"(E)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"origName":"m","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"m","addr":"(E)","loc":"d,7:8,7:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"m","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"clk","addr":"(F)","loc":"d,8:10,8:13","dtypep":"(G)","origName":"clk","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]} @@ -12,7 +12,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(H)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(H)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(I)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(H)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index 96ab43284..6eeaa4ad6 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -1,6 +1,6 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"m","addr":"(E)","loc":"d,12:8,12:9","isChecker":false,"isProgram":false,"origName":"m","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"m","addr":"(E)","loc":"d,12:8,12:9","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"m","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"clk_ip","addr":"(F)","loc":"d,14:11,14:17","dtypep":"(G)","origName":"clk_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"rst_ip","addr":"(H)","loc":"d,15:11,15:17","dtypep":"(G)","origName":"rst_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, @@ -91,7 +91,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(AC)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(AC)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(BC)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(AC)","varsp": [],"blocksp": [],"inlinesp": []} ]} diff --git a/test_regress/t/t_var_port_json_only.out b/test_regress/t/t_var_port_json_only.out index f441175aa..e65946543 100644 --- a/test_regress/t/t_var_port_json_only.out +++ b/test_regress/t/t_var_port_json_only.out @@ -1,66 +1,66 @@ {"type":"NETLIST","name":"$root","addr":"(B)","loc":"a,0:0,0:0","timeunit":"1ps","timeprecision":"1ps","typeTablep":"(C)","constPoolp":"(D)","dollarUnitPkgp":"UNLINKED","stdPackagep":"UNLINKED","evalp":"UNLINKED","evalNbap":"UNLINKED","dpiExportTriggerp":"UNLINKED","delaySchedulerp":"UNLINKED","nbaEventp":"UNLINKED","nbaEventTriggerp":"UNLINKED","topScopep":"UNLINKED", "modulesp": [ - {"type":"MODULE","name":"mh2","addr":"(E)","loc":"d,18:8,18:11","isChecker":false,"isProgram":false,"origName":"mh2","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh2","addr":"(E)","loc":"d,18:8,18:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh2","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_inout_wire_integer","addr":"(F)","loc":"d,18:27,18:47","dtypep":"(G)","origName":"x_inout_wire_integer","isSc":false,"isPrimaryIO":false,"direction":"INOUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh5","addr":"(H)","loc":"d,24:8,24:11","isChecker":false,"isProgram":false,"origName":"mh5","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh5","addr":"(H)","loc":"d,24:8,24:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh5","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_input_wire_logic","addr":"(I)","loc":"d,24:19,24:37","dtypep":"(J)","origName":"x_input_wire_logic","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh6","addr":"(K)","loc":"d,26:8,26:11","isChecker":false,"isProgram":false,"origName":"mh6","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh6","addr":"(K)","loc":"d,26:8,26:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh6","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_input_var_logic","addr":"(L)","loc":"d,26:23,26:40","dtypep":"(J)","origName":"x_input_var_logic","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh7","addr":"(M)","loc":"d,28:8,28:11","isChecker":false,"isProgram":false,"origName":"mh7","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh7","addr":"(M)","loc":"d,28:8,28:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh7","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_input_var_integer","addr":"(N)","loc":"d,28:31,28:50","dtypep":"(G)","origName":"x_input_var_integer","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh8","addr":"(O)","loc":"d,30:8,30:11","isChecker":false,"isProgram":false,"origName":"mh8","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh8","addr":"(O)","loc":"d,30:8,30:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh8","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_wire_logic","addr":"(P)","loc":"d,30:20,30:39","dtypep":"(J)","origName":"x_output_wire_logic","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh9","addr":"(Q)","loc":"d,32:8,32:11","isChecker":false,"isProgram":false,"origName":"mh9","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh9","addr":"(Q)","loc":"d,32:8,32:11","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh9","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_var_logic","addr":"(R)","loc":"d,32:24,32:42","dtypep":"(J)","origName":"x_output_var_logic","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh10","addr":"(S)","loc":"d,34:8,34:12","isChecker":false,"isProgram":false,"origName":"mh10","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh10","addr":"(S)","loc":"d,34:8,34:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh10","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_wire_logic_signed_p6","addr":"(T)","loc":"d,34:33,34:62","dtypep":"(U)","origName":"x_output_wire_logic_signed_p6","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh11","addr":"(V)","loc":"d,36:8,36:12","isChecker":false,"isProgram":false,"origName":"mh11","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh11","addr":"(V)","loc":"d,36:8,36:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh11","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_var_integer","addr":"(W)","loc":"d,36:28,36:48","dtypep":"(G)","origName":"x_output_var_integer","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh12","addr":"(X)","loc":"d,38:8,38:12","isChecker":false,"isProgram":false,"origName":"mh12","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh12","addr":"(X)","loc":"d,38:8,38:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh12","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_ref_logic_p6","addr":"(Y)","loc":"d,38:23,38:37","dtypep":"(Z)","origName":"x_ref_logic_p6","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh13","addr":"(AB)","loc":"d,40:8,40:12","isChecker":false,"isProgram":false,"origName":"mh13","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh13","addr":"(AB)","loc":"d,40:8,40:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh13","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_ref_var_logic_u6","addr":"(BB)","loc":"d,40:17,40:35","dtypep":"(CB)","origName":"x_ref_var_logic_u6","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh17","addr":"(DB)","loc":"d,50:8,50:12","isChecker":false,"isProgram":false,"origName":"mh17","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh17","addr":"(DB)","loc":"d,50:8,50:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh17","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_input_var_integer","addr":"(EB)","loc":"d,50:31,50:50","dtypep":"(G)","origName":"x_input_var_integer","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"y_input_wire_logic","addr":"(FB)","loc":"d,50:57,50:75","dtypep":"(J)","origName":"y_input_wire_logic","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh18","addr":"(GB)","loc":"d,52:8,52:12","isChecker":false,"isProgram":false,"origName":"mh18","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh18","addr":"(GB)","loc":"d,52:8,52:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh18","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_var_logic","addr":"(HB)","loc":"d,52:24,52:42","dtypep":"(J)","origName":"x_output_var_logic","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"y_input_wire_logic","addr":"(IB)","loc":"d,52:50,52:68","dtypep":"(J)","origName":"y_input_wire_logic","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh19","addr":"(JB)","loc":"d,54:8,54:12","isChecker":false,"isProgram":false,"origName":"mh19","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh19","addr":"(JB)","loc":"d,54:8,54:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh19","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_output_wire_logic_signed_p6","addr":"(KB)","loc":"d,54:33,54:62","dtypep":"(U)","origName":"x_output_wire_logic_signed_p6","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"y_output_var_integer","addr":"(LB)","loc":"d,54:72,54:92","dtypep":"(G)","origName":"y_output_var_integer","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh20","addr":"(MB)","loc":"d,56:8,56:12","isChecker":false,"isProgram":false,"origName":"mh20","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh20","addr":"(MB)","loc":"d,56:8,56:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh20","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"x_ref_var_logic_p6","addr":"(NB)","loc":"d,56:23,56:41","dtypep":"(Z)","origName":"x_ref_var_logic_p6","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"y_ref_var_logic_p6","addr":"(OB)","loc":"d,56:43,56:61","dtypep":"(Z)","origName":"y_ref_var_logic_p6","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} ]}, - {"type":"MODULE","name":"mh21","addr":"(PB)","loc":"d,58:8,58:12","isChecker":false,"isProgram":false,"origName":"mh21","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], + {"type":"MODULE","name":"mh21","addr":"(PB)","loc":"d,58:8,58:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"mh21","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"ref_var_logic_u6","addr":"(QB)","loc":"d,58:17,58:33","dtypep":"(RB)","origName":"ref_var_logic_u6","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"y_ref_var_logic","addr":"(SB)","loc":"d,58:41,58:56","dtypep":"(J)","origName":"y_ref_var_logic","isSc":false,"isPrimaryIO":false,"direction":"REF","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []} @@ -97,7 +97,7 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(AC)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(AC)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [], "stmtsp": [ {"type":"SCOPE","name":"@CONST-POOL@","addr":"(BC)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(AC)","varsp": [],"blocksp": [],"inlinesp": []} ]} From f506aa878bfab7b2468fe867ea3e3193ca939ea2 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Fri, 22 Aug 2025 13:58:03 +0200 Subject: [PATCH 214/252] Fix of inline constraints with member selects (#6321) --- src/V3AstNodes.cpp | 2 +- src/V3LinkResolve.cpp | 38 +++++++++++++++ test_regress/t/t_randomize_member_select.py | 21 ++++++++ test_regress/t/t_randomize_member_select.v | 50 ++++++++++++++++++++ test_regress/t/t_randomize_neasted_unsup.out | 5 ++ test_regress/t/t_randomize_neasted_unsup.py | 16 +++++++ test_regress/t/t_randomize_neasted_unsup.v | 21 ++++++++ 7 files changed, 152 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_randomize_member_select.py create mode 100644 test_regress/t/t_randomize_member_select.v create mode 100644 test_regress/t/t_randomize_neasted_unsup.out create mode 100755 test_regress/t/t_randomize_neasted_unsup.py create mode 100644 test_regress/t/t_randomize_neasted_unsup.v diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 920b5bd75..12ab9539e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2058,7 +2058,7 @@ AstMemberSel::AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp) bool AstMemberSel::sameNode(const AstNode* samep) const { const AstMemberSel* const sp = VN_DBG_AS(samep, MemberSel); return sp && access() == sp->access() && fromp()->isSame(sp->fromp()) && name() == sp->name() - && (varp() && sp->varp() && varp()->sameNode(sp->varp())); + && (varp() == sp->varp() || (varp() && sp->varp() && varp()->sameNode(sp->varp()))); } void AstMemberSel::dump(std::ostream& str) const { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 7d7552171..f2b97b849 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -54,6 +54,8 @@ class LinkResolveVisitor final : public VNVisitor { int m_senitemCvtNum = 0; // Temporary signal counter std::deque m_underGenFors; // Stack of GenFor underneath bool m_underGenerate = false; // Under GenFor/GenIf + AstNodeExpr* m_currentRandomizeSelectp = nullptr; // fromp() of current `randomize()` call + bool m_inRandomizeWith = false; // If in randomize() with (and no other with afterwards) // VISITORS // TODO: Most of these visitors are here for historical reasons. @@ -185,6 +187,18 @@ class LinkResolveVisitor final : public VNVisitor { if (nodep->dpiExport()) nodep->scopeNamep(new AstScopeName{nodep->fileline(), false}); } void visit(AstNodeFTaskRef* nodep) override { + VL_RESTORER(m_currentRandomizeSelectp); + + if (nodep->name() == "randomize") { + if (const AstMethodCall* const methodcallp = VN_CAST(nodep, MethodCall)) { + if (m_inRandomizeWith) { + nodep->v3warn( + E_UNSUPPORTED, + "Unsupported: randomize() nested in inline randomize() constraints"); + } + m_currentRandomizeSelectp = methodcallp->fromp(); + } + } iterateChildren(nodep); if (AstLet* letp = VN_CAST(nodep->taskp(), Let)) { UINFO(7, "letSubstitute() " << nodep << " <- " << letp); @@ -525,6 +539,30 @@ class LinkResolveVisitor final : public VNVisitor { iterateChildren(nodep); } + void visit(AstMemberSel* nodep) override { + if (m_inRandomizeWith && nodep->fromp()->isSame(m_currentRandomizeSelectp)) { + // Replace member selects to the element + // on which the randomize() is called with LambdaArgRef + // This allows V3Randomize to work properly when + // constrained variables are referred using that object + AstNodeExpr* const prevFromp = nodep->fromp(); + prevFromp->replaceWith( + new AstLambdaArgRef{prevFromp->fileline(), prevFromp->name(), false}); + pushDeletep(prevFromp); + } + iterateChildren(nodep); + } + + void visit(AstWith* nodep) override { + VL_RESTORER(m_inRandomizeWith); + if (const AstMethodCall* const methodCallp = VN_CAST(nodep->backp(), MethodCall)) { + m_inRandomizeWith = methodCallp->name() == "randomize"; + } else { + m_inRandomizeWith = false; + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { iterateChildren(nodep); } public: diff --git a/test_regress/t/t_randomize_member_select.py b/test_regress/t/t_randomize_member_select.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_member_select.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_member_select.v b/test_regress/t/t_randomize_member_select.v new file mode 100644 index 000000000..d0bd36df5 --- /dev/null +++ b/test_regress/t/t_randomize_member_select.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class B; + rand int insideB; + constraint i { + insideB inside {[0:10]}; + }; +endclass + +class A; + rand logic[31:0] rdata; + rand int delay; + int i = 97; + rand B b; + function new(); + b = new; + endfunction + constraint delay_bounds { + delay inside {[0:2]}; + } +endclass + +module t; + A a; + int i; + int delay; + logic[31:0] rdata; + int b; + initial begin + a = new; + i = 7; + repeat (120) begin + a.b.insideB = 3; + a.delay = 1; + a.rdata = 3; + if (a.randomize() with {if (a.delay == 1) a.rdata == i;} == 0) $stop; + if (a.b.randomize() with {a.b.insideB < 3;} == 0) $stop; + if (a.delay == 1 && a.rdata != 97) $stop; + if (a.b.insideB >= 3) $stop; + if (a.randomize() with {if (a.delay == 1) a.rdata == local::i;} == 0) $stop; + if (a.delay == 1 && a.rdata != 7) $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_neasted_unsup.out b/test_regress/t/t_randomize_neasted_unsup.out new file mode 100644 index 000000000..61a5c5a27 --- /dev/null +++ b/test_regress/t/t_randomize_neasted_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_randomize_neasted_unsup.v:17:41: Unsupported: randomize() nested in inline randomize() constraints + 17 | if (a.randomize() with {rdata == aa.randomize();} == 0) $stop; + | ^~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_neasted_unsup.py b/test_regress/t/t_randomize_neasted_unsup.py new file mode 100755 index 000000000..1bf1426f9 --- /dev/null +++ b/test_regress/t/t_randomize_neasted_unsup.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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_neasted_unsup.v b/test_regress/t/t_randomize_neasted_unsup.v new file mode 100644 index 000000000..8a47d9167 --- /dev/null +++ b/test_regress/t/t_randomize_neasted_unsup.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class A; + rand logic[31:0] rdata; +endclass + +module t; + A a; + A aa; + initial begin + a = new; + aa = new; + if (a.randomize() with {rdata == aa.randomize();} == 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 1c86ff0af2ce2106f862d002ef3a43e1709916db Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 22 Aug 2025 21:43:49 +0100 Subject: [PATCH 215/252] Fix corner case bugs in module and variable inlining (#6322) There were a couple corner case bugs in V3Inline, and one in Dfg when dealing with inlining of modules/variables. V3Inline: - Invalid code generated when inlining an input that also had an assignment to it (Throws an ASSIGNIN, but this is sometimes reasonable to do, e.g. hiererchical reference to an unonnected input port) - Inlining (aliasing) publicly writeable input port. - Inlining forcable port connected to constant. Dfg: - Inining publicly writeable variables The tests that cover these are the same and fixing one will trigger the other bug, so fixing them all in one go. Also cleanup V3Inline to be less out of order and rely less on unique APIs only used by V3Inine (will remove those in follow up patch). Small step towards #6280. --- src/V3Ast.h | 5 - src/V3DfgPeephole.cpp | 1 + src/V3DfgVertices.h | 3 +- src/V3Inline.cpp | 484 +++++++++--------- src/V3Inst.cpp | 2 +- src/V3Inst.h | 2 +- src/V3Sched.cpp | 2 +- test_regress/t/t_dist_cppstyle.py | 2 +- test_regress/t/t_dpi_var.cpp | 22 +- test_regress/t/t_dpi_var.py | 16 +- test_regress/t/t_dpi_var.v | 45 +- test_regress/t/t_dpi_var.vlt | 2 + test_regress/t/t_force_port_alias.v | 61 --- ...e_port_alias.py => t_force_port_inline.py} | 2 +- test_regress/t/t_force_port_inline.v | 75 +++ test_regress/t/t_inst_tree_inl0_pub1.py | 2 +- test_regress/t/t_json_only_flat.out | 36 +- test_regress/t/t_var_in_assign_pedantic.py | 4 +- test_regress/t/t_xml_flat.out | 16 +- 19 files changed, 417 insertions(+), 365 deletions(-) delete mode 100644 test_regress/t/t_force_port_alias.v rename test_regress/t/{t_force_port_alias.py => t_force_port_inline.py} (91%) create mode 100644 test_regress/t/t_force_port_inline.v diff --git a/src/V3Ast.h b/src/V3Ast.h index 1cf11dcf1..0e86c79a4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1941,8 +1941,6 @@ public: inline void iterateChildrenBackwardsConst(AstNode* nodep); /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list inline void iterateAndNextConstNull(AstNode* nodep); - /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order - inline void iterateAndNextConstNullBackwards(AstNode* nodep); virtual void visit(AstNode* nodep) = 0; virtual ~VNVisitorConst() {} @@ -3128,9 +3126,6 @@ void VNVisitorConst::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildr void VNVisitorConst::iterateChildrenBackwardsConst(AstNode* nodep) { nodep->iterateChildrenBackwardsConst(*this); } -void VNVisitorConst::iterateAndNextConstNullBackwards(AstNode* nodep) { - if (VL_LIKELY(nodep)) nodep->iterateListBackwardsConst(*this); -} void VNVisitorConst::iterateAndNextConstNull(AstNode* nodep) { if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this); } diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 5a1df937f..96c857911 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1216,6 +1216,7 @@ class V3DfgPeephole final : public DfgVisitor { DfgVarArray* const varp = vtxp->fromp()->cast(); if (!varp) return; if (varp->varp()->isForced()) return; + if (varp->varp()->isSigUserRWPublic()) return; DfgVertex* const srcp = varp->srcp(); if (!srcp) return; diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 1b0370725..9e45add00 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -70,7 +70,8 @@ public: void tmpForp(AstNode* nodep) { m_tmpForp = nodep; } bool isDrivenFullyByDfg() const { - return srcp() && !srcp()->is() && !varp()->isForced(); + return srcp() && !srcp()->is() && !varp()->isForced() + && !varp()->isSigUserRWPublic(); } // Variable referenced via an AstVarXRef (hierarchical reference) diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index e1bd41da2..7b9c6c3f7 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -250,9 +250,9 @@ class InlineRelinkVisitor final : public VNVisitor { // STATE std::unordered_set m_renamedInterfaces; // Name of renamed interface variables - AstNodeModule* const m_modp; // Current module - const AstCell* const m_cellp; // Cell being cloned - bool m_initialStatic = false; // Inside InitialStatic + AstNodeModule* const m_modp; // The module we are inlining into + const AstCell* const m_cellp; // The cell being inlined + size_t m_nPlaceholders = 0; // Unique identifier sequence number for placeholder variables // VISITORS void visit(AstCellInline* nodep) override { @@ -279,66 +279,6 @@ class InlineRelinkVisitor final : public VNVisitor { iterateChildren(nodep); } void visit(AstVar* nodep) override { - if (nodep->user2p()) { - // Make an assignment, so we'll trace it properly - // user2p is either a const or a var. - FileLine* const flp = nodep->fileline(); - AstConst* const exprconstp = VN_CAST(nodep->user2p(), Const); - AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef); - UINFO(8, "connectto: " << nodep->user2p()); - UASSERT_OBJ(exprconstp || exprvarrefp, nodep, - "Unknown interconnect type; pinReconnectSimple should have cleared up"); - if (exprconstp) { - m_modp->addStmtsp(new AstAssignW{flp, new AstVarRef{flp, nodep, VAccess::WRITE}, - exprconstp->cloneTree(false)}); - nodep->user4(true); // Making assignment to it - } else if (nodep->user3()) { - // Public variable at the lower module end - we need to make sure we propagate - // the logic changes up and down; if we aliased, we might - // remove the change detection on the output variable. - UINFO(9, "public pin assign: " << exprvarrefp); - UASSERT_OBJ(!nodep->isNonOutput(), nodep, "Outputs only - inputs use AssignAlias"); - m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp->cloneTree(false), - new AstVarRef{flp, nodep, VAccess::READ}}); - } else if (nodep->isSigPublic() && VN_IS(nodep->dtypep(), UnpackArrayDType)) { - // Public variable at this end and it is an unpacked array. We need to assign - // instead of aliased, because otherwise it will pass V3Slice and invalid - // code will be emitted. - UINFO(9, "assign to public and unpacked: " << nodep); - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - nodep->user4(true); // Making assignment to it - m_modp->addStmtsp( - new AstAssignW{flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp}); - } else if (nodep->isIfaceRef()) { - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - m_modp->addStmtsp(new AstAssignVarScope{ - flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp}); - FileLine* const flbp = exprvarrefp->varp()->fileline(); - flp->modifyStateInherit(flbp); - flbp->modifyStateInherit(flp); - } else { - // Do to inlining child's variable now within the same - // module, so a AstVarRef not AstVarXRef below - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - AstVarRef* const nodeVarRefp = new AstVarRef{flp, nodep, VAccess::WRITE}; - if (nodep->isForced() && nodep->direction() == VDirection::INPUT) { - nodep->user4(true); // Making assignment to it - m_modp->addStmtsp(new AstAssignW{flp, nodeVarRefp, exprvarrefp}); - } else if (nodep->isForced() && nodep->direction() == VDirection::OUTPUT) { - exprvarrefp->access(VAccess::WRITE); - nodeVarRefp->access(VAccess::READ); - m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp, nodeVarRefp}); - } else { - m_modp->addStmtsp(new AstAssignAlias{flp, nodeVarRefp, exprvarrefp}); - } - FileLine* const flbp = exprvarrefp->varp()->fileline(); - flp->modifyStateInherit(flbp); - flbp->modifyStateInherit(flp); - } - } // Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) { m_renamedInterfaces.insert(nodep->name()); @@ -350,7 +290,7 @@ class InlineRelinkVisitor final : public VNVisitor { ifacerefp->addNextHere(newdp); // Relink to point to newly cloned cell if (newdp->cellp()) { - if (AstCell* const newcellp = VN_CAST(newdp->cellp()->user4p(), Cell)) { + if (AstCell* const newcellp = VN_CAST(newdp->cellp()->user3p(), Cell)) { newdp->cellp(newcellp); newdp->cellName(newcellp->name()); // Tag the old ifacerefp to ensure it leaves no stale @@ -377,41 +317,47 @@ class InlineRelinkVisitor final : public VNVisitor { nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); iterateChildren(nodep); } - void visit(AstInitialStatic* nodep) override { - VL_RESTORER(m_initialStatic); - m_initialStatic = true; - iterateChildren(nodep); - } - void visit(AstNodeAssign* nodep) override { - if (const AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) { - if (m_initialStatic && varrefp->varp()->user2() && varrefp->varp()->user4()) { - // Initial assignment to i/o we are overriding, can remove - UINFO(9, "Remove InitialStatic " << nodep); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } - } - iterateChildren(nodep); + void visit(AstAssignAlias* nodep) override { + // Don't replace port variable in the alias } void visit(AstVarRef* nodep) override { - if (nodep->varp()->user2p() // It's being converted to an alias. - && !nodep->varp()->user3() - // Don't constant propagate aliases (we just made) - && !VN_IS(nodep->backp(), AssignAlias) - // Forced signals do not use aliases - && !nodep->varp()->isForced()) { - const AstVar* const varp = nodep->varp(); - if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) { - nodep->replaceWith(constp->cloneTree(false)); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } else if (const AstVarRef* const vrefp = VN_CAST(varp->user2p(), VarRef)) { - nodep->varp(vrefp->varp()); - nodep->classOrPackagep(vrefp->classOrPackagep()); + // If the target port is being inlined, replace reference with the + // connected expression (always a Const of a VarRef). + AstNode* const pinExpr = nodep->varp()->user2p(); + if (!pinExpr) return; + + // If it's a constant, inline it + if (AstConst* const constp = VN_CAST(pinExpr, Const)) { + // You might think we would not try to substitute a constant for + // a written variable, but we might need to do this if for example + // there is an assignment to an input port, and that input port + // is tied to a constant on the cell we are inlining. This does + // generate an ASSIGNIN warning, but that can be downgraded to + // a warning. (Also assigning to an input can has valid uses if + // e.g. done via a hierarchical reference from outside to an input + // unconnected on the instance, so we don't want ASSIGNIN fatal.) + // Same applies when there is a static initialzier for an input. + // To avoid having to special case malformed assignment, or worse + // yet emiting code like 0 = 0, we instead substitute a placeholder + // variable that will later be pruned (it will otherwise be unreferenced). + if (!nodep->access().isReadOnly()) { + AstVar* const varp = nodep->varp(); + const std::string name = "__vInlPlaceholder_" + std::to_string(++m_nPlaceholders); + AstVar* const holdep = new AstVar{varp->fileline(), VVarType::VAR, name, varp}; + m_modp->addStmtsp(holdep); + AstVarRef* const newp = new AstVarRef{nodep->fileline(), holdep, nodep->access()}; + nodep->replaceWith(newp); } else { - nodep->v3fatalSrc("Null connection?"); + nodep->replaceWith(constp->cloneTree(false)); } + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; } + + // Otherwise it must be a variable reference, retarget this ref + const AstVarRef* const vrefp = VN_AS(pinExpr, VarRef); + nodep->varp(vrefp->varp()); + nodep->classOrPackagep(vrefp->classOrPackagep()); } void visit(AstVarXRef* nodep) override { // Track what scope it was originally under so V3LinkDot can resolve it @@ -476,175 +422,226 @@ public: }; //###################################################################### -// Inline state, as a visitor of each AstNode +// Module inliner -class InlineVisitor final : public VNVisitor { - // NODE STATE - // Cleared entire netlist - // AstIfaceRefDType::user1() // Whether the cell pointed to by this - // // AstIfaceRefDType has been inlined - // Input: - // AstNodeModule::user1p() // ModuleState instance (via m_moduleState) - // Cleared each cell - // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this - // // is a direct connect to - // AstVar::user3() // bool Don't alias the user2, keep it as signal - // AstVar::user4() // bool Was input, remove InitialStatic Assign - // AstCell::user4 // AstCell* of the created clone - const VNUser4InUse m_inuser4; +namespace ModuleInliner { - ModuleStateUser1Allocator& m_moduleState; +// A port variable in an inlined module can be connected 2 ways. +// Either add a continuous assignment between the pin expression from +// the instance and the port variable, or simply inline the pin expression +// in place of the port variable. We will prefer to do the later whenever +// possible (and sometimes required). When inlining, we need to create an +// alias for the inined variable, in order to resovle hierarchical refrences +// against it later in V3Scope (and also for tracing, which is inserted +//later). Returns ture iff the given port variable should be inlined, +// and false if a continuous assignment should be used. +bool inlinePort(AstVar* nodep) { + // Interface references are always inlined + if (nodep->isIfaceRef()) return true; + // Ref ports must be always inlined + if (nodep->direction() == VDirection::REF) return true; + // Forced signals must not be inlined. The port signal can be + // forced separately from the connected signals. + if (nodep->isForced()) return false; - // STATE - AstNodeModule* m_modp = nullptr; // Current module - VDouble0 m_statCells; // Statistic tracking + // Note: For singls marked 'public' (and not 'public_flat') inlining + // of their contaning modules is disabled so they wont reach here. - // METHODS - void inlineCell(AstCell* nodep) { - UINFO(5, " Inline CELL " << nodep); + // TODO: For now, writable public signals inside the cell cannot be + // eliminated as they are entered into the VerilatedScope, and + // changes would not propagate to it when assigned. (The alias created + // for them ensures they would be read correctly, but would not + // propagate any changes.) This can be removed when the VerialtedScope + // construction in V3EmitCSyms understands aliases. + if (nodep->isSigUserRWPublic()) return false; - const VNUser2InUse user2InUse; - const VNUser3InUse user3InUse; + // Otherwise we can repalce the variable + return true; +} - ++m_statCells; +// Connect the given port 'nodep' (being inlined into 'modp') to the given +// expression (from the Cell Pin) +void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) { + UINFO(6, "Connecting " << pinExprp); + UINFO(6, " to " << nodep); - // Before cloning simplify pin assignments. Better off before, as if the module has - // multiple instantiations we will save work, and we can't call pinReconnectSimple in this - // loop as it clone()s itself. - for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - V3Inst::pinReconnectSimple(pinp, nodep, false); - } + // Decide whether to inline the port variable or use continuous assignments + const bool inlineIt = inlinePort(nodep); - // Is this the last cell referencing this module? - const bool lastCell = --m_moduleState(nodep->modp()).m_cellRefs == 0; + // If we deccided to inline it, record the expression to substitute this variable with + if (inlineIt) nodep->user2p(pinExprp); - // Important: If this is the last cell, then don't clone the instantiated module but - // inline the original directly. While this requires some special casing, doing so - // saves us having to temporarily clone the module for the last cell, which - // significantly reduces Verilator memory usage. This is especially true as often the - // top few levels of the hierarchy are singleton wrapper modules, which we always - // inline. In this case this special casing saves us from having to clone essentially - // the entire netlist, which would in effect double Verilator memory consumption, or - // worse if we put off deleting the inlined modules until the end. Not having to clone - // large trees also improves speed. - AstNodeModule* newmodp = nullptr; - if (!lastCell) { - // Clone original module - newmodp = nodep->modp()->cloneTree(false); + FileLine* const flp = nodep->fileline(); + + // Helper to creates an AstVarRef reference to the port variable + const auto portRef = [&](VAccess access) { return new AstVarRef{flp, nodep, access}; }; + + // If the connected expression is a constant, add an assignment to set + // the port variable. The constant can still be inlined, in which case + // this is needed for tracing the inlined port variable. + if (AstConst* const pinp = VN_CAST(pinExprp, Const)) { + modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)}); + return; + } + + // Otherwise it must be a variable reference due to having called pinReconnectSimple + const AstVarRef* const pinRefp = VN_AS(pinExprp, VarRef); + + // Helper to create an AstVarRef reference to the pin variable + const auto pinRef = [&](VAccess access) { + AstVarRef* const p = new AstVarRef{pinRefp->fileline(), pinRefp->varp(), access}; + p->classOrPackagep(pinRefp->classOrPackagep()); + return p; + }; + + // If it is being inlined, create the alias for it + if (inlineIt) { + UINFO(6, "Inlning port variable: " << nodep); + if (nodep->isIfaceRef()) { + modp->addStmtsp( + new AstAssignVarScope{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); } else { - // For the last cell, reuse the original module - nodep->modp()->unlinkFrBack(); - newmodp = nodep->modp(); + modp->addStmtsp( + new AstAssignAlias{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); } - // Find cell cross-references - nodep->modp()->foreach([](AstCell* cellp) { - // clonep is nullptr when inlining the last instance, if so the use original node - cellp->user4p(cellp->clonep() ? cellp->clonep() : cellp); - }); - // Create data for dotted variable resolution - AstCellInline* const inlinep - = new AstCellInline{nodep->fileline(), nodep->name(), nodep->modp()->origName(), - nodep->modp()->timeunit()}; - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - // Create assignments to the pins - for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; - UINFO(6, " Pin change from " << pinp->modVarp()); - - AstNode* const connectRefp = pinp->exprp(); - UASSERT_OBJ(VN_IS(connectRefp, Const) || VN_IS(connectRefp, VarRef), pinp, - "Unknown interconnect type; pinReconnectSimple should have cleared up"); - V3Inst::checkOutputShort(pinp); - - // Make new signal; even though we'll optimize the interconnect, we - // need an alias to trace correctly. If tracing is disabled, we'll - // delete it in later optimizations. - AstVar* const pinOldVarp = pinp->modVarp(); - AstVar* const pinNewVarp = lastCell ? pinOldVarp : pinOldVarp->clonep(); - UASSERT_OBJ(pinNewVarp, pinOldVarp, "Cloning failed"); - // Propagate any attributes across the interconnect - pinNewVarp->propagateAttrFrom(pinOldVarp); - if (const AstVarRef* const vrefp = VN_CAST(connectRefp, VarRef)) { - vrefp->varp()->propagateAttrFrom(pinOldVarp); - } - - // One to one interconnect won't make a temporary variable. - // This prevents creating a lot of extra wires for clock signals. - // It will become a tracing alias. - UINFO(6, "One-to-one " << connectRefp); - UINFO(6, " -to " << pinNewVarp); - pinNewVarp->user2p(connectRefp); - // Public output inside the cell must go via an assign rather - // than alias. Else the public logic will set the alias, losing - // the value to be propagated up (InOnly isn't a problem as the - // AssignAlias will create the assignment for us) - pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() - && pinNewVarp->direction() == VDirection::OUTPUT); - } - // Cleanup var names, etc, to not conflict - { InlineRelinkVisitor{newmodp, m_modp, nodep}; } - // Move statements under the module we are inlining into - if (AstNode* const stmtsp = newmodp->stmtsp()) { - stmtsp->unlinkFrBackWithNext(); - m_modp->addStmtsp(stmtsp); - } - // Clear any leftover ports, etc - VL_DO_DANGLING(newmodp->deleteTree(), newmodp); - // Remove the cell we just inlined - nodep->unlinkFrBack(); - VL_DO_DANGLING(pushDeletep(nodep), nodep); + // They will become the same variable, so propagate file-line and variable attributes + pinRefp->varp()->fileline()->modifyStateInherit(flp); + flp->modifyStateInherit(pinRefp->varp()->fileline()); + pinRefp->varp()->propagateAttrFrom(nodep); + nodep->propagateAttrFrom(pinRefp->varp()); + return; } - // VISITORS - void visit(AstNetlist* nodep) override { - // Iterate modules backwards, in bottom-up order. Required! - iterateAndNextConstNullBackwards(nodep->modulesp()); - // Clean up AstIfaceRefDType references - iterateChildren(nodep->typeTablep()); + // Otherwise create the continuous assignment between the port var and the pin expression + UINFO(6, "Not inlning port variable: " << nodep); + if (nodep->direction() == VDirection::INPUT) { + modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); + } else if (nodep->direction() == VDirection::OUTPUT) { + modp->addStmtsp(new AstAssignW{flp, pinRef(VAccess::WRITE), portRef(VAccess::READ)}); + } else { + pinExprp->v3fatalSrc("V3Tristate left INOUT port"); // LCOV_EXCL_LINE } - void visit(AstNodeModule* nodep) override { - UASSERT_OBJ(!m_modp, nodep, "Unsupported: Nested modules"); - VL_RESTORER(m_modp); - m_modp = nodep; - // Iterate the stored cells directly to reduce traversal - for (AstCell* const cellp : m_moduleState(nodep).m_childCells) { - if (m_moduleState(cellp->modp()).m_inlined) inlineCell(cellp); - } - m_moduleState(nodep).m_childCells.clear(); +} + +// Inline 'cellp' into 'modp'. 'last' indicatest this is tha last instance of the inlined module +void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) { + UINFO(5, " Inline Cell " << cellp); + UINFO(5, " into Module " << modp); + + const VNUser2InUse user2InUse; + + // Important: If this is the last cell, then don't clone the instantiated module but + // inline the original directly. While this requires some special casing, doing so + // saves us having to temporarily clone the module for the last cell, which + // significantly reduces Verilator memory usage. This is especially true as often the + // top few levels of the hierarchy are singleton wrapper modules, which we always + // inline. In this case this special casing saves us from having to clone essentially + // the entire netlist, which would in effect double Verilator memory consumption, or + // worse if we put off deleting the inlined modules until the end. Not having to clone + // large trees also improves speed. + + // The module we will yank the contents out of and put into 'modp' + AstNodeModule* const inlinedp = last ? cellp->modp()->unlinkFrBack() // + : cellp->modp()->cloneTree(false); + + // Compute map from original port variables and cells to their clones + std::unordered_map modVar2Clone; + for (AstNode *ap = cellp->modp()->stmtsp(), *bp = inlinedp->stmtsp(); ap || bp; + ap = ap->nextp(), bp = bp->nextp()) { + UASSERT_OBJ(ap && bp, ap ? ap : bp, "Clone has different number of children"); + // We only care about AstVar and AstCell, but faster to just set them all + ap->user3p(bp); } - void visit(AstIfaceRefDType* nodep) override { - if (nodep->user1()) { - // The cell has been removed so let's make sure we don't leave a reference to it - // This dtype may still be in use by the AstAssignVarScope created earlier - // but that'll get cleared up later - nodep->cellp(nullptr); + + // Create data for resolving hierarchical references later. + modp->addInlinesp(new AstCellInline{cellp->fileline(), cellp->name(), + cellp->modp()->origName(), cellp->modp()->timeunit()}); + + // Connect the pins on the instance + for (AstPin* pinp = cellp->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (!pinp->exprp()) continue; + UINFO(6, "Conecting port " << pinp->modVarp()); + UINFO(6, " of instance " << cellp); + + // Make sure the conneccted pin expression is always a VarRef or a Const + V3Inst::pinReconnectSimple(pinp, cellp, false); + + // Warn + V3Inst::checkOutputShort(pinp); + + // Pick up the old and new port variables signal (new is the same on last instance) + const AstVar* const oldModVarp = pinp->modVarp(); + AstVar* const newModVarp = VN_AS(oldModVarp->user3p(), Var); + // Pick up the connected expression (a VarRef or Const due to pinReconnectSimple) + AstNodeExpr* const pinExprp = VN_AS(pinp->exprp(), NodeExpr); + + // Connect up the port + connectPort(modp, newModVarp, pinExprp); + } + + // Cleanup var names, etc, to not conflict, relink replaced variables + { InlineRelinkVisitor{inlinedp, modp, cellp}; } + // Move statements from the inlined module into the module we are inlining into + if (AstNode* const stmtsp = inlinedp->stmtsp()) { + modp->addStmtsp(stmtsp->unlinkFrBackWithNext()); + } + // Delete the empty shell of the inlined module + VL_DO_DANGLING(inlinedp->deleteTree(), inlinedp); + // Remove the cell we just inlined + VL_DO_DANGLING(cellp->unlinkFrBack(), cellp); +} + +// Apply all inlining decisions +void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) { + // NODE STATE + // Input: + // AstNodeModule::user1p() // ModuleState instance (via moduleState) + // + // Cleared entire netlist + // AstIfaceRefDType::user1() // Whether the cell pointed to by this + // // AstIfaceRefDType has been inlined + // AstCell::user3p() // AstCell*, the clone + // AstVar::user3p() // AstVar*, the clone clone + // Cleared each cell + // AstVar::user2p() // AstVarRef*/AstConst* This port is connected to (AstPin::expr()) + const VNUser3InUse m_user3InUse; + + // Number of inlined instances, for statistics + VDouble0 m_nInlined; + + // We want to inline bottom up. The modules under the netlist are in + // dependency order (top first, leaves last), so find the end of the list. + AstNode* nodep = netlistp->modulesp(); + while (nodep->nextp()) nodep = nodep->nextp(); + + // Iterate module list backwards (stop when we get back to the Netlist) + while (AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) { + nodep = nodep->backp(); + + // Consider each cell inside the current module for inling + for (AstCell* const cellp : moduleStates(modp).m_childCells) { + ModuleState& childState = moduleStates(cellp->modp()); + if (!childState.m_inlined) continue; + ++m_nInlined; + inlineCell(modp, cellp, --childState.m_cellRefs == 0); } } - //-------------------- - void visit(AstCell* nodep) override { // LCOV_EXCL_START - nodep->v3fatalSrc("Traversal should have been short circuited"); - } - void visit(AstNodeStmt* nodep) override { - nodep->v3fatalSrc("Traversal should have been short circuited"); - } // LCOV_EXCL_STOP - void visit(AstNodeFile*) override {} // Accelerate - void visit(AstNodeDType*) override {} // Accelerate - void visit(AstNode* nodep) override { iterateChildren(nodep); } + V3Stats::addStat("Optimizations, Inlined instances", m_nInlined); -public: - // CONSTRUCTORS - explicit InlineVisitor(AstNode* nodep, ModuleStateUser1Allocator& moduleState) - : m_moduleState{moduleState} { - iterate(nodep); - } - ~InlineVisitor() override { - V3Stats::addStat("Optimizations, Inlined instances", m_statCells); - } -}; + // Clean up AstIfaceRefDType references + // If the cell has been removed let's make sure we don't leave a + // reference to it. This dtype may still be in use by the + // AstAssignVarScope created earlier but that'll get cleared up later + netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) { + if (nodep->user1()) nodep->cellp(nullptr); + }); +} + +} //namespace ModuleInliner //###################################################################### -// Inline class functions +// V3Inline class functions void V3Inline::inlineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); @@ -656,7 +653,8 @@ void V3Inline::inlineAll(AstNetlist* nodep) { // Scoped to clean up temp userN's { InlineMarkVisitor{nodep, moduleState}; } - { InlineVisitor{nodep, moduleState}; } + // Inline the modles we decided to inline + ModuleInliner::process(nodep, moduleState); // Check inlined modules have been removed during traversal. Otherwise we might have blown // up Verilator memory consumption. diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index aaa3d87dd..fccaa5709 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -694,7 +694,7 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTri return InstStatic::pinReconnectSimple(pinp, cellp, forTristate, alwaysCvt); } -void V3Inst::checkOutputShort(AstPin* nodep) { +void V3Inst::checkOutputShort(const AstPin* nodep) { if (nodep->modVarp()->direction() == VDirection::OUTPUT) { if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), Extend) || (VN_IS(nodep->exprp(), Concat) diff --git a/src/V3Inst.h b/src/V3Inst.h index 98a82d78d..1b5ffaf67 100644 --- a/src/V3Inst.h +++ b/src/V3Inst.h @@ -33,7 +33,7 @@ public: static void dearrayAll(AstNetlist* nodep) VL_MT_DISABLED; static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate, bool alwaysCvt = false) VL_MT_DISABLED; - static void checkOutputShort(AstPin* nodep) VL_MT_DISABLED; + static void checkOutputShort(const AstPin* nodep) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 9b58281f0..a94d18b69 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -743,7 +743,7 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, ss << "@("; V3EmitV::verilogForTree(senItemp, ss); ss << ")"; - addDebug(triggerNumber, ss.str()); + addDebug(triggerNumber, VString::quoteBackslash(ss.str())); // ++triggerNumber; diff --git a/test_regress/t/t_dist_cppstyle.py b/test_regress/t/t_dist_cppstyle.py index 596bd8dd3..a522fe10c 100755 --- a/test_regress/t/t_dist_cppstyle.py +++ b/test_regress/t/t_dist_cppstyle.py @@ -93,7 +93,7 @@ for filename in sorted(files.keys()): "Use brace instead of parenthesis-style constructors e.g. ': m_...{...}'") if re.search(r'\.(c|cpp)', filename): - check_pattern(filename, contents, r'(\w+\s+)*(inline)[^\n]*', None, + check_pattern(filename, contents, r'(\w+\s+)*(\binline\b)[^\n]*', None, "'inline' keyword is on functions defined in .cpp files") test.passes() diff --git a/test_regress/t/t_dpi_var.cpp b/test_regress/t/t_dpi_var.cpp index 6da1a12e6..77cf84450 100644 --- a/test_regress/t/t_dpi_var.cpp +++ b/test_regress/t/t_dpi_var.cpp @@ -19,25 +19,27 @@ struct MyMon { uint32_t* sigsp[2]; + uint32_t addend = 0; MyMon() { sigsp[0] = NULL; sigsp[1] = NULL; } }; -MyMon mons[2]; +MyMon mons[4]; -void mon_register_a(const char* namep, void* sigp, bool isOut) { +void mon_register_a(const char* namep, void* sigp, bool isOut, int n, int addend) { // Callback from initial block in monitor #ifdef TEST_VERBOSE - VL_PRINTF("- mon_register_a(\"%s\", %p, %d);\n", namep, sigp, isOut); + VL_PRINTF("- mon_register_a(\"%s\", %p, %d, %d, %d);\n", namep, sigp, isOut, n, addend); #endif - mons[0].sigsp[isOut] = (uint32_t*)sigp; + mons[n].sigsp[isOut] = (uint32_t*)sigp; + mons[n].addend = addend; } void mon_do(MyMon* monp) { if (!monp->sigsp[0]) vl_fatal(__FILE__, __LINE__, "", "never registered"); if (!monp->sigsp[1]) vl_fatal(__FILE__, __LINE__, "", "never registered"); - *monp->sigsp[1] = (*(monp->sigsp[0])) + 1; + *monp->sigsp[1] = (*(monp->sigsp[0])) + monp->addend; #ifdef TEST_VERBOSE VL_PRINTF("- mon_do(%08x(&%p) -> %08x(&%p));\n", *(monp->sigsp[0]), monp->sigsp[0], @@ -66,11 +68,10 @@ void mon_scope_name(const char* namep) { vl_fatal(__FILE__, __LINE__, "", ("Unexp dpiscope name "s + modp).c_str()); } -extern "C" void mon_register_b(const char* namep, int isOut); -void mon_register_b(const char* namep, int isOut) { +extern "C" void mon_register_b(const char* namep, int isOut, int n, int addend) { const char* modp = svGetNameFromScope(svGetScope()); #ifdef TEST_VERBOSE - VL_PRINTF("- mon_register_b('%s', \"%s\", %d);\n", modp, namep, isOut); + VL_PRINTF("- mon_register_b('%s', \"%s\", %d, %d %d);\n", modp, namep, isOut, n, addend); #endif // Use scope to get pointer and size of signal const VerilatedScope* scopep = Verilated::dpiScope(); @@ -82,7 +83,8 @@ void mon_register_b(const char* namep, int isOut) { } else { uint32_t* datap = (uint32_t*)(varp->datap()); VL_PRINTF("- mon_register_b('%s', \"%s\", %p, %d);\n", modp, namep, datap, isOut); - mons[1].sigsp[isOut] = (uint32_t*)(varp->datap()); + mons[n].sigsp[isOut] = (uint32_t*)(varp->datap()); + mons[n].addend = addend; } } @@ -106,6 +108,8 @@ void mon_eval() { // Callback from always@ negedge mon_do(&mons[0]); mon_do(&mons[1]); + mon_do(&mons[2]); + mon_do(&mons[3]); } //====================================================================== diff --git a/test_regress/t/t_dpi_var.py b/test_regress/t/t_dpi_var.py index 0cf3bcd6e..b54cadf07 100755 --- a/test_regress/t/t_dpi_var.py +++ b/test_regress/t/t_dpi_var.py @@ -21,19 +21,27 @@ test.compile( if test.vlt_all: test.file_grep( out_filename, - r'{"type":"VAR","name":"formatted",.*"loc":"\w,56:[^"]*",.*"origName":"formatted",.*"direction":"INPUT",.*"dtypeName":"string",.*"attrSFormat":true' + r'{"type":"VAR","name":"formatted",.*"loc":"\w,69:[^"]*",.*"origName":"formatted",.*"direction":"INPUT",.*"dtypeName":"string",.*"attrSFormat":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.in",.*"loc":"\w,77:[^"]*",.*"origName":"in",.*"dtypeName":"int",.*"isSigUserRdPublic":true' + r'{"type":"VAR","name":"t.sub.in",.*"loc":"\w,91:[^"]*",.*"origName":"in",.*"dtypeName":"int",.*"isSigUserRdPublic":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.fr_a",.*"loc":"\w,78:[^"]*",.*"origName":"fr_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + r'{"type":"VAR","name":"t.sub.in_a",.*"loc":"\w,92:[^"]*",.*"origName":"in_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.fr_b",.*"loc":"\w,79:[^"]*",.*"origName":"fr_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + r'{"type":"VAR","name":"t.sub.in_b",.*"loc":"\w,93:[^"]*",.*"origName":"in_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + ) + test.file_grep( + out_filename, + r'{"type":"VAR","name":"t.sub.fr_a",.*"loc":"\w,94:[^"]*",.*"origName":"fr_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + ) + test.file_grep( + out_filename, + r'{"type":"VAR","name":"t.sub.fr_b",.*"loc":"\w,95:[^"]*",.*"origName":"fr_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.execute() diff --git a/test_regress/t/t_dpi_var.v b/test_regress/t/t_dpi_var.v index e5829cb89..45e772c0a 100644 --- a/test_regress/t/t_dpi_var.v +++ b/test_regress/t/t_dpi_var.v @@ -17,27 +17,40 @@ module t (/*AUTOARG*/ wire monclk = ~clk; int in; + int in_a; + int in_b; int fr_a; int fr_b; + int fr_a2; + int fr_b2; int fr_chk; sub sub (.*); // Test loop always @ (posedge clk) begin `ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d in=%x fr_a=%x b=%x fr_chk=%x\n", $time, cyc, in, fr_a, fr_b, fr_chk); + $write("[%0t] cyc==%0d in=%x sub.in_a=%x sub.in_b=%x fr_a=%x fr_b=%x fr_a2=%x fr_b2=%x fr_chk=%x\n", + $time, cyc, in, sub.in_a, sub.in_b, fr_a, fr_b, fr_a2, fr_b2, fr_chk); `endif cyc <= cyc + 1; in <= {in[30:0], in[31]^in[2]^in[0]}; + // The inputs to sub will be updated externally on the neg-edge so these + // don't matter for the result + in_a <= in_a + 1; + in_b <= in_b + 1; if (cyc==0) begin // Setup in <= 32'hd70a4497; + in_a <= 0; + in_b <= 0; end else if (cyc<3) begin end else if (cyc<10) begin if (fr_chk != fr_a) $stop; if (fr_chk != fr_b) $stop; + if (fr_chk != fr_a2) $stop; + if (fr_chk != fr_b2) $stop; end else if (cyc==10) begin $write("*-* All Finished *-*\n"); @@ -57,33 +70,43 @@ import "DPI-C" context function void mon_scope_name (input string formatted /*ve `else import "DPI-C" context function void mon_scope_name (input string formatted); `endif -import "DPI-C" context function void mon_register_b(string name, int isOut); +import "DPI-C" context function void mon_register_b(string name, int isOut, int n, int addend); import "DPI-C" context function void mon_register_done(); import "DPI-C" context function void mon_eval(); module sub (/*AUTOARG*/ // Outputs - fr_a, fr_b, fr_chk, + fr_a, fr_b, fr_a2, fr_b2, fr_chk, // Inputs - in + in, in_a, in_b ); `systemc_imp_header void mon_class_name(const char* namep); - void mon_register_a(const char* namep, void* sigp, bool isOut); + void mon_register_a(const char* namep, void* sigp, bool isOut, int n, int addend); `verilog + /* verilator lint_off ASSIGNIN */ `ifdef ATTRIBUTES input int in /*verilator public_flat_rd*/; + input int in_a /*verilator public_flat_rw @(posedge t.monclk)*/; + input int in_b /*verilator public_flat_rw @(posedge t.monclk)*/; output int fr_a /*verilator public_flat_rw @(posedge t.monclk)*/; output int fr_b /*verilator public_flat_rw @(posedge t.monclk)*/; `else input int in; + input int in_a; + input int in_b; output int fr_a; output int fr_b; `endif + output int fr_a2; + output int fr_b2; output int fr_chk; + /* verilator lint_on ASSIGNIN */ + always @* fr_a2 = in_a + 1; + always @* fr_b2 = in_b + 1; always @* fr_chk = in + 1; initial begin @@ -91,11 +114,15 @@ module sub (/*AUTOARG*/ $c("mon_class_name(this->name());"); mon_scope_name("%m"); // Scheme A - pass pointer directly - $c("mon_register_a(\"in\", &", in, ", false);"); - $c("mon_register_a(\"fr_a\", &", fr_a, ", true);"); + $c("mon_register_a(\"in\", &", in, ", false, 0, 1);"); + $c("mon_register_a(\"fr_a\", &", fr_a, ", true, 0, 1);"); + $c("mon_register_a(\"in\", &", in, ", false, 1, 0);"); + $c("mon_register_a(\"in_a\", &", in_a, ", true, 1, 0);"); // Scheme B - use VPIish callbacks to see what signals exist - mon_register_b("in", 0); - mon_register_b("fr_b", 1); + mon_register_b("in", 0, 2, 1); + mon_register_b("fr_b", 1, 2, 1); + mon_register_b("in", 0, 3, 0); + mon_register_b("in_b", 1, 3, 0); mon_register_done(); end diff --git a/test_regress/t/t_dpi_var.vlt b/test_regress/t/t_dpi_var.vlt index 70216609d..e5fb1d038 100644 --- a/test_regress/t/t_dpi_var.vlt +++ b/test_regress/t/t_dpi_var.vlt @@ -8,5 +8,7 @@ sformat -task "mon_scope_name" -var "formatted" public_flat_rd -module "sub" -var "in" +public_flat_rw -module "sub" -var "in_a" @(posedge t.monclk) +public_flat_rw -module "sub" -var "in_b" @(posedge t.monclk) public_flat_rw -module "sub" -var "fr_a" @(posedge t.monclk) public_flat_rw -module "sub" -var "fr_b" @(posedge t.monclk) diff --git a/test_regress/t/t_force_port_alias.v b/test_regress/t/t_force_port_alias.v deleted file mode 100644 index d4bff0986..000000000 --- a/test_regress/t/t_force_port_alias.v +++ /dev/null @@ -1,61 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2024 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 sub( - // Outputs - out, - // Inputs - clk - ); - // verilator inline_module - - output [3:0] out /* <-- this variable has to be marked as having external refs */; - input clk; - - reg [3:0] r; - - always @ (posedge clk) - r <= 4'h1; - assign out = r; -endmodule - -module t(/*AUTOARG*/ - // Inputs - clk - ); - - input clk; - reg [3:0] unused; - - sub sub1(unused, clk); - - integer cyc = 0; - - always @ (posedge clk) begin - cyc <= cyc + 1; - if (cyc == 1) begin - `checkh(sub1.r, 4'h1); - `checkh(sub1.out, 4'h1); - end - else if (cyc == 2) begin - force sub1.r = 4'h2; - force sub1.out = 4'h3; - end - else if (cyc == 3) begin - `checkh(sub1.r, 4'h2); - `checkh(sub1.out, 4'h3); - end - // - else if (cyc == 99) begin - $write("*-* All Finished *-*\n"); - $finish; - end - end - -endmodule diff --git a/test_regress/t/t_force_port_alias.py b/test_regress/t/t_force_port_inline.py similarity index 91% rename from test_regress/t/t_force_port_alias.py rename to test_regress/t/t_force_port_inline.py index cacc314ab..587a5d18f 100755 --- a/test_regress/t/t_force_port_alias.py +++ b/test_regress/t/t_force_port_inline.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile() +test.compile(verilator_flags2=['--binary']) test.execute() diff --git a/test_regress/t/t_force_port_inline.v b/test_regress/t/t_force_port_inline.v new file mode 100644 index 000000000..062ddb88c --- /dev/null +++ b/test_regress/t/t_force_port_inline.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 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 sub( + input wire [7:0] i, + output wire [7:0] o +); + // Must inline this module + // verilator inline_module + + wire [7:0] m; + assign m = i; + assign o = m; +endmodule + +module top; + // Variable input + reg [7:0] i = 8'h01; + reg [7:0] o_v; + sub sub_v(i, o_v); + + // Constant input + reg [7:0] o_c; + sub sub_c(8'h10, o_c); + + logic clk = 1'b0; + always #1 clk = ~clk; + int cyc = 0; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + `checkh(i, 8'h01); + `checkh(sub_v.i, 8'h01); + `checkh(sub_v.m, 8'h01); + `checkh(sub_v.o, 8'h01); + `checkh(o_v, 8'h01); + `checkh(sub_c.i, 8'h10); + `checkh(sub_c.m, 8'h10); + `checkh(sub_c.o, 8'h10); + `checkh(o_c, 8'h10); + end + else if (cyc == 2) begin + force sub_v.i = 8'h02; + force sub_v.m = 8'h03; + force sub_v.o = 8'h04; + force sub_c.i = 8'h20; + force sub_c.m = 8'h30; + force sub_c.o = 8'h40; + end + else if (cyc == 3) begin + `checkh(i, 8'h01); + `checkh(sub_v.i, 8'h02); + `checkh(sub_v.m, 8'h03); + `checkh(sub_v.o, 8'h04); + `checkh(o_v, 8'h04); + `checkh(sub_c.i, 8'h20); + `checkh(sub_c.m, 8'h30); + `checkh(sub_c.o, 8'h40); + `checkh(o_c, 8'h40); + end + // + else if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_inst_tree_inl0_pub1.py b/test_regress/t/t_inst_tree_inl0_pub1.py index 5aca960d1..9d89a7285 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.py +++ b/test_regress/t/t_inst_tree_inl0_pub1.py @@ -42,7 +42,7 @@ if test.vlt_all: # We expect to combine sequent functions across multiple instances of # l2, l3, l4, l5. If this number drops, please confirm this has not broken. test.file_grep(test.stats, r'Optimizations, Combined CFuncs\s+(\d+)', - (107 if test.vltmt else 114)) + (99 if test.vltmt else 82)) # Everything should use relative references check_relative_refs("t", True) diff --git a/test_regress/t/t_json_only_flat.out b/test_regress/t/t_json_only_flat.out index 6ce3beadd..7f48ce13a 100644 --- a/test_regress/t/t_json_only_flat.out +++ b/test_regress/t/t_json_only_flat.out @@ -65,26 +65,26 @@ "lhsp": [ {"type":"VARREF","name":"t.d","addr":"(YB)","loc":"d,14:16,14:17","dtypep":"(H)","access":"WR","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(ZB)","loc":"d,34:24,34:27","dtypep":"(J)", + {"type":"ASSIGNALIAS","name":"","addr":"(ZB)","loc":"d,36:30,36:31","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.clk","addr":"(AC)","loc":"d,21:42,21:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(AC)","loc":"d,20:14,20:21","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.clk","addr":"(BC)","loc":"d,34:24,34:27","dtypep":"(J)","access":"WR","varp":"(S)","varScopep":"(JB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.q","addr":"(BC)","loc":"d,36:30,36:31","dtypep":"(H)","access":"WR","varp":"(U)","varScopep":"(LB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(CC)","loc":"d,35:30,35:31","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(CC)","loc":"d,34:24,34:27","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"t.d","addr":"(DC)","loc":"d,22:42,22:43","dtypep":"(H)","access":"RD","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.clk","addr":"(DC)","loc":"d,21:42,21:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.d","addr":"(EC)","loc":"d,35:30,35:31","dtypep":"(H)","access":"WR","varp":"(T)","varScopep":"(KB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.clk","addr":"(EC)","loc":"d,34:24,34:27","dtypep":"(J)","access":"WR","varp":"(S)","varScopep":"(JB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(FC)","loc":"d,36:30,36:31","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(FC)","loc":"d,35:30,35:31","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(GC)","loc":"d,20:14,20:21","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.d","addr":"(GC)","loc":"d,22:42,22:43","dtypep":"(H)","access":"RD","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.q","addr":"(HC)","loc":"d,36:30,36:31","dtypep":"(H)","access":"WR","varp":"(U)","varScopep":"(LB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.d","addr":"(HC)","loc":"d,35:30,35:31","dtypep":"(H)","access":"WR","varp":"(T)","varScopep":"(KB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"ALWAYS","name":"","addr":"(IC)","loc":"d,41:4,41:10","keyword":"always","isSuspendable":false,"needProcess":false, "sentreep": [ @@ -105,26 +105,26 @@ {"type":"VARREF","name":"t.between","addr":"(OC)","loc":"d,42:6,42:7","dtypep":"(H)","access":"WR","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ]}, - {"type":"ASSIGNALIAS","name":"","addr":"(PC)","loc":"d,48:10,48:13","dtypep":"(J)", + {"type":"ASSIGNALIAS","name":"","addr":"(PC)","loc":"d,49:16,49:17","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.clk","addr":"(QC)","loc":"d,27:42,27:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(QC)","loc":"d,25:16,25:23","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.clk","addr":"(RC)","loc":"d,48:10,48:13","dtypep":"(J)","access":"WR","varp":"(X)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.d","addr":"(RC)","loc":"d,49:16,49:17","dtypep":"(H)","access":"WR","varp":"(Y)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(SC)","loc":"d,49:16,49:17","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(SC)","loc":"d,50:22,50:23","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(TC)","loc":"d,25:16,25:23","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.q","addr":"(TC)","loc":"d,26:42,26:43","dtypep":"(H)","access":"RD","varp":"(L)","varScopep":"(EB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.d","addr":"(UC)","loc":"d,49:16,49:17","dtypep":"(H)","access":"WR","varp":"(Y)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.q","addr":"(UC)","loc":"d,50:22,50:23","dtypep":"(H)","access":"WR","varp":"(Z)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(VC)","loc":"d,50:22,50:23","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(VC)","loc":"d,48:10,48:13","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"t.q","addr":"(WC)","loc":"d,26:42,26:43","dtypep":"(H)","access":"RD","varp":"(L)","varScopep":"(EB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.clk","addr":"(WC)","loc":"d,27:42,27:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.q","addr":"(XC)","loc":"d,50:22,50:23","dtypep":"(H)","access":"WR","varp":"(Z)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.clk","addr":"(XC)","loc":"d,48:10,48:13","dtypep":"(J)","access":"WR","varp":"(X)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"ASSIGNW","name":"","addr":"(YC)","loc":"d,53:13,53:14","dtypep":"(H)", "rhsp": [ diff --git a/test_regress/t/t_var_in_assign_pedantic.py b/test_regress/t/t_var_in_assign_pedantic.py index 7b97942aa..778ba42c8 100755 --- a/test_regress/t/t_var_in_assign_pedantic.py +++ b/test_regress/t/t_var_in_assign_pedantic.py @@ -12,6 +12,8 @@ import vltest_bootstrap test.scenarios('vlt') test.top_filename = "t/t_var_in_assign_bad.v" -test.lint(verilator_flags2=['-Wpedantic -Wno-fatal']) +# Although this is mostly a lint test, do 'compile' to make sure we do not +# generate thrash code in the presence of a warning that is not fatal +test.compile(verilator_flags2=['-Wpedantic -Wno-fatal --flatten -fno-gate']) test.passes() diff --git a/test_regress/t/t_xml_flat.out b/test_regress/t/t_xml_flat.out index df60ede82..45d3b91cd 100644 --- a/test_regress/t/t_xml_flat.out +++ b/test_regress/t/t_xml_flat.out @@ -63,6 +63,10 @@ + + + + @@ -71,10 +75,6 @@ - - - - @@ -86,10 +86,6 @@ - - - - @@ -98,6 +94,10 @@ + + + + From d56d1a7719599be993f7c5a3c0c891acf28e502a Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Sat, 23 Aug 2025 16:37:40 +0200 Subject: [PATCH 216/252] Fix queue extend to check bounds (#6324) --- include/verilated_types.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/verilated_types.h b/include/verilated_types.h index 0fa30d2ca..cfd1e47ee 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -621,11 +621,8 @@ public: T_Value& atWriteAppend(int32_t index) { // cppcheck-suppress variableScope static thread_local T_Value t_throwAway; + if (index == m_deque.size()) push_back(atDefault()); if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - if (index == m_deque.size()) { - push_back(atDefault()); - return m_deque[index]; - } t_throwAway = atDefault(); return t_throwAway; } From 34315a4f7098c7d05eff311f70fdff70c66afc40 Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Sat, 23 Aug 2025 16:38:18 +0200 Subject: [PATCH 217/252] Fix FreeBSD missing headers (#6326) * Include missing libgen.h * Include missing cinttypes --- test_regress/t/TestVpiMain.cpp | 4 ++++ test_regress/t/t_time_vpi_c.cpp | 4 ++++ test_regress/t/t_vpi_cb_iter.cpp | 4 ++++ test_regress/t/t_vpi_const_type.cpp | 4 ++++ test_regress/t/t_vpi_dump.cpp | 4 ++++ test_regress/t/t_vpi_escape.cpp | 4 ++++ test_regress/t/t_vpi_get.cpp | 4 ++++ test_regress/t/t_vpi_get_value_array.cpp | 4 ++++ test_regress/t/t_vpi_memory.cpp | 4 ++++ test_regress/t/t_vpi_module.cpp | 4 ++++ test_regress/t/t_vpi_module_empty.cpp | 4 ++++ test_regress/t/t_vpi_multidim.cpp | 5 +++++ test_regress/t/t_vpi_onetime_cbs.cpp | 5 +++++ test_regress/t/t_vpi_package.cpp | 4 ++++ test_regress/t/t_vpi_param.cpp | 4 ++++ test_regress/t/t_vpi_public_depth.cpp | 4 ++++ test_regress/t/t_vpi_put_value_array.cpp | 4 ++++ test_regress/t/t_vpi_repetitive_cbs.cpp | 4 ++++ test_regress/t/t_vpi_time_cb_c.cpp | 4 ++++ test_regress/t/t_vpi_unimpl.cpp | 4 ++++ test_regress/t/t_vpi_var.cpp | 4 ++++ test_regress/t/t_vpi_zero_time_cb.cpp | 4 ++++ 22 files changed, 90 insertions(+) diff --git a/test_regress/t/TestVpiMain.cpp b/test_regress/t/TestVpiMain.cpp index e51323177..b72127d15 100644 --- a/test_regress/t/TestVpiMain.cpp +++ b/test_regress/t/TestVpiMain.cpp @@ -21,6 +21,10 @@ #include #include +extern "C" { +#include +} + #ifndef VM_TRACE_FST // emulate new verilator behavior for legacy versions #define VM_TRACE_FST 0 diff --git a/test_regress/t/t_time_vpi_c.cpp b/test_regress/t/t_time_vpi_c.cpp index 86ab6a5ef..8063f5bbf 100644 --- a/test_regress/t/t_time_vpi_c.cpp +++ b/test_regress/t/t_time_vpi_c.cpp @@ -15,6 +15,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_cb_iter.cpp b/test_regress/t/t_vpi_cb_iter.cpp index d98fe968c..b5af77a6f 100644 --- a/test_regress/t/t_vpi_cb_iter.cpp +++ b/test_regress/t/t_vpi_cb_iter.cpp @@ -22,6 +22,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_const_type.cpp b/test_regress/t/t_vpi_const_type.cpp index 5efd97175..04812fb54 100644 --- a/test_regress/t/t_vpi_const_type.cpp +++ b/test_regress/t/t_vpi_const_type.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_dump.cpp b/test_regress/t/t_vpi_dump.cpp index cf2147e54..b3c7a7c3c 100644 --- a/test_regress/t/t_vpi_dump.cpp +++ b/test_regress/t/t_vpi_dump.cpp @@ -14,6 +14,10 @@ #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_escape.cpp b/test_regress/t/t_vpi_escape.cpp index 85acca39b..3018a20ea 100644 --- a/test_regress/t/t_vpi_escape.cpp +++ b/test_regress/t/t_vpi_escape.cpp @@ -27,6 +27,10 @@ #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 7a9e5711f..a3a802514 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_get_value_array.cpp b/test_regress/t/t_vpi_get_value_array.cpp index 621d1ac62..c1f3bcc5e 100644 --- a/test_regress/t/t_vpi_get_value_array.cpp +++ b/test_regress/t/t_vpi_get_value_array.cpp @@ -18,6 +18,10 @@ #endif +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index 857ee2d90..c0268c4ab 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_module.cpp b/test_regress/t/t_vpi_module.cpp index a0a3c030f..886487fa7 100644 --- a/test_regress/t/t_vpi_module.cpp +++ b/test_regress/t/t_vpi_module.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_module_empty.cpp b/test_regress/t/t_vpi_module_empty.cpp index f08ebb27d..dd79c521a 100644 --- a/test_regress/t/t_vpi_module_empty.cpp +++ b/test_regress/t/t_vpi_module_empty.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_multidim.cpp b/test_regress/t/t_vpi_multidim.cpp index 8a2f155a0..79fbb3bf3 100644 --- a/test_regress/t/t_vpi_multidim.cpp +++ b/test_regress/t/t_vpi_multidim.cpp @@ -28,12 +28,17 @@ #endif #include +#include #include #include #include #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_onetime_cbs.cpp b/test_regress/t/t_vpi_onetime_cbs.cpp index 4adf77969..c9945ab9c 100644 --- a/test_regress/t/t_vpi_onetime_cbs.cpp +++ b/test_regress/t/t_vpi_onetime_cbs.cpp @@ -27,6 +27,11 @@ #endif +extern "C" { +#include +} + +// These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_package.cpp b/test_regress/t/t_vpi_package.cpp index c36b28f1e..93360131f 100644 --- a/test_regress/t/t_vpi_package.cpp +++ b/test_regress/t/t_vpi_package.cpp @@ -31,6 +31,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_param.cpp b/test_regress/t/t_vpi_param.cpp index d5700b10a..9ce832fbd 100644 --- a/test_regress/t/t_vpi_param.cpp +++ b/test_regress/t/t_vpi_param.cpp @@ -38,6 +38,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_public_depth.cpp b/test_regress/t/t_vpi_public_depth.cpp index e1bf6e2a6..4d9b9971b 100644 --- a/test_regress/t/t_vpi_public_depth.cpp +++ b/test_regress/t/t_vpi_public_depth.cpp @@ -39,6 +39,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_put_value_array.cpp b/test_regress/t/t_vpi_put_value_array.cpp index b8f8c65a7..6e6294267 100644 --- a/test_regress/t/t_vpi_put_value_array.cpp +++ b/test_regress/t/t_vpi_put_value_array.cpp @@ -18,6 +18,10 @@ #endif +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_repetitive_cbs.cpp b/test_regress/t/t_vpi_repetitive_cbs.cpp index 1ed4e48f6..107a77629 100644 --- a/test_regress/t/t_vpi_repetitive_cbs.cpp +++ b/test_regress/t/t_vpi_repetitive_cbs.cpp @@ -29,6 +29,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestSimulator.h" #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_time_cb_c.cpp b/test_regress/t/t_vpi_time_cb_c.cpp index 4f69d232c..00e50b5fe 100644 --- a/test_regress/t/t_vpi_time_cb_c.cpp +++ b/test_regress/t/t_vpi_time_cb_c.cpp @@ -16,6 +16,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_unimpl.cpp b/test_regress/t/t_vpi_unimpl.cpp index 732b30d53..fa6f5291f 100644 --- a/test_regress/t/t_vpi_unimpl.cpp +++ b/test_regress/t/t_vpi_unimpl.cpp @@ -19,6 +19,10 @@ #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestVpi.h" diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 91b15b185..020961907 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -44,6 +44,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" diff --git a/test_regress/t/t_vpi_zero_time_cb.cpp b/test_regress/t/t_vpi_zero_time_cb.cpp index e19bd7f38..fe2d183fa 100644 --- a/test_regress/t/t_vpi_zero_time_cb.cpp +++ b/test_regress/t/t_vpi_zero_time_cb.cpp @@ -32,6 +32,10 @@ #include #include +extern "C" { +#include +} + // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" #include "TestSimulator.h" From 353a2e3d2039920743e42f4baff5a01c5729d1c2 Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Sat, 23 Aug 2025 16:45:13 +0200 Subject: [PATCH 218/252] Fix gathering senaitivities from virtual interface members (#6325) --- src/V3Sched.cpp | 53 ++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index a94d18b69..d7abb1eec 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -104,17 +104,20 @@ void invertAndMergeSenTreeMap( for (const auto& pair : senTreeMap) result.emplace(pair.second, pair.first); } -AstSenTree* findTriggeredIface(const AstVarScope* vscp, - const VirtIfaceTriggers::IfaceSensMap& vifTrigged, - const VirtIfaceTriggers::IfaceMemberSensMap& vifMemberTriggered) { +std::vector +findTriggeredIface(const AstVarScope* vscp, const VirtIfaceTriggers::IfaceSensMap& vifTrigged, + const VirtIfaceTriggers::IfaceMemberSensMap& vifMemberTriggered) { UASSERT_OBJ(vscp->varp()->sensIfacep(), vscp, "Not an virtual interface trigger"); + std::vector result; const auto ifaceIt = vifTrigged.find(vscp->varp()->sensIfacep()); - if (ifaceIt != vifTrigged.end()) return ifaceIt->second; + if (ifaceIt != vifTrigged.end()) result.push_back(ifaceIt->second); for (const auto& memberIt : vifMemberTriggered) { - if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) return memberIt.second; + if (vscp->varp()->sensIfacep() == memberIt.first.m_ifacep) { + result.push_back(memberIt.second); + } } - vscp->v3fatalSrc("Did not find virtual interface trigger"); - return nullptr; // unreachable, appease MSVC + if (result.empty()) vscp->v3fatalSrc("Did not find virtual interface trigger"); + return result; } //============================================================================ @@ -974,20 +977,20 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp, = virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp); // Create and Order the body function - AstCFunc* const icoFuncp - = V3Order::order(netlistp, {&logic}, trigToSen, "ico", false, false, - [=](const AstVarScope* vscp, std::vector& out) { - AstVar* const varp = vscp->varp(); - if (varp->isPrimaryInish() || varp->isSigUserRWPublic()) { - out.push_back(inputChanged); - } - if (varp->isWrittenByDpi()) out.push_back(dpiExportTriggered); - if (vscp->varp()->sensIfacep()) { - AstSenTree* ifaceTriggered = findTriggeredIface( - vscp, vifTriggeredIco, vifMemberTriggeredIco); - out.push_back(ifaceTriggered); - } - }); + AstCFunc* const icoFuncp = V3Order::order( + netlistp, {&logic}, trigToSen, "ico", false, false, + [=](const AstVarScope* vscp, std::vector& out) { + AstVar* const varp = vscp->varp(); + if (varp->isPrimaryInish() || varp->isSigUserRWPublic()) { + out.push_back(inputChanged); + } + if (varp->isWrittenByDpi()) out.push_back(dpiExportTriggered); + if (vscp->varp()->sensIfacep()) { + std::vector ifaceTriggered + = findTriggeredIface(vscp, vifTriggeredIco, vifMemberTriggeredIco); + out.insert(out.end(), ifaceTriggered.begin(), ifaceTriggered.end()); + } + }); splitCheck(icoFuncp); // Create the eval loop @@ -1397,9 +1400,9 @@ void schedule(AstNetlist* netlistp) { if (it != actTimingDomains.end()) out = it->second; if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredAct); if (vscp->varp()->sensIfacep()) { - AstSenTree* ifaceTriggered + std::vector ifaceTriggered = findTriggeredIface(vscp, vifTriggeredAct, vifMemberTriggeredAct); - out.push_back(ifaceTriggered); + out.insert(out.end(), ifaceTriggered.begin(), ifaceTriggered.end()); } }); splitCheck(actFuncp); @@ -1438,9 +1441,9 @@ void schedule(AstNetlist* netlistp) { if (it != timingDomains.end()) out = it->second; if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggered); if (vscp->varp()->sensIfacep()) { - AstSenTree* ifaceTriggered + std::vector ifaceTriggered = findTriggeredIface(vscp, vifTriggered, vifMemberTriggered); - out.push_back(ifaceTriggered); + out.insert(out.end(), ifaceTriggered.begin(), ifaceTriggered.end()); } }); From a9aa2f11b804a6dc154e9ed022b19ef71ed0f2fc Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Sat, 23 Aug 2025 16:49:03 +0200 Subject: [PATCH 219/252] Improve testing on FreeBSD (#6328) * Skip profiling tests on non-glibc platforms * Enforce dumb terminal in tests * Include POSIX headers whenever __unix__ macro is defined * Treat no procfs as normal condition * Respect MAKE variable when running make --- include/verilated.cpp | 2 +- test_regress/driver.py | 1 + test_regress/t/t_ccache_report.py | 4 ++-- test_regress/t/t_dist_lint_py.py | 2 +- test_regress/t/t_gantt_numa.py | 2 +- test_regress/t/t_prof.py | 2 ++ test_regress/t/t_prof_timing.py | 2 ++ test_regress/t/t_profc.py | 6 ++++++ test_regress/t/t_trace_two_dump_cc.py | 8 +++++--- test_regress/t/t_trace_two_dump_sc.py | 5 ++++- test_regress/t/t_trace_two_dumpfst_cc.py | 5 ++++- test_regress/t/t_trace_two_hdr_cc.py | 5 ++++- test_regress/t/t_trace_two_hdr_sc.py | 5 ++++- test_regress/t/t_trace_two_hdrfst_cc.py | 8 +++++--- test_regress/t/t_trace_two_port_cc.py | 5 ++++- test_regress/t/t_trace_two_port_sc.py | 5 ++++- test_regress/t/t_trace_two_portfst_cc.py | 8 +++++--- 17 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index a43b00c6b..c3f44dbde 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -72,7 +72,7 @@ # include # define _VL_HAVE_STACKTRACE #endif -#if defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) # include # include # define _VL_HAVE_GETRLIMIT diff --git a/test_regress/driver.py b/test_regress/driver.py index 5c6f2d8de..6c0573b0a 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -2809,6 +2809,7 @@ if __name__ == '__main__': if 'TEST_REGRESS' in os.environ: sys.exit("%Error: TEST_REGRESS environment variable is already set") os.environ['TEST_REGRESS'] = os.getcwd() + os.environ['TERM'] = "dumb" Start = time.time() _Parameter_Next_Level = None diff --git a/test_regress/t/t_ccache_report.py b/test_regress/t/t_ccache_report.py index 40d5882b9..6109fa46b 100755 --- a/test_regress/t/t_ccache_report.py +++ b/test_regress/t/t_ccache_report.py @@ -31,8 +31,8 @@ test.files_identical(report, "t/" + test.name + "__ccache_report_initial.out") # Now rebuild again (should be all up to date) test.run(logfile=test.obj_dir + "/rebuild.log", cmd=[ - "make", "-C " + test.obj_dir, "-f " + test.vm_prefix + ".mk", test.vm_prefix, - "ccache-report" + os.environ["MAKE"], "-C " + test.obj_dir, "-f " + test.vm_prefix + ".mk", + test.vm_prefix, "ccache-report" ]) test.files_identical(report, "t/" + test.name + "__ccache_report_rebuild.out") diff --git a/test_regress/t/t_dist_lint_py.py b/test_regress/t/t_dist_lint_py.py index 8c8b9a2f6..f011fb1f8 100755 --- a/test_regress/t/t_dist_lint_py.py +++ b/test_regress/t/t_dist_lint_py.py @@ -18,6 +18,6 @@ if 'VERILATOR_TEST_NO_LINT_PY' in os.environ: if not os.path.exists(root + "/.git"): test.skip("Not in a git repository") -test.run(cmd=["cd " + root + " && make lint-py"]) +test.run(cmd=["cd " + root + " && " + os.environ["MAKE"] + " lint-py"]) test.passes() diff --git a/test_regress/t/t_gantt_numa.py b/test_regress/t/t_gantt_numa.py index 39651ecf0..db25d0d87 100755 --- a/test_regress/t/t_gantt_numa.py +++ b/test_regress/t/t_gantt_numa.py @@ -38,7 +38,7 @@ for trial in range(0, trials): ]) test.file_grep(gantt_log, r'CPU info:') - test.file_grep(gantt_log, r'NUMA status += assigned') + test.file_grep(gantt_log, r'NUMA status += (assigned|%Warning: no /proc/cpuinfo)') # False fails occasionally # test.file_grep_not(gantt_log, r'%Warning:') # e.g. There were fewer CPUs (1) than threads (3). diff --git a/test_regress/t/t_prof.py b/test_regress/t/t_prof.py index e5ebf6147..429e6a0ac 100755 --- a/test_regress/t/t_prof.py +++ b/test_regress/t/t_prof.py @@ -14,6 +14,8 @@ test.scenarios('vlt_all') if re.search(r'clang', test.cxx_version) and 'aarch64' in platform.processor(): test.skip("Known compiler profile issues on clang aarch64") +if platform.libc_ver()[0] != "glibc": + test.skip("The test depends on GMON_OUT_PREFIX which is glibc-specific") test.compile(verilator_flags2=["--stats --prof-cfuncs +define+T_PROF"]) diff --git a/test_regress/t/t_prof_timing.py b/test_regress/t/t_prof_timing.py index 59f233265..6f3f7a570 100755 --- a/test_regress/t/t_prof_timing.py +++ b/test_regress/t/t_prof_timing.py @@ -15,6 +15,8 @@ test.top_filename = "t/t_prof.v" if re.search(r'clang', test.cxx_version) and 'aarch64' in platform.processor(): test.skip("Known compiler profile issues on clang aarch64") +if platform.libc_ver()[0] != "glibc": + test.skip("The test depends on GMON_OUT_PREFIX which is glibc-specific") # TODO below might no longer be required as configure checks for -pg if 'VERILATOR_TEST_NO_GPROF' in os.environ: diff --git a/test_regress/t/t_profc.py b/test_regress/t/t_profc.py index 74f928bb9..a70fca363 100755 --- a/test_regress/t/t_profc.py +++ b/test_regress/t/t_profc.py @@ -8,10 +8,16 @@ # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap +import platform test.scenarios('vlt') test.top_filename = "t_prof.v" +if re.search(r'clang', test.cxx_version) and 'aarch64' in platform.processor(): + test.skip("Known compiler profile issues on clang aarch64") +if platform.libc_ver()[0] != "glibc": + test.skip("The test depends on GMON_OUT_PREFIX which is glibc-specific") + test.compile(verilator_flags2=["--stats --prof-c +define+T_PROF"]) # TODO below might no longer be required as configure checks for -pg diff --git a/test_regress/t/t_trace_two_dump_cc.py b/test_regress/t/t_trace_two_dump_cc.py index 7536b0171..a51b7661e 100755 --- a/test_regress/t/t_trace_two_dump_cc.py +++ b/test_regress/t/t_trace_two_dump_cc.py @@ -20,9 +20,11 @@ test.compile(make_main=False, vm_prefix='Vt_trace_two_b', verilator_flags2=['-trace']) -test.run( - logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) +test.run(logfile=test.obj_dir + "/make_first_ALL.log", + cmd=[ + os.environ["MAKE"], "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dump_sc.py b/test_regress/t/t_trace_two_dump_sc.py index 2f28b7607..00db26b57 100755 --- a/test_regress/t/t_trace_two_dump_sc.py +++ b/test_regress/t/t_trace_two_dump_sc.py @@ -23,7 +23,10 @@ test.compile(make_main=False, verilator_flags2=['-sc -trace']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dumpfst_cc.py b/test_regress/t/t_trace_two_dumpfst_cc.py index 809dbc452..625f3c8ac 100755 --- a/test_regress/t/t_trace_two_dumpfst_cc.py +++ b/test_regress/t/t_trace_two_dumpfst_cc.py @@ -21,7 +21,10 @@ test.compile(make_main=False, verilator_flags2=['--trace-fst --trace-threads 1 -DTEST_FST']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile( make_main=False, diff --git a/test_regress/t/t_trace_two_hdr_cc.py b/test_regress/t/t_trace_two_hdr_cc.py index 77ad85f64..a6212983e 100755 --- a/test_regress/t/t_trace_two_hdr_cc.py +++ b/test_regress/t/t_trace_two_hdr_cc.py @@ -21,7 +21,10 @@ test.compile(make_main=False, verilator_flags2=['-trace']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdr_sc.py b/test_regress/t/t_trace_two_hdr_sc.py index ebd1ddb6e..f189d58f0 100755 --- a/test_regress/t/t_trace_two_hdr_sc.py +++ b/test_regress/t/t_trace_two_hdr_sc.py @@ -23,7 +23,10 @@ test.compile(make_main=False, verilator_flags2=['-sc -trace']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdrfst_cc.py b/test_regress/t/t_trace_two_hdrfst_cc.py index 0817439d8..3836a14ad 100755 --- a/test_regress/t/t_trace_two_hdrfst_cc.py +++ b/test_regress/t/t_trace_two_hdrfst_cc.py @@ -20,9 +20,11 @@ test.compile(make_main=False, vm_prefix='Vt_trace_two_b', verilator_flags2=['--trace-fst --trace-threads 1']) -test.run( - logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) +test.run(logfile=test.obj_dir + "/make_first_ALL.log", + cmd=[ + os.environ["MAKE"], "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile( make_main=False, diff --git a/test_regress/t/t_trace_two_port_cc.py b/test_regress/t/t_trace_two_port_cc.py index 9ada6cf8e..e4317efaa 100755 --- a/test_regress/t/t_trace_two_port_cc.py +++ b/test_regress/t/t_trace_two_port_cc.py @@ -21,7 +21,10 @@ test.compile(make_main=False, verilator_flags2=['-trace']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_port_sc.py b/test_regress/t/t_trace_two_port_sc.py index 239f3e938..b26f20592 100755 --- a/test_regress/t/t_trace_two_port_sc.py +++ b/test_regress/t/t_trace_two_port_sc.py @@ -24,7 +24,10 @@ test.compile(make_main=False, verilator_flags2=['-sc -trace']) test.run(logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) + cmd=[ + os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile(make_main=False, top_filename='t_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_portfst_cc.py b/test_regress/t/t_trace_two_portfst_cc.py index 470301806..b1048f6a9 100755 --- a/test_regress/t/t_trace_two_portfst_cc.py +++ b/test_regress/t/t_trace_two_portfst_cc.py @@ -20,9 +20,11 @@ test.compile(make_main=False, vm_prefix='Vt_trace_two_b', verilator_flags2=['--trace-fst --trace-threads 1']) -test.run( - logfile=test.obj_dir + "/make_first_ALL.log", - cmd=["make", "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"]) +test.run(logfile=test.obj_dir + "/make_first_ALL.log", + cmd=[ + os.environ["MAKE"], "-C", "" + test.obj_dir, "-f", "Vt_trace_two_b.mk", + "Vt_trace_two_b__ALL.cpp" + ]) test.compile( make_main=False, From ee1ab074dce03da7138ee211cf9aaa0454f37dbb Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Sat, 23 Aug 2025 16:59:12 +0200 Subject: [PATCH 220/252] Internals: Fix missing variable initializations (#6327) --- bin/verilator_gantt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/verilator_gantt b/bin/verilator_gantt index bae7a2603..c84533660 100755 --- a/bin/verilator_gantt +++ b/bin/verilator_gantt @@ -238,6 +238,7 @@ def report_mtasks(): thread_mtask_time = collections.defaultdict(lambda: 0) long_mtask_time = 0 long_mtask = None + long_mtask_hier_block = None predict_mtask_time = 0 predict_elapsed = 0 for (hier_block, mtask_id) in Mtasks: @@ -279,6 +280,9 @@ def report_mtasks(): max_p2e = -1000000 max_mtask = None + min_hier_block = None + max_hier_block = None + for (hier_block, mtask_id) in sorted(Mtasks.keys()): mtask = Mtasks[(hier_block, mtask_id)] if mtask['elapsed'] > 0: From f45b811c22533b9e9fe542cdbc11d59cacfbc8bb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 23 Aug 2025 15:20:36 -0400 Subject: [PATCH 221/252] Commentary: Changes update --- Changes | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 00801af05..84b245735 100644 --- a/Changes +++ b/Changes @@ -38,11 +38,13 @@ Verilator 5.039 devel * Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.] * Support covergroup extends, etc., as unsupported (#6160). [Artur Bieniek, Antmicro Ltd.] * Support parameter resolution of 1D unpacked array slices (#6257) (#6268). [Michael Bedford Taylor] +* Support generic interfaces (#6272). [Igor Zaworski, Antmicro Ltd.] * Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Change `$display("%p")` to remove space after `}`. * Improve `--skip-identical` to skip on identical input file contents (#6109). +* Improve testing on FreeBSD (#6328). [Aleksander Kiryk] * Optimize to return memory when using -build (#6192) (#6226). [Michael B. Taylor] * Optimize 2 ** X to 1 << X if base is signed (#6203). [Max Wipfli] * Optimize more complex combinational logic in DFG (#6205) (#6209) (#6298). [Geza Lore] @@ -83,10 +85,17 @@ Verilator 5.039 devel * Fix hierarchical NBAs (#6286) (#6300). [Geza Lore] * Fix variables hiding package imports (#6289). [Johan Wouters] * Fix DFG circular driver tracing. [Geza Lore] -* Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290). [Mateusz Gancarz] +* Fix no matching function calls for randomized `VlWide` in unpacked and dynamic arrays (#6290). [Mateusz Gancarz, Antmicro Ltd.] * Fix PowerPC support (#6292). [Sergey Fedorov] * Fix referencing module variables above classes (#6304). [Artur Bieniek, Antmicro Ltd.] +* Fix direct NBA to dynamically-sized variable (#6310). [Artur Bieniek, Antmicro Ltd.] * Fix static vars under member select (#6313). [Igor Zaworski, Antmicro Ltd.] +* Fix expression type comparison (#6316). [Igor Zaworski, Antmicro Ltd.] +* Fix of inline constraints with member selects (#6321). [Igor Zaworski, Antmicro Ltd.] +* Fix corner case bugs in module and variable inlining (#6322). [Geza Lore] +* Fix queue extend to check bounds (#6324). [Aleksander Kiryk] +* Fix gathering sensitivities from virtual interface members (#6325). [Aleksander Kiryk] +* Fix FreeBSD missing headers (#6326). [Aleksander Kiryk] Verilator 5.038 2025-07-08 @@ -191,7 +200,7 @@ Verilator 5.036 2025-04-27 * Change `--output-groups` to default to value of `--build-jobs` (#5751). Those using build farms may need to now use `--output-groups 0` or otherwise. * Support user-defined primitives (UDPs) (#468) (#5807) (#5936). [Zhou Shen, Krzysztof Sychla, et al] -* Add `--trace-saif` for SAIF power traces (#5812) (#5914). [Mateusz Gancarz] +* Add `--trace-saif` for SAIF power traces (#5812) (#5914). [Mateusz Gancarz, Antmicro Ltd.] **Other:** From b226be7f98a350cb2eb3b0e6dc5d42f22ae989c7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 23 Aug 2025 15:20:58 -0400 Subject: [PATCH 222/252] Fix to select UDPs when they are the only candidate for a top module. --- Changes | 1 + src/V3LinkCells.cpp | 15 +++++++++++++++ src/V3LinkLevel.cpp | 5 ++++- test_regress/t/t_udp_binary.py | 18 ++++++++++++++++++ test_regress/t/t_udp_binary.v | 14 ++++++++++++++ test_regress/t/t_udp_binary_top.py | 18 ++++++++++++++++++ test_regress/t/t_udp_binary_top.v | 18 ++++++++++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_udp_binary.py create mode 100644 test_regress/t/t_udp_binary.v create mode 100755 test_regress/t/t_udp_binary_top.py create mode 100644 test_regress/t/t_udp_binary_top.v diff --git a/Changes b/Changes index 84b245735..ba61bbe9c 100644 --- a/Changes +++ b/Changes @@ -96,6 +96,7 @@ Verilator 5.039 devel * Fix queue extend to check bounds (#6324). [Aleksander Kiryk] * Fix gathering sensitivities from virtual interface members (#6325). [Aleksander Kiryk] * Fix FreeBSD missing headers (#6326). [Aleksander Kiryk] +* Fix to select UDPs when they are the only candidate for a top module. Verilator 5.038 2025-07-08 diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index eb2d71b6d..6334e504d 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -176,6 +176,20 @@ class LinkCellsVisitor final : public VNVisitor { return modp; } + static void removeLibFlag() { + // If the only NodeModules are in libraries, then presumably user + // wants to check the library, so clear library flag + if (!v3Global.opt.topModule().empty()) return; + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; + nodep = VN_AS(nodep->nextp(), NodeModule)) { + if (!nodep->inLibrary()) return; + } + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; + nodep = VN_AS(nodep->nextp(), NodeModule)) { + nodep->inLibrary(false); + } + } + // VISITORS void visit(AstNetlist* nodep) override { readModNames(); @@ -669,6 +683,7 @@ public: } else { m_origTopModuleName = v3Global.opt.topModule(); } + removeLibFlag(); iterate(nodep); } ~LinkCellsVisitor() override { diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 6dd093cf3..a71f27bcf 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -47,7 +47,10 @@ void V3LinkLevel::modSortByLevel() { ModVec tops; // Top level modules for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_AS(nodep->nextp(), NodeModule)) { - if (nodep->level() <= 2 && !VN_IS(nodep, NotFoundModule)) tops.push_back(nodep); + if (nodep->level() <= 2 && !VN_IS(nodep, NotFoundModule)) { + UINFO(9, "top candidate " << nodep); + tops.push_back(nodep); + } mods.push_back(nodep); } if (tops.size() >= 2) { diff --git a/test_regress/t/t_udp_binary.py b/test_regress/t/t_udp_binary.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_udp_binary.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_udp_binary.v b/test_regress/t/t_udp_binary.v new file mode 100644 index 000000000..f98676a16 --- /dev/null +++ b/test_regress/t/t_udp_binary.v @@ -0,0 +1,14 @@ +// 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 + +// Test that a standalone primitive can be a top level module + +primitive p(output id_2, input id_1); + table + 1 : 0; + 0 : 1; + endtable +endprimitive diff --git a/test_regress/t/t_udp_binary_top.py b/test_regress/t/t_udp_binary_top.py new file mode 100755 index 000000000..f33fc647b --- /dev/null +++ b/test_regress/t/t_udp_binary_top.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('vlt') + +test.compile(verilator_flags2=['--binary --top-module p']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_udp_binary_top.v b/test_regress/t/t_udp_binary_top.v new file mode 100644 index 000000000..f45244d3c --- /dev/null +++ b/test_regress/t/t_udp_binary_top.v @@ -0,0 +1,18 @@ +// 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 + +// Test that a standalone primitive can be a top level module + +primitive p(output id_2, input id_1); + table + 1 : 0; + 0 : 1; + endtable +endprimitive + +module t; // Overridden by --top-module + initial $stop; +endmodule From 7affb3e580ba72cffeb457aaca512aeb05d24080 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 23 Aug 2025 21:10:46 -0400 Subject: [PATCH 223/252] Internals: Add $past variable statistic --- src/V3Assert.cpp | 8 +++++++- src/V3AssertPre.cpp | 24 ++++++++++++------------ test_regress/t/t_past.py | 5 ++++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 72279d547..a8a3b984f 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -23,7 +23,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### -// Assert class functions +// AssertVisitor class AssertVisitor final : public VNVisitor { // CONSTANTS @@ -48,6 +48,7 @@ class AssertVisitor final : public VNVisitor { VDouble0 m_statAsNotImm; // Statistic tracking VDouble0 m_statAsImm; // Statistic tracking VDouble0 m_statAsFull; // Statistic tracking + VDouble0 m_statPastVars; // Statistic tracking bool m_inSampled = false; // True inside a sampled expression // METHODS @@ -466,9 +467,13 @@ class AssertVisitor final : public VNVisitor { = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr}; m_modp->addStmtsp(alwaysp); for (uint32_t i = 0; i < ticks; ++i) { + // TODO recognize AstVarRef is getting delayed and share variables between + // $pasts with same reference (or same expression). Saves downstream + // optimizations from identifying and removing duplication. AstVar* const outvarp = new AstVar{ nodep->fileline(), VVarType::MODULETEMP, "_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep()}; + ++m_statPastVars; m_modp->addStmtsp(outvarp); AstNode* const assp = new AstAssignDly{ nodep->fileline(), new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}, inp}; @@ -719,6 +724,7 @@ public: V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm); V3Stats::addStat("Assertions, cover statements", m_statCover); V3Stats::addStat("Assertions, full/parallel case", m_statAsFull); + V3Stats::addStat("Assertions, $past variables", m_statPastVars); } }; diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 7b6a21bc0..89fdc83b1 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -461,9 +461,9 @@ private: if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; AstSenTree* sentreep = nodep->sentreep(); if (sentreep) sentreep->unlinkFrBack(); - AstNodeExpr* const past = new AstPast{fl, exprp}; - past->dtypeFrom(exprp); - exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTreePure(false)}}; + AstNodeExpr* const pastp = new AstPast{fl, exprp}; + pastp->dtypeFrom(exprp); + exprp = new AstAnd{fl, pastp, new AstNot{fl, exprp->cloneTreePure(false)}}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); nodep->sentreep(newSenTree(nodep, sentreep)); @@ -482,9 +482,9 @@ private: if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; AstSenTree* sentreep = nodep->sentreep(); if (sentreep) sentreep->unlinkFrBack(); - AstNodeExpr* const past = new AstPast{fl, exprp}; - past->dtypeFrom(exprp); - exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTreePure(false)}; + AstNodeExpr* const pastp = new AstPast{fl, exprp}; + pastp->dtypeFrom(exprp); + exprp = new AstAnd{fl, new AstNot{fl, pastp}, exprp->cloneTreePure(false)}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); nodep->sentreep(newSenTree(nodep, sentreep)); @@ -497,9 +497,9 @@ private: AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); AstSenTree* sentreep = nodep->sentreep(); if (sentreep) sentreep->unlinkFrBack(); - AstNodeExpr* const past = new AstPast{fl, exprp}; - past->dtypeFrom(exprp); - exprp = new AstEq{fl, past, exprp->cloneTreePure(false)}; + AstNodeExpr* const pastp = new AstPast{fl, exprp}; + pastp->dtypeFrom(exprp); + exprp = new AstEq{fl, pastp, exprp->cloneTreePure(false)}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); nodep->sentreep(newSenTree(nodep, sentreep)); @@ -515,9 +515,9 @@ private: if (m_disablep) lhsp = new AstAnd{fl, new AstNot{fl, m_disablep}, lhsp}; - AstNodeExpr* const past = new AstPast{fl, lhsp}; - past->dtypeFrom(lhsp); - AstNodeExpr* const exprp = new AstOr{fl, new AstNot{fl, past}, rhsp}; + AstNodeExpr* const pastp = new AstPast{fl, lhsp}; + pastp->dtypeFrom(lhsp); + AstNodeExpr* const exprp = new AstOr{fl, new AstNot{fl, pastp}, rhsp}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); nodep->sentreep(newSenTree(nodep)); diff --git a/test_regress/t/t_past.py b/test_regress/t/t_past.py index d4f986441..bd043aa76 100755 --- a/test_regress/t/t_past.py +++ b/test_regress/t/t_past.py @@ -11,8 +11,11 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=['--stats']) test.execute() +# Check that $past shared common variables +test.file_grep(test.stats, r'Assertions, \$past variables\s+(\d+)', 11) + test.passes() From ac2a75fbb52614702f1775145784ee18324a5014 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 23 Aug 2025 21:16:53 -0400 Subject: [PATCH 224/252] Support future sampled value functions. --- Changes | 1 + src/V3Assert.cpp | 121 ++++++++++++++++++++--- src/V3AssertPre.cpp | 46 +++++++++ src/V3AstNodeExpr.h | 66 +++++++++++++ src/V3EmitV.cpp | 20 ++++ src/V3Width.cpp | 25 +++++ src/verilog.l | 5 + src/verilog.y | 15 ++- test_regress/t/t_assert_future.py | 18 ++++ test_regress/t/t_assert_future.v | 87 ++++++++++++++++ test_regress/t/t_assert_future_bad.out | 22 +++++ test_regress/t/t_assert_future_bad.py | 16 +++ test_regress/t/t_assert_future_bad.v | 34 +++++++ test_regress/t/t_assert_future_unsup.out | 6 ++ test_regress/t/t_assert_future_unsup.py | 16 +++ test_regress/t/t_assert_future_unsup.v | 23 +++++ 16 files changed, 508 insertions(+), 13 deletions(-) create mode 100755 test_regress/t/t_assert_future.py create mode 100644 test_regress/t/t_assert_future.v create mode 100644 test_regress/t/t_assert_future_bad.out create mode 100755 test_regress/t/t_assert_future_bad.py create mode 100644 test_regress/t/t_assert_future_bad.v create mode 100644 test_regress/t/t_assert_future_unsup.out create mode 100755 test_regress/t/t_assert_future_unsup.py create mode 100644 test_regress/t/t_assert_future_unsup.v diff --git a/Changes b/Changes index ba61bbe9c..f30fd991d 100644 --- a/Changes +++ b/Changes @@ -40,6 +40,7 @@ Verilator 5.039 devel * Support parameter resolution of 1D unpacked array slices (#6257) (#6268). [Michael Bedford Taylor] * Support generic interfaces (#6272). [Igor Zaworski, Antmicro Ltd.] * Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] +* Support future sampled value functions. * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Change `$display("%p")` to remove space after `}`. diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index a8a3b984f..245fe83d1 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -22,6 +22,98 @@ VL_DEFINE_DEBUG_FUNCTIONS; +//###################################################################### +// AssertDeFuture +// If any AstFuture, then move all non-future varrefs to be one cycle behind, +// see IEEE 1800-2023 16.9.4. + +class AssertDeFuture final : public VNVisitor { + // STATE - across all visitors + AstNodeModule* const m_modp; // Module future is underneath + const AstFuture* m_futurep; // First AstFuture found + const unsigned m_pastNum; // Prefix unique number for this module + std::map m_delayedVars; // Old to delayed variable mapping + // STATE - for current visit position (use VL_RESTORER) + bool m_inFuture = false; // Inside a future + bool m_unsupported = false; // Printed unsupported + + // METHODS + void unsupported(AstNode* nodep) { + if (m_unsupported) return; + m_unsupported = true; + nodep->v3warn(E_UNSUPPORTED, + "Unsupported/illegal: future value function used with expression with " + << nodep->prettyOperatorName()); + } + // VISITORS + void visit(AstFuture* nodep) override { + VL_RESTORER(m_inFuture); + m_inFuture = true; + iterateChildren(nodep); + // Done with the future, this subexpression is current-time + nodep->replaceWith(nodep->exprp()->unlinkFrBack()); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + void visit(AstNodeVarRef* nodep) override { + if (nodep->user1SetOnce()) return; + if (m_inFuture || m_unsupported) + return; // Need user1 set above, don't process when Future is removed + if (nodep->access().isWriteOrRW()) { + unsupported(nodep); + return; + } + auto it = m_delayedVars.find(nodep->varp()); + AstVar* outvarp; + if (it == m_delayedVars.end()) { + AstSenTree* const sentreep = m_futurep->sentreep(); + AstAlways* const alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, + sentreep->cloneTree(false), nullptr}; + m_modp->addStmtsp(alwaysp); + outvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, + "__Vnotfuture" + cvtToStr(m_pastNum) + "_" + nodep->name(), + nodep->dtypep()}; + m_modp->addStmtsp(outvarp); + AstVarRef* varRefAWritep = new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}; + varRefAWritep->user1(true); + AstNodeVarRef* varRefAReadp = nodep->cloneTree(false); + varRefAReadp->user1(true); + AstNode* const assp = new AstAssignDly{nodep->fileline(), varRefAWritep, varRefAReadp}; + alwaysp->addStmtsp(assp); + m_delayedVars.emplace(nodep->varp(), outvarp); + } else { + outvarp = it->second; + } + AstVarRef* newp = new AstVarRef{nodep->fileline(), outvarp, VAccess::READ}; + newp->user1(true); + UINFO(9, "DeFuture " << nodep << " becomes " << newp); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + void visit(AstNodeFTaskRef* nodep) override { unsupported(nodep); } + void visit(AstMethodCall* nodep) override { unsupported(nodep); } + void visit(AstNode* nodep) override { + if (!nodep->isPure()) unsupported(nodep); + iterateChildren(nodep); + } + +public: + // CONSTRUCTORS + explicit AssertDeFuture(AstNode* nodep, AstNodeModule* modp, unsigned pastNum) + : m_modp{modp} + , m_pastNum{pastNum} { + // See if any Future before we process + if (nodep->forall([&](const AstFuture* futurep) -> bool { + m_futurep = futurep; + return false; + })) + return; + // UINFOTREE(9, nodep, "", "defuture-in"); + visit(nodep); // Nodep may get deleted + // UINFOTREE(9, nodep, "", "defuture-ou"); + } + ~AssertDeFuture() = default; +}; + //###################################################################### // AssertVisitor @@ -32,7 +124,7 @@ class AssertVisitor final : public VNVisitor { // NODE STATE/TYPES // Cleared on netlist - // AstNode::user() -> bool. True if processed + // AstNode::user1() -> bool. True if processed const VNUser1InUse m_inuser1; // STATE @@ -203,9 +295,12 @@ class AssertVisitor final : public VNVisitor { return bodysp; } - void newPslAssertion(AstNodeCoverOrAssert* nodep, AstNode* failsp) { + void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) { if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name()); + { AssertDeFuture{nodep->propp(), m_modp, m_modPastNum++}; } + iterateChildren(nodep); + AstNodeExpr* const propp = VN_AS(nodep->propp()->unlinkFrBackWithNext(), NodeExpr); AstSenTree* const sentreep = nodep->sentreep(); const string& message = nodep->name(); @@ -449,6 +544,13 @@ class AssertVisitor final : public VNVisitor { } } + void visit(AstFuture* nodep) override { + nodep->v3error("Future sampled value function called outside property or sequence " + "expression (IEEE 16.9.4)"); + nodep->replaceWith(new AstConst{nodep->fileline(), 0}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + //========== Past void visit(AstPast* nodep) override { iterateChildren(nodep); @@ -599,9 +701,8 @@ class AssertVisitor final : public VNVisitor { nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } - void visit(AstAssert* nodep) override { - iterateChildren(nodep); - newPslAssertion(nodep, nodep->failsp()); + void visit(AstAssert* nodep) override { // + visitAssertionIterate(nodep, nodep->failsp()); } void visit(AstAssertCtl* nodep) override { if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) { @@ -678,13 +779,11 @@ class AssertVisitor final : public VNVisitor { } VL_DO_DANGLING(pushDeletep(nodep), nodep); } - void visit(AstAssertIntrinsic* nodep) override { - iterateChildren(nodep); - newPslAssertion(nodep, nodep->failsp()); + void visit(AstAssertIntrinsic* nodep) override { // + visitAssertionIterate(nodep, nodep->failsp()); } - void visit(AstCover* nodep) override { - iterateChildren(nodep); - newPslAssertion(nodep, nullptr); + void visit(AstCover* nodep) override { // + visitAssertionIterate(nodep, nullptr); } void visit(AstRestrict* nodep) override { iterateChildren(nodep); diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 89fdc83b1..5f413ecaf 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -453,6 +453,19 @@ private: if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep)); clearAssertInfo(); } + void visit(AstFalling* nodep) override { + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); + AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); + if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; + AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)}; + futurep->dtypeFrom(exprp); + exprp = new AstAnd{fl, exprp->cloneTreePure(false), new AstNot{fl, futurep}}; + exprp->dtypeSetBit(); + nodep->replaceWith(exprp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstFell* nodep) override { if (nodep->user1SetOnce()) return; iterateChildren(nodep); @@ -469,11 +482,31 @@ private: nodep->sentreep(newSenTree(nodep, sentreep)); VL_DO_DANGLING(pushDeletep(nodep), nodep); } + void visit(AstFuture* nodep) override { + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + AstSenTree* const sentreep = nodep->sentreep(); + if (sentreep) sentreep->unlinkFrBack(); + nodep->sentreep(newSenTree(nodep)); + } void visit(AstPast* nodep) override { if (nodep->sentreep()) return; // Already processed iterateChildren(nodep); nodep->sentreep(newSenTree(nodep)); } + void visit(AstRising* nodep) override { + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); + AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); + if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; + AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)}; + futurep->dtypeFrom(exprp); + exprp = new AstAnd{fl, new AstNot{fl, exprp->cloneTreePure(false)}, futurep}; + exprp->dtypeSetBit(); + nodep->replaceWith(exprp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstRose* nodep) override { if (nodep->user1SetOnce()) return; iterateChildren(nodep); @@ -505,6 +538,19 @@ private: nodep->sentreep(newSenTree(nodep, sentreep)); VL_DO_DANGLING(pushDeletep(nodep), nodep); } + void visit(AstSteady* nodep) override { + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); + AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); + if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; + AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)}; + futurep->dtypeFrom(exprp); + exprp = new AstEq{fl, exprp->cloneTreePure(false), futurep}; + exprp->dtypeSetBit(); + nodep->replaceWith(exprp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstImplication* nodep) override { if (nodep->sentreep()) return; // Already processed diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index be535e86d..ae918af30 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1489,6 +1489,22 @@ public: bool cleanOut() const override { return false; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstFalling final : public AstNodeExpr { + // Verilog $falling_gclk + // @astgen op1 := exprp : AstNodeExpr +public: + AstFalling(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_Falling(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstFalling; + string emitVerilog() override { return "$falling_gclk(%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(""); } + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; class AstFell final : public AstNodeExpr { // Verilog $fell // @astgen op1 := exprp : AstNodeExpr @@ -1507,6 +1523,24 @@ public: int instrCount() const override { return widthInstrs(); } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstFuture final : public AstNodeExpr { + // Verilog $future_gclk + // @astgen op1 := exprp : AstNodeExpr + // @astgen op2 := sentreep : Optional[AstSenTree] +public: + AstFuture(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep) + : ASTGEN_SUPER_Future(fl) { + this->exprp(exprp); + this->sentreep(sentreep); + } + ASTGEN_MEMBERS_AstFuture; + string emitVerilog() override { return "$future_gclk(%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(""); } + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; class AstGatePin final : public AstNodeExpr { // Possibly expand a gate primitive input pin value to match the range of the gate primitive // @astgen op1 := exprp : AstNodeExpr // Pin expression @@ -1888,6 +1922,22 @@ public: int instrCount() const override { return INSTR_COUNT_PLI; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstRising final : public AstNodeExpr { + // Verilog $rising_gclk + // @astgen op1 := exprp : AstNodeExpr +public: + AstRising(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_Rising(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstRising; + string emitVerilog() override { return "$rising_gclk(%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(""); } + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; class AstRose final : public AstNodeExpr { // Verilog $rose // @astgen op1 := exprp : AstNodeExpr @@ -2147,6 +2197,22 @@ public: bool cleanOut() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstSteady final : public AstNodeExpr { + // Verilog $steady_gclk + // @astgen op1 := exprp : AstNodeExpr +public: + AstSteady(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_Steady(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstSteady; + string emitVerilog() override { return "$steady_gclk(%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(""); } + int instrCount() const override { return widthInstrs(); } + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; class AstStructSel final : public AstNodeExpr { // Unpacked struct/union member access // Parents: math|stmt diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 2765564b0..4f730c348 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -426,6 +426,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { iterateAndNextConstNull(nodep->exprp()); puts(")"); } + void visit(AstRising* nodep) override { + putfs(nodep, "$rising("); + iterateAndNextConstNull(nodep->exprp()); + puts(")"); + } void visit(AstRose* nodep) override { putfs(nodep, "$rose("); iterateAndNextConstNull(nodep->exprp()); @@ -444,6 +449,16 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } puts(")"); } + void visit(AstFalling* nodep) override { + putfs(nodep, "$falling("); + iterateAndNextConstNull(nodep->exprp()); + puts(")"); + } + void visit(AstFuture* nodep) override { + putfs(nodep, "$future("); + iterateAndNextConstNull(nodep->exprp()); + puts(")"); + } void visit(AstStable* nodep) override { putfs(nodep, "$stable("); iterateAndNextConstNull(nodep->exprp()); @@ -453,6 +468,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } puts(")"); } + void visit(AstSteady* nodep) override { + putfs(nodep, "$steady("); + iterateAndNextConstNull(nodep->exprp()); + puts(")"); + } void visit(AstReturn* nodep) override { putfs(nodep, "return "); iterateAndNextConstNull(nodep->lhsp()); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 22ce31ba7..a12da7ed2 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1344,6 +1344,19 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetBit(); } } + void visit(AstFalling* nodep) override { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + nodep->dtypeSetBit(); + } + } + void visit(AstFuture* nodep) override { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + userIterate(nodep->sentreep(), nullptr); + nodep->dtypeFrom(nodep->exprp()); + } + } void visit(AstPast* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); @@ -1369,6 +1382,12 @@ class WidthVisitor final : public VNVisitor { userIterate(nodep->sentreep(), nullptr); } } + void visit(AstRising* nodep) override { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + nodep->dtypeSetBit(); + } + } void visit(AstRose* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); @@ -1431,6 +1450,12 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetBit(); } } + void visit(AstSteady* nodep) override { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + nodep->dtypeSetBit(); + } + } void visit(AstStmtExpr* nodep) override { assertAtStatement(nodep); diff --git a/src/verilog.l b/src/verilog.l index dc1e3a323..de6c832c2 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -471,13 +471,16 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$bits" { FL; return yD_BITS; } "$changed" { FL; return yD_CHANGED; } "$changed_gclk" { FL; return yD_CHANGED_GCLK; } + "$changing_gclk" { FL; return yD_CHANGING_GCLK; } "$countbits" { FL; return yD_COUNTBITS; } "$countones" { FL; return yD_COUNTONES; } "$dimensions" { FL; return yD_DIMENSIONS; } "$error" { FL; return yD_ERROR; } + "$falling_gclk" { FL; return yD_FALLING_GCLK; } "$fatal" { FL; return yD_FATAL; } "$fell" { FL; return yD_FELL; } "$fell_gclk" { FL; return yD_FELL_GCLK; } + "$future_gclk" { FL; return yD_FUTURE_GCLK; } "$high" { FL; return yD_HIGH; } "$increment" { FL; return yD_INCREMENT; } "$inferred_disable" { FL; return yD_INFERRED_DISABLE; } @@ -491,12 +494,14 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$past" { FL; return yD_PAST; } "$past_gclk" { FL; return yD_PAST_GCLK; } "$right" { FL; return yD_RIGHT; } + "$rising_gclk" { FL; return yD_RISING_GCLK; } "$root" { FL; return yD_ROOT; } "$rose" { FL; return yD_ROSE; } "$rose_gclk" { FL; return yD_ROSE_GCLK; } "$size" { FL; return yD_SIZE; } "$stable" { FL; return yD_STABLE; } "$stable_gclk" { FL; return yD_STABLE_GCLK; } + "$steady_gclk" { FL; return yD_STEADY_GCLK; } "$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; } "$warning" { FL; return yD_WARNING; } /* SV2005 Keywords */ diff --git a/src/verilog.y b/src/verilog.y index b34af6ab9..2a93acad9 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -853,6 +853,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_CEIL "$ceil" %token yD_CHANGED "$changed" %token yD_CHANGED_GCLK "$changed_gclk" +%token yD_CHANGING_GCLK "$changing_gclk" %token yD_CLOG2 "$clog2" %token yD_COS "$cos" %token yD_COSH "$cosh" @@ -881,6 +882,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_ERROR "$error" %token yD_EXIT "$exit" %token yD_EXP "$exp" +%token yD_FALLING_GCLK "$falling_gclk" %token yD_FATAL "$fatal" %token yD_FCLOSE "$fclose" %token yD_FDISPLAY "$fdisplay" @@ -910,6 +912,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_FSTROBEH "$fstrobeh" %token yD_FSTROBEO "$fstrobeo" %token yD_FTELL "$ftell" +%token yD_FUTURE_GCLK "$future_gclk" %token yD_FWRITE "$fwrite" %token yD_FWRITEB "$fwriteb" %token yD_FWRITEH "$fwriteh" @@ -946,6 +949,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_REALTOBITS "$realtobits" %token yD_REWIND "$rewind" %token yD_RIGHT "$right" +%token yD_RISING_GCLK "$rising_gclk" %token yD_ROOT "$root" %token yD_ROSE "$rose" %token yD_ROSE_GCLK "$rose_gclk" @@ -965,6 +969,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_STABLE "$stable" %token yD_STABLE_GCLK "$stable_gclk" %token yD_STACKTRACE "$stacktrace" +%token yD_STEADY_GCLK "$steady_gclk" %token yD_STIME "$stime" %token yD_STOP "$stop" %token yD_STROBE "$strobe" @@ -4490,6 +4495,8 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}}; } | yD_CHANGED_GCLK '(' expr ')' { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}}; } + | yD_CHANGING_GCLK '(' expr ')' + { $$ = new AstLogNot{$1, new AstSteady{$1, $3}}; } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2{$1, $3}; } | yD_COS '(' expr ')' { $$ = new AstCosD{$1, $3}; } | yD_COSH '(' expr ')' { $$ = new AstCoshD{$1, $3}; } @@ -4509,6 +4516,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_DIST_T '(' expr ',' expr ')' { $$ = new AstDistT{$1, $3, $5}; } | yD_DIST_UNIFORM '(' expr ',' expr ',' expr ')' { $$ = new AstDistUniform{$1, $3, $5, $7}; } | yD_EXP '(' expr ')' { $$ = new AstExpD{$1, $3}; } + | yD_FALLING_GCLK '(' expr ')' { $$ = new AstFalling{$1, $3}; } | yD_FELL '(' expr ')' { $$ = new AstFell{$1, $3, nullptr}; } | yD_FELL '(' expr ',' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_FELL_GCLK '(' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } @@ -4523,6 +4531,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_FREAD '(' expr ',' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, $9}; } | yD_FREAD '(' expr ',' expr ',' ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, $8}; } | yD_FREWIND '(' expr ')' { $$ = new AstFRewind{$1, $3}; } + | yD_FUTURE_GCLK '(' expr ')' { $$ = new AstFuture{$1, $3, nullptr}; } | yD_FLOOR '(' expr ')' { $$ = new AstFloorD{$1, $3}; } | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF{$1, *$5, $3, $6}; } | yD_FSEEK '(' expr ',' expr ',' expr ')' { $$ = new AstFSeek{$1, $3, $5, $7}; } @@ -4562,6 +4571,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_REWIND '(' expr ')' { $$ = new AstFSeek{$1, $3, new AstConst{$1, 0}, new AstConst{$1, 0}}; } | yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, nullptr}; } | yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, $5}; } + | yD_RISING_GCLK '(' expr ')' { $$ = new AstRising{$1, $3}; } | yD_ROSE '(' expr ')' { $$ = new AstRose{$1, $3, nullptr}; } | yD_ROSE '(' expr ',' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_ROSE_GCLK '(' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } @@ -4576,11 +4586,12 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_SIZE '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_SIZE, $3, $5}; } | yD_SQRT '(' expr ')' { $$ = new AstSqrtD{$1, $3}; } | yD_SSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstSScanF{$1, *$5, $3, $6}; } - | yD_STIME parenE - { $$ = new AstSel{$1, new AstTime{$1, VTimescale{VTimescale::NONE}}, 0, 32}; } | yD_STABLE '(' expr ')' { $$ = new AstStable{$1, $3, nullptr}; } | yD_STABLE '(' expr ',' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; } | yD_STABLE_GCLK '(' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } + | yD_STEADY_GCLK '(' expr ')' { $$ = new AstSteady{$1, $3}; } + | yD_STIME parenE + { $$ = new AstSel{$1, new AstTime{$1, VTimescale{VTimescale::NONE}}, 0, 32}; } | yD_TAN '(' expr ')' { $$ = new AstTanD{$1, $3}; } | yD_TANH '(' expr ')' { $$ = new AstTanhD{$1, $3}; } | yD_TESTPLUSARGS '(' expr ')' { $$ = new AstTestPlusArgs{$1, $3}; } diff --git a/test_regress/t/t_assert_future.py b/test_regress/t/t_assert_future.py new file mode 100755 index 000000000..a34dd7c7a --- /dev/null +++ b/test_regress/t/t_assert_future.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=['--assert']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assert_future.v b/test_regress/t/t_assert_future.v new file mode 100644 index 000000000..ef88c6843 --- /dev/null +++ b/test_regress/t/t_assert_future.v @@ -0,0 +1,87 @@ +// 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 + +// verilog_format: off +`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); +// verilog_format: on + +module t ( + /*AUTOARG*/ + // Inputs + clk + ); + input clk; + + global clocking @(posedge clk); + endclocking + + logic a; + + //PASSES: + int count_not_stable; // Counts changes + assert property (@(posedge clk) $stable(a)) + else count_not_stable = count_not_stable + 1; + + int count_not_stable_gclk; // Counts changes + assert property (@(posedge clk) $stable_gclk(a)) + else count_not_stable_gclk = count_not_stable_gclk + 1; + + int count_changing_gclk; + assert property (@(posedge clk) $changing_gclk(a)) count_changing_gclk = count_changing_gclk + 1; + + int count_falling_gclk; + assert property (@(posedge clk) $falling_gclk(a)) count_falling_gclk = count_falling_gclk + 1; + + int count_future_gclk; // Counts changes + assert property (@(posedge clk) $future_gclk(a) != a) count_future_gclk = count_future_gclk + 1; + + int count_rising_gclk; + assert property (@(posedge clk) $rising_gclk(a)) count_rising_gclk = count_rising_gclk + 1; + + int count_not_steady_gclk; // Counts changes + assert property (@(posedge clk) $steady_gclk(a)) + else count_not_steady_gclk = count_not_steady_gclk + 1; + + int cyc = 0; + + always @(posedge clk) begin +`ifdef TEST_VERBOSE + $display("[%0t] cyc=%0d a=%0x gs=%x gsc=%x", $time, cyc, a, count_not_stable, + count_not_stable_gclk); +`endif + cyc <= cyc + 1; + if (cyc <= 3) begin + a <= 0; + count_not_stable = 0; + count_not_stable_gclk = 0; + end + else if (cyc == 4) begin + a <= 0; // stable + end + else if (cyc == 5) begin + a <= 1; // rise + end + else if (cyc == 6) begin + a <= 1; // stable + end + else if (cyc == 7) begin + a <= 0; // fall + end + else if (cyc == 10) begin + `checkd(count_not_stable, 2); + `checkd(count_not_stable_gclk, 2); + `checkd(count_changing_gclk, 2); + `checkd(count_falling_gclk, 1); + `checkd(count_future_gclk, 2); + `checkd(count_rising_gclk, 1); + `checkd(count_not_steady_gclk, 2); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_assert_future_bad.out b/test_regress/t/t_assert_future_bad.out new file mode 100644 index 000000000..3cfa347d0 --- /dev/null +++ b/test_regress/t/t_assert_future_bad.out @@ -0,0 +1,22 @@ +%Error: t/t_assert_future_bad.v:18:31: Future sampled value function called outside property or sequence expression (IEEE 16.9.4) + : ... note: In instance 't' + 18 | else $display("Future=%0d", $future_gclk(a)); + | ^~~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_assert_future_bad.v:21:31: Future sampled value function called outside property or sequence expression (IEEE 16.9.4) + : ... note: In instance 't' + 21 | else $display("Future=%0d", $rising_gclk(a)); + | ^~~~~~~~~~~~ +%Error: t/t_assert_future_bad.v:24:31: Future sampled value function called outside property or sequence expression (IEEE 16.9.4) + : ... note: In instance 't' + 24 | else $display("Future=%0d", $falling_gclk(a)); + | ^~~~~~~~~~~~~ +%Error: t/t_assert_future_bad.v:27:31: Future sampled value function called outside property or sequence expression (IEEE 16.9.4) + : ... note: In instance 't' + 27 | else $display("Future=%0d", $steady_gclk(a)); + | ^~~~~~~~~~~~ +%Error: t/t_assert_future_bad.v:30:31: Future sampled value function called outside property or sequence expression (IEEE 16.9.4) + : ... note: In instance 't' + 30 | else $display("Future=%0d", $changing_gclk(a)); + | ^~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_assert_future_bad.py b/test_regress/t/t_assert_future_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_assert_future_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_assert_future_bad.v b/test_regress/t/t_assert_future_bad.v new file mode 100644 index 000000000..d6b7926d4 --- /dev/null +++ b/test_regress/t/t_assert_future_bad.v @@ -0,0 +1,34 @@ +// 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 + a, clk + ); + input a; + input clk; + + global clocking @(posedge clk); + endclocking + + assert property (@(posedge clk) 1 == 1) + else $display("Future=%0d", $future_gclk(a)); + + assert property (@(posedge clk) 1 == 1) + else $display("Future=%0d", $rising_gclk(a)); + + assert property (@(posedge clk) 1 == 1) + else $display("Future=%0d", $falling_gclk(a)); + + assert property (@(posedge clk) 1 == 1) + else $display("Future=%0d", $steady_gclk(a)); + + assert property (@(posedge clk) 1 == 1) + else $display("Future=%0d", $changing_gclk(a)); + + initial $stop; + +endmodule diff --git a/test_regress/t/t_assert_future_unsup.out b/test_regress/t/t_assert_future_unsup.out new file mode 100644 index 000000000..8dc04f62a --- /dev/null +++ b/test_regress/t/t_assert_future_unsup.out @@ -0,0 +1,6 @@ +%Error-UNSUPPORTED: t/t_assert_future_unsup.v:21:54: Unsupported/illegal: future value function used with expression with operator FUNCREF 'func' + : ... note: In instance 't' + 21 | assert property (@(posedge clk) $future_gclk(a) == func(a)); + | ^~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_assert_future_unsup.py b/test_regress/t/t_assert_future_unsup.py new file mode 100755 index 000000000..1bf1426f9 --- /dev/null +++ b/test_regress/t/t_assert_future_unsup.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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_assert_future_unsup.v b/test_regress/t/t_assert_future_unsup.v new file mode 100644 index 000000000..77282bb83 --- /dev/null +++ b/test_regress/t/t_assert_future_unsup.v @@ -0,0 +1,23 @@ +// 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 + a, clk + ); + input a; + input clk; + + function logic func(input logic i); + return i; + endfunction + + global clocking @(posedge clk); + endclocking + + assert property (@(posedge clk) $future_gclk(a) == func(a)); + +endmodule From c2cac8a7fd8ad1fefc204466fb42d4f5a759c83b Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Mon, 25 Aug 2025 12:55:11 +0200 Subject: [PATCH 225/252] Fix of localize for super constructors with function calls as arguments (#6330) --- src/V3Localize.cpp | 22 +++++++++++++++++++-- test_regress/t/t_class_super_new3.py | 18 +++++++++++++++++ test_regress/t/t_class_super_new3.v | 29 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_class_super_new3.py create mode 100644 test_regress/t/t_class_super_new3.v diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index f61dd2303..43cc60555 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -45,6 +45,9 @@ class LocalizeVisitor final : public VNVisitor { // AstVarScope::user3p() -> Set of CFuncs referencing this VarScope. (via m_accessors) // AstCFunc::user4p() -> Multimap of 'VarScope -> VarRefs that reference that VarScope' // in this function. (via m_references) + // AstVarScope::user4() -> Bool indicating VarScope cannot be optimized + // - compared to AstVarScope::user1 this guarantees that this + // scope won't be optimized. const VNUser1InUse m_user1InUse; const VNUser3InUse m_user3InUse; const VNUser4InUse m_user4InUse; @@ -60,11 +63,15 @@ class LocalizeVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) AstCFunc* m_cfuncp = nullptr; // Current active function uint32_t m_nodeDepth = 0; // Node depth under m_cfuncp + bool m_inSuperConstructorCallStmt = false; // If under super constructor call statement // METHODS bool isOptimizable(AstVarScope* nodep) { // Don't want to malloc/free the backing store all the time if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) return false; + // Variables used in super constructor call can't be localized, because + // in C++ there is no way to declare them before base class constructor call + if (nodep->user4()) return false; return ((!nodep->user1() // Not marked as not optimizable, or ... // .. a block temp used in a single CFunc || (nodep->varp()->varType() == VVarType::BLOCKTEMP @@ -147,6 +154,14 @@ class LocalizeVisitor final : public VNVisitor { iterateChildrenConst(nodep); } + void visit(AstCNew* nodep) override { + VL_RESTORER(m_inSuperConstructorCallStmt); + m_inSuperConstructorCallStmt + = m_cfuncp->isConstructor() && VN_IS(nodep->backp(), StmtExpr); + m_cfuncp->user1(true); // Mark caller as not a leaf function + iterateChildren(nodep); + } + void visit(AstCCall* nodep) override { m_cfuncp->user1(true); // Mark caller as not a leaf function iterateChildrenConst(nodep); @@ -195,8 +210,11 @@ class LocalizeVisitor final : public VNVisitor { // Remember the reference so we can fix it up later (we always need this as well) m_references(m_cfuncp).emplace(varScopep, nodep); - // Check if already marked as not optimizable - if (!varScopep->user1()) { + if (m_inSuperConstructorCallStmt) { + // Variable used in super constructor call can't be localized + varScopep->user1(true); + varScopep->user4(true); + } else if (!varScopep->user1()) { // Check if already marked as not optimizable // Note: we only check read variables, as it's ok to localize (and in fact discard) // any variables that are only written but never read. if (nodep->access().isReadOrRW() && !varScopep->user2()) { diff --git a/test_regress/t/t_class_super_new3.py b/test_regress/t/t_class_super_new3.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_class_super_new3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_super_new3.v b/test_regress/t/t_class_super_new3.v new file mode 100644 index 000000000..c216e91a9 --- /dev/null +++ b/test_regress/t/t_class_super_new3.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Base; + int j; + function new(int x); + j = x; + endfunction + static function int get_default(); + return 8; + endfunction +endclass +class Derived extends Base; + function new(); + super.new(get_default()); + endfunction +endclass + +module t; + initial begin + Derived d = new; + if (d.j != 8) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 02e64f07958ace8dab55f13b2cfc66017f7ea5f3 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 25 Aug 2025 13:47:45 +0100 Subject: [PATCH 226/252] Optimize multiplexers in Dfg synthesis (#6331) The previous algorithm was designed to handle the general case where a full control flow path predicate is required to select which value to use when synthesizing control flow join point in an always block. Here we add a better algorithm that tries to use the predicate of the closest dominating branch if the branch paths dominate the joining paths. This is almost universally true in synthesizable logic (RTLMeter has no exceptions), however there are cases where this is not applicable, for which we fall back on the previous generic algorithm. Overall this significantly simplifies the synthesized Dfg graphs and enables further optimization. --- src/V3Cfg.cpp | 287 ++++++++++++-- src/V3Cfg.h | 541 +++++++++++++++++++++------ src/V3CfgBuilder.cpp | 184 ++++----- src/V3CfgLiveVariables.cpp | 27 +- src/V3DfgAstToDfg.cpp | 6 +- src/V3DfgContext.h | 16 +- src/V3DfgSynthesize.cpp | 315 ++++++++++------ src/V3DfgVertices.h | 7 +- src/V3List.h | 1 + test_regress/t/t_dfg_break_cycles.py | 5 +- test_regress/t/t_dfg_synthesis.py | 10 +- test_regress/t/t_dfg_synthesis.v | 277 ++++++++++++++ 12 files changed, 1269 insertions(+), 407 deletions(-) diff --git a/src/V3Cfg.cpp b/src/V3Cfg.cpp index 54ec09ae8..e671cfd59 100644 --- a/src/V3Cfg.cpp +++ b/src/V3Cfg.cpp @@ -29,24 +29,9 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### -// ControlFlowGraph method definitions +// CfgBlock method definitions -bool ControlFlowGraph::containsLoop() const { - for (const V3GraphVertex& vtx : vertices()) { - const BasicBlock& current = static_cast(vtx); - for (const V3GraphEdge& edge : current.outEdges()) { - const BasicBlock& successor = *static_cast(edge.top()); - // IDs are the reverse post-order numbering, so easy to check for a back-edge - if (successor.id() < current.id()) return true; - } - } - return false; -} - -//###################################################################### -// BasicBlock method definitions - -std::string BasicBlock::name() const { +std::string CfgBlock::name() const { std::stringstream ss; ss << "BB " + std::to_string(id()) + ":\n"; for (AstNode* nodep : m_stmtps) { @@ -62,25 +47,25 @@ std::string BasicBlock::name() const { V3EmitV::debugVerilogForTree(nodep, ss); } } - std::string text = VString::replaceSubstr(ss.str(), "\n", "\\l "); - if (inEmpty()) text = "**ENTER**\n" + text; - if (outEmpty()) text = text + "\n**EXIT**"; + std::string text = VString::replaceSubstr( + VString::replaceSubstr(ss.str(), "\n", "\\l "), "\"", "\\\""); + if (isEnter()) text = "**ENTER**\n" + text; + if (isExit()) text = text + "\n**EXIT**"; return text; } -std::string BasicBlock::dotShape() const { return "rect"; } -std::string BasicBlock::dotRank() const { - if (inEmpty()) return "source"; - if (outEmpty()) return "sink"; +std::string CfgBlock::dotShape() const { return "rect"; } +std::string CfgBlock::dotRank() const { + if (isEnter()) return "source"; + if (isExit()) return "sink"; return ""; } //###################################################################### -// ControlFlowEdge method definitions +// CfgEdge method definitions -std::string ControlFlowEdge::dotLabel() const { +std::string CfgEdge::dotLabel() const { std::string label = "E" + std::to_string(id()); - const BasicBlock& source = *fromp()->as(); - const ControlFlowEdge* const untknp = source.untknEdgep(); + const CfgEdge* const untknp = srcp()->untknEdgep(); if (this == untknp) { label += " / F"; } else if (untknp) { @@ -88,3 +73,249 @@ std::string ControlFlowEdge::dotLabel() const { } return label; } + +//###################################################################### +// CfgGraph method definitions + +static void cfgOrderVisitBlock(std::vector& postOrderEnumeration, CfgBlock* bbp) { + // Mark visited + bbp->user(1); + // Visit un-visited successors + if (CfgBlock* const takenp = bbp->takenp()) { + if (!takenp->user()) cfgOrderVisitBlock(postOrderEnumeration, takenp); + if (CfgBlock* const untknp = bbp->untknp()) { + if (!untknp->user()) cfgOrderVisitBlock(postOrderEnumeration, untknp); + } + } + // Add to post order enumeration + postOrderEnumeration.emplace_back(bbp); +}; + +void CfgGraph::rpoBlocks() { + UASSERT_OBJ(m_nEdits != m_nLastOrdered, m_enterp, "Redundant 'CfgGraph::order' call"); + m_nLastOrdered = m_nEdits; + + // Reset marks + for (V3GraphVertex& v : vertices()) v.user(0); + + // Compute post-order enumeration. Simple recursive algorith will do. + std::vector postOrderEnumeration; + postOrderEnumeration.reserve(m_nBlocks); + cfgOrderVisitBlock(postOrderEnumeration, m_enterp); + UASSERT_OBJ(postOrderEnumeration.size() == m_nBlocks, m_enterp, "Inconsistent block count"); + + // Assign block IDs equal to the reverse post-order number and sort vertices + for (size_t i = 0; i < postOrderEnumeration.size(); ++i) { + CfgBlock* const bbp = postOrderEnumeration[m_nBlocks - 1 - i]; + bbp->m_rpoNumber = i; + vertices().unlink(bbp); + vertices().linkBack(bbp); + } + + // Assign edge IDs + size_t edgeCount = 0; + for (V3GraphVertex& v : vertices()) { + for (V3GraphEdge& e : v.outEdges()) static_cast(e).m_id = edgeCount++; + } + UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count"); +} + +bool CfgGraph::containsLoop() const { + for (const V3GraphVertex& vtx : vertices()) { + const CfgBlock& current = static_cast(vtx); + for (const V3GraphEdge& edge : current.outEdges()) { + const CfgBlock& successor = *static_cast(edge.top()); + // IDs are the reverse post-order numbering, so easy to check for a back-edge + if (successor.id() < current.id()) return true; + } + } + return false; +} + +void CfgGraph::minimize() { + // Remove empty blocks (except enter and exit) + for (V3GraphVertex* const vtxp : vertices().unlinkable()) { + CfgBlock* const bbp = static_cast(vtxp); + if (bbp->isEnter()) continue; + if (bbp->isExit()) continue; + if (!bbp->stmtps().empty()) continue; + UASSERT(!bbp->isBranch(), "Empty block should have a single successor"); + CfgBlock* const succp = bbp->takenp(); + for (V3GraphEdge* const edgep : bbp->inEdges().unlinkable()) edgep->relinkTop(succp); + ++m_nEdits; + --m_nEdges; + --m_nBlocks; + VL_DO_DANGLING(bbp->unlinkDelete(this), bbp); + } + + // Combine sequential blocks + for (V3GraphVertex* const vtxp : vertices().unlinkable()) { + CfgBlock* const srcp = static_cast(vtxp); + if (srcp->isExit()) continue; + if (srcp->isBranch()) continue; + CfgBlock* const dstp = srcp->takenp(); + if (dstp->isJoin()) continue; + // Combine them + if (srcp->isEnter()) m_enterp = dstp; + std::vector stmtps{std::move(srcp->m_stmtps)}; + stmtps.reserve(stmtps.size() + dstp->m_stmtps.size()); + stmtps.insert(stmtps.end(), dstp->m_stmtps.begin(), dstp->m_stmtps.end()); + dstp->m_stmtps = std::move(stmtps); + for (V3GraphEdge* const edgep : srcp->inEdges().unlinkable()) edgep->relinkTop(dstp); + ++m_nEdits; + --m_nEdges; + --m_nBlocks; + VL_DO_DANGLING(srcp->unlinkDelete(this), srcp); + } + + if (m_nEdits != m_nLastOrdered) rpoBlocks(); + if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-minimize"); +} + +void CfgGraph::breakCriticalEdges() { + // Gather critical edges + std::vector criticalEdges; + criticalEdges.reserve(m_nEdges); + for (V3GraphVertex& vtx : vertices()) { + const CfgBlock& bb = static_cast(vtx); + if (!bb.isBranch()) continue; + for (V3GraphEdge& edge : vtx.outEdges()) { + const CfgBlock& succ = static_cast(*edge.top()); + if (!succ.isJoin()) continue; + criticalEdges.emplace_back(static_cast(&edge)); + } + } + // Insert blocks + for (CfgEdge* const edgep : criticalEdges) { + CfgBlock* const newp = addBlock(); + addTakenEdge(newp, edgep->dstp()); + edgep->relinkTop(newp); + } + + if (m_nEdits != m_nLastOrdered) rpoBlocks(); + if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-breakCriticalEdges"); +} + +// Given a branching basic block, if the sub-graph below this branch, up until +// the point where all of its control flow path convertes is series-parallel, +// then return the (potentially newly created) basic block with exactly 2 +// predecessors where the two control flow paths from this branch have joined. +// If the relevant sub-graph is not series-parallel (there is a control flow +// path between the branches, or to a path not dominated by the given branch), +// then return nullptr. Cached results in the given map +CfgBlock* CfgGraph::getOrCreateTwoWayJoinFor(CfgBlock* bbp) { + UASSERT_OBJ(bbp->isBranch(), bbp, "Not a branch"); + + // Mark visited + UASSERT_OBJ(!bbp->user(), bbp, "Should not visit twice"); + bbp->user(1); + + // We need the edge converting to a join block along both path. This is how we find it: + const auto chaseEdge = [&](CfgEdge* edgep) -> CfgEdge* { + while (true) { + CfgBlock* dstp = edgep->dstp(); + // Stop if found the joining block along this path + if (dstp->isJoin()) return edgep; + // If the successor is a branch, recursively get it's 2-way join block + while (dstp->isBranch()) { + dstp = getOrCreateTwoWayJoinFor(dstp); + // If the subgarph below dstp is not series-parallel, then no solution + if (!dstp) return nullptr; + } + UASSERT_OBJ(!dstp->isExit(), bbp, "Non-convergent branch - multiple Exit blocks?"); + edgep = dstp->takenEdgep(); + } + }; + + // Walk down both paths + CfgEdge* const takenEdgep = chaseEdge(bbp->takenEdgep()); + if (!takenEdgep) return nullptr; + CfgEdge* const untknEdgep = chaseEdge(bbp->untknEdgep()); + if (!untknEdgep) return nullptr; + // If we ended up at different joining blocks, then there is a path from one + // of the branches into a path of another branch before 'bbp', no solution + if (takenEdgep->dstp() != untknEdgep->dstp()) return nullptr; + + // Pick up the common successor + CfgBlock* const succp = takenEdgep->dstp(); + // If the common successor is a 2-way join, we can use it directly + if (succp->isTwoWayJoin()) return succp; + // Otherwise insert a new block to join the 2 paths of the original block + CfgBlock* const joinp = addBlock(); + addTakenEdge(joinp, succp); + takenEdgep->relinkTop(joinp); + untknEdgep->relinkTop(joinp); + return joinp; +} + +bool CfgGraph::insertTwoWayJoins() { + // Reset marks + for (V3GraphVertex& v : vertices()) v.user(0); + + bool isSeriesParallel = true; + + // We will be adding vertices at the end. That's OK, they don't need to be visited again + for (V3GraphVertex& v : vertices()) { + CfgBlock& bb = static_cast(v); + // Skip if already visited + if (bb.user()) continue; + // Skip if not a branch + if (!bb.isBranch()) continue; + // Fix it up, record if failed + if (!getOrCreateTwoWayJoinFor(&bb)) isSeriesParallel = false; + } + + if (m_nEdits != m_nLastOrdered) rpoBlocks(); + if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-insertTwoWayJoins"); + + return isSeriesParallel; +} + +//###################################################################### +// CfgDominatorTree + +const CfgBlock* CfgDominatorTree::intersect(const CfgBlock* ap, const CfgBlock* bp) { + while (ap != bp) { + while (*ap > *bp) ap = m_bb2Idom[*ap]; + while (*bp > *ap) bp = m_bb2Idom[*bp]; + } + return ap; +} + +CfgDominatorTree::CfgDominatorTree(const CfgGraph& cfg) + : m_bb2Idom{cfg.makeBlockMap()} { + + // Build the immediate dominator map, using algorithm from: + // "A Simple, Fast Dominance Algorithm", Keith D. Cooper et al., 2006 + // Immediate dominator of the enter block + + // Point enteer block to itself, while computing below + m_bb2Idom[cfg.enter()] = &cfg.enter(); + // Iterate until settled + for (bool changed = true; changed;) { + changed = false; + // For each vertex except enter block + for (const V3GraphVertex& vtx : cfg.vertices()) { + const CfgBlock& curr = static_cast(vtx); + if (curr.isEnter()) continue; // Skip entry block + + // For each predecessor of current block + const CfgBlock* idom = nullptr; + for (const V3GraphEdge& edge : curr.inEdges()) { + const CfgBlock& pred = static_cast(*edge.fromp()); + // Skip if perdecessor not yet processed + if (!m_bb2Idom[pred]) continue; + // Pick first, then use intersect + idom = !idom ? &pred : intersect(&pred, idom); + } + + // If chenged, record it, else move on + if (idom == m_bb2Idom[curr]) continue; + m_bb2Idom[curr] = idom; + changed = true; + } + } + + // The enter block is the root of the tree and does not itself have an immediate dominator + m_bb2Idom[cfg.enter()] = nullptr; +} diff --git a/src/V3Cfg.h b/src/V3Cfg.h index 2eba9514e..51c426659 100644 --- a/src/V3Cfg.h +++ b/src/V3Cfg.h @@ -68,69 +68,103 @@ #include //###################################################################### -// DenseMap - This can be made generic if needed +// Control Flow Graph (CFG) and related data structures -// Map from unique non-negative integers (IDs) to generic values. As opposed -// to a std::map/std::unordered_map, in DenseMap, all entries are value -// initialized at construction, and the map is always dense. This can be -// improved if necessary but is sufficient for our current purposes. -template -class DenseMap final { - std::vector m_map; // The map, stored as a vector +class CfgGraph; +class CfgBlock; +class CfgEdge; -public: - // CONSTRUCTOR - explicit DenseMap(size_t size) - : m_map{size} {} - - T_Value& operator[](const T_Key& key) { return m_map.at((key.*Index)()); } - const T_Value& operator[](const T_Key& key) const { return m_map.at((key.*Index)()); } -}; +template +class CfgMap; +template +class CfgBlockMap; +template +class CfgEdgeMap; //###################################################################### -// ControlFlowGraph data structure +// CfgBlock - A basic block (verticies of the control flow graph) -class ControlFlowGraph; -class BasicBlock; -class ControlFlowEdge; - -// A basic block (verticies of the control flow graph) -class BasicBlock final : public V3GraphVertex { - VL_RTTI_IMPL(BasicBlock, V3GraphVertex) +class CfgBlock final : public V3GraphVertex { + friend class CfgGraph; + template + friend class CfgMap; friend class CfgBuilder; - // STATE - Immutable after construction, set by CfgBuilder - // V3GraphEdge::user() is set to the unique id by CfgBuilder - std::vector m_stmtps; // statements in this BasicBlock + // STATE + CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under + size_t m_rpoNumber; // Reverse post-order number and unique ID of this CfgBlock - // CONSTRUCTOR - via CfgBuilder only - inline explicit BasicBlock(ControlFlowGraph* graphp); - ~BasicBlock() override = default; + // V3GraphEdge::user() is set to the unique id by CfgBuilder + std::vector m_stmtps; // statements in this CfgBlock + + // PRIVATE METHODS + // ID (reverse post-order numpber) of this block + inline size_t id(); + inline size_t id() const; + + // CONSTRUCTOR/DESTRUCTOR - via CfgGraph only + inline explicit CfgBlock(CfgGraph* cfgp); + ~CfgBlock() override = default; public: - // METHODS - all const + // PUBLIC METHODS - // Statements in this BasicBlock + // Is this the entry block of the CFG? + bool isEnter() const { return inEmpty(); } + // Is this the exit block of the CFG? + bool isExit() const { return outEmpty(); } + // Is this a branching block (multiple successors)? + bool isBranch() const { return outEdges().hasMultipleElements(); } + // Is this a control flow convergence block (multiple predecessors)? + bool isJoin() const { return inEdges().hasMultipleElements(); } + // Is this a join of exactly 2 paths? + bool isTwoWayJoin() const { return inEdges().hasTwoElements(); } + + // The edge going to the taken (or uncinditional) successor, or nullptr if exit block + inline CfgEdge* takenEdgep(); + inline const CfgEdge* takenEdgep() const; + // The edge going to the untaken successor, or nullptr if not a branch, or exit block + inline CfgEdge* untknEdgep(); + inline const CfgEdge* untknEdgep() const; + // The taken successor block, or nullptr if exit block + inline CfgBlock* takenp(); + inline const CfgBlock* takenp() const; + // The untakens successor block, or nullptr if not a branch, or exit block + inline CfgBlock* untknp(); + inline const CfgBlock* untknp() const; + + // The first predecessor edge, or nullptr if enter block + inline CfgEdge* firstPredecessorEdgep(); + inline const CfgEdge* firstPredecessorEdgep() const; + // The last predecessor edge, or nullptr if enter block + inline CfgEdge* lastPredecessorEdgep(); + inline const CfgEdge* lastPredecessorEdgep() const; + // The first predecessor block, or nullptr if enter block + inline CfgBlock* firstPredecessorp(); + inline const CfgBlock* firstPredecessorp() const; + // The last predecessor block, or nullptr if enter block + inline CfgBlock* lastPredecessorp(); + inline const CfgBlock* lastPredecessorp() const; + + // Statements in this CfgBlock const std::vector& stmtps() const { return m_stmtps; } - // Unique ID of this BasicBlock - defines topological ordering - size_t id() const { return V3GraphVertex::user(); } - // The edge corresponding to the terminator branch being taken (including unonditoinal goto) - inline const ControlFlowEdge* takenEdgep() const; - // The edge corresponding to the terminator branch being not taken (or nullptr if goto) - inline const ControlFlowEdge* untknEdgep() const; - // Same as takenpEdgep/untknEdgep but returns the successor basic blocks - inline const BasicBlock* takenSuccessorp() const; - inline const BasicBlock* untknSuccessorp() const; + // Ordering is done on reverese post-order numbering. For loop-free graph + // this ensures that a block that compares less than another is not a + // successor of the other block (it is an ancestor, or sibling). + bool operator<(const CfgBlock& that) const { return id() < that.id(); } + bool operator>(const CfgBlock& that) const { return id() > that.id(); } + bool operator==(const CfgBlock& that) const { return this == &that; } - void forEachSuccessor(std::function f) const { - for (const V3GraphEdge& edge : outEdges()) f(*edge.top()->as()); - } - - void forEachPredecessor(std::function f) const { - for (const V3GraphEdge& edge : inEdges()) f(*edge.fromp()->as()); + // Iterators + void forEachSuccessor(std::function f) const { + for (const V3GraphEdge& edge : outEdges()) f(*static_cast(edge.top())); + } + void forEachPredecessor(std::function f) const { + for (const V3GraphEdge& edge : inEdges()) f(*static_cast(edge.fromp())); } + // Source location for debugging FileLine* fileline() const override { return !m_stmtps.empty() ? m_stmtps.front()->fileline() : nullptr; } @@ -141,130 +175,397 @@ public: std::string dotRank() const override; }; -// A control flow graph edge -class ControlFlowEdge final : public V3GraphEdge { - VL_RTTI_IMPL(ControlFlowEdge, V3GraphEdge) - friend class CfgBuilder; +//###################################################################### +// CfgEdge - An edges of the control flow graph + +class CfgEdge final : public V3GraphEdge { + friend class CfgGraph; + template + friend class CfgMap; // STATE - Immutable after construction, set by CfgBuilder - // V3GraphEdge::user() is set to the unique id by CfgBuilder + CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under + size_t m_id; // Unique ID of this vertex - // CONSTRUCTOR - via CfgBuilder only - inline ControlFlowEdge(ControlFlowGraph* graphp, BasicBlock* srcp, BasicBlock* dstp); - ~ControlFlowEdge() override = default; + // PRIVATE METHODS + // Unique ID of this CfgEdge - no particular meaning + inline size_t id(); + inline size_t id() const; + + // CONSTRUCTOR/DESTRUCTOR - via CfgGraph only + inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp); + ~CfgEdge() override = default; public: // METHODS - all const - // Unique ID of this ControlFlowEdge - no particular meaning - size_t id() const { return user(); } - - // Source/destination BasicBlock - const BasicBlock& src() const { return *static_cast(fromp()); } - const BasicBlock& dst() const { return *static_cast(top()); } + // Source/destination CfgBlock + const CfgBlock* srcp() const { return static_cast(fromp()); } + const CfgBlock* dstp() const { return static_cast(top()); } + CfgBlock* srcp() { return static_cast(fromp()); } + CfgBlock* dstp() { return static_cast(top()); } // For Graphviz dumps only std::string dotLabel() const override; }; -template -using BasicBlockMap = DenseMap; +//###################################################################### +// CfgGraph - The control flow graph -template -using ControlFlowEdgeMap = DenseMap; - -// The control flow graph -class ControlFlowGraph final : public V3Graph { +class CfgGraph final : public V3Graph { + friend class CfgBlock; + friend class CfgEdge; + template + friend class CfgMap; friend class CfgBuilder; - // STATE - Immutable after construction, set by CfgBuilder - BasicBlock* m_enterp = nullptr; // The singular entry vertex - BasicBlock* m_exitp = nullptr; // The singular exit vertex - size_t m_nBasicBlocks = 0; // Number of BasicBlocks in this ControlFlowGraph - size_t m_nEdges = 0; // Number of ControlFlowEdges in this ControlFlowGraph + // STATE + size_t m_nEdits = 0; // Edit count of this graph + size_t m_nLastOrdered = 0; // Last edit count blocks were ordered + CfgBlock* m_enterp = nullptr; // The singular entry vertex + CfgBlock* m_exitp = nullptr; // The singular exit vertex + size_t m_nBlocks = 0; // Number of CfgBlocks in this CfgGraph + size_t m_nEdges = 0; // Number of CfgEdges in this CfgGraph - // CONSTRUCTOR - via CfgBuilder only - ControlFlowGraph() = default; + // PRIVATE METHODS -public: - ~ControlFlowGraph() override = default; + // Compute reverse post-order enumeration of blocks, and sort them + // accordingly. Assign blocks, and edge IDs. Invalidates all previous IDs. + void rpoBlocks(); - // METHODS - void foreach(std::function f) const { - for (const V3GraphVertex& vtx : vertices()) f(*vtx.as()); + // Add a new CfgBlock to this graph + CfgBlock* addBlock() { + ++m_nEdits; + ++m_nBlocks; + return new CfgBlock{this}; } + // Add a new taken (or unconditional) CfgEdge to this CFG + void addTakenEdge(CfgBlock* srcp, CfgBlock* dstp) { + UASSERT_OBJ(srcp->m_cfgp == this, srcp, "'srcp' is not in this graph"); + UASSERT_OBJ(dstp->m_cfgp == this, dstp, "'dstp' is not in this graph"); + UASSERT_OBJ(!srcp->takenEdgep(), srcp, "Taken edge should be added first"); + // + UASSERT_OBJ(dstp != m_enterp, dstp, "Enter block cannot have a predecessor"); + UASSERT_OBJ(srcp != m_exitp, srcp, "Exit block cannot have a successor"); + ++m_nEdits; + ++m_nEdges; + new CfgEdge{this, srcp, dstp}; + } + + // Add a new untaken CfgEdge to this CFG + void addUntknEdge(CfgBlock* srcp, CfgBlock* dstp) { + UASSERT_OBJ(srcp->m_cfgp == this, srcp, "'srcp' is not in this graph"); + UASSERT_OBJ(dstp->m_cfgp == this, dstp, "'dstp' is not in this graph"); + UASSERT_OBJ(srcp->takenEdgep(), srcp, "Untaken edge shold be added second"); + UASSERT_OBJ(srcp->takenp() != dstp, srcp, "Untaken branch targets the same block"); + // + UASSERT_OBJ(dstp != m_enterp, dstp, "Enter block cannot have a predecessor"); + UASSERT_OBJ(srcp != m_exitp, srcp, "Exit block cannot have a successor"); + ++m_nEdits; + ++m_nEdges; + new CfgEdge{this, srcp, dstp}; + } + + size_t idOf(const CfgBlock* bbp) const { + UASSERT_OBJ(m_nEdits == m_nLastOrdered, m_enterp, "Cfg was edited but not re-ordered"); + return bbp->m_rpoNumber; + } + + size_t idOf(const CfgEdge* edgep) const { + UASSERT_OBJ(m_nEdits == m_nLastOrdered, m_enterp, "Cfg was edited but not re-ordered"); + return edgep->m_id; + } + + // Implementation for insertTwoWayJoins + CfgBlock* getOrCreateTwoWayJoinFor(CfgBlock* bbp); + + // CONSTRUCTOR - use CfgGraph::build, which might fail, so this can't be public + CfgGraph() = default; + +public: + ~CfgGraph() override = default; + + // STATIC FUNCTIONS + + // Build CFG for the given list of statements + static std::unique_ptr build(AstNode* stmtsp); + + // PUBLIC METHODS + // Accessors - const BasicBlock& enter() const { return *m_enterp; } - const BasicBlock& exit() const { return *m_exitp; } + const CfgBlock& enter() const { return *m_enterp; } + const CfgBlock& exit() const { return *m_exitp; } // Number of basic blocks in this graph - size_t nBasicBlocks() const { return m_nBasicBlocks; } + size_t nBlocks() const { return m_nBlocks; } // Number of control flow edges in this graph size_t nEdges() const { return m_nEdges; } - // Create a BasicBlock map for this graph + // Create a CfgBlock map for this graph template - BasicBlockMap makeBasicBlockMap() const { - return BasicBlockMap{nBasicBlocks()}; - } - // Create a ControlFlowEdgeMap map for this graph + inline CfgBlockMap makeBlockMap() const; + // Create a CfgEdgeMap map for this graph template - ControlFlowEdgeMap makeEdgeMap() const { - return ControlFlowEdgeMap{nEdges()}; - } + inline CfgEdgeMap makeEdgeMap() const; // Returns true iff the graph contains a loop (back-edge) bool containsLoop() const; + + //------------------------------------------------------------------ + // The following methods mutate this CFG and invalidate CfgBlock and + // CfgEdge IDs and associated CfgBlockMap, CfgEdgeMap and other + // query instances. + + // Remove empty blocks, combine sequential blocks. Keeps the enter/exit block, even if empty. + void minimize(); + + // Insert empty blocks to fix critical edges (edges that have a source with + // multiple successors, and a destination with multiple predecessors) + void breakCriticalEdges(); + + bool insertTwoWayJoins(); }; //###################################################################### -// Inline method definitions +// CfgMap - Map from CfgBlock or CfgEdge to generic values -BasicBlock::BasicBlock(ControlFlowGraph* cfgp) - : V3GraphVertex{cfgp} {} +template +class CfgMap VL_NOT_FINAL { + // As opposed to a std::map/std::unordered_map, all entries are value + // initialized at construction, and the map is always dense. This can + // be improved if necessary but is sufficient for our current purposes. -const ControlFlowEdge* BasicBlock::takenEdgep() const { - // It's always the first edge - const V3GraphEdge* const frontp = outEdges().frontp(); - return static_cast(frontp); -} + const CfgGraph* m_cfgp; // The control flow graph this map is for + size_t m_created; // Edit count of CFG this map was created at + std::vector m_map; // The map, stored as a vector -const ControlFlowEdge* BasicBlock::untknEdgep() const { - // It's always the second (last) edge - const V3GraphEdge* const frontp = outEdges().frontp(); - const V3GraphEdge* const backp = outEdges().backp(); - return backp != frontp ? static_cast(backp) : nullptr; -} +protected: + // CONSTRUCTOR + explicit CfgMap(const CfgGraph* cfgp, size_t size) + : m_cfgp{cfgp} + , m_created{cfgp->m_nEdits} + , m_map{size} {} -const BasicBlock* BasicBlock::takenSuccessorp() const { - const ControlFlowEdge* const edgep = takenEdgep(); - return edgep ? static_cast(edgep->top()) : nullptr; -} +public: + // Can create an empty map + CfgMap() + : m_cfgp{nullptr} + , m_created{0} {} -const BasicBlock* BasicBlock::untknSuccessorp() const { - const ControlFlowEdge* const edgep = untknEdgep(); - return edgep ? static_cast(edgep->top()) : nullptr; -} + // Copyable, movable + CfgMap(const CfgMap&) = default; + CfgMap(CfgMap&&) = default; + CfgMap& operator=(const CfgMap&) = default; + CfgMap& operator=(CfgMap&&) = default; -ControlFlowEdge::ControlFlowEdge(ControlFlowGraph* graphp, BasicBlock* srcp, BasicBlock* dstp) - : V3GraphEdge{graphp, srcp, dstp, 1, false} {} + T_Value& operator[](const T_Key& key) { + UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale"); + UASSERT_OBJ(m_cfgp == key.m_cfgp, m_cfgp->m_enterp, "Key not in this CFG"); + return m_map.at(key.id()); + } + const T_Value& operator[](const T_Key& key) const { + UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale"); + UASSERT_OBJ(m_cfgp == key.m_cfgp, m_cfgp->m_enterp, "Key not in this CFG"); + return m_map.at(key.id()); + } + T_Value& operator[](const T_Key* keyp) { + UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale"); + UASSERT_OBJ(m_cfgp == keyp->m_cfgp, m_cfgp->m_enterp, "Key not in this CFG"); + return m_map.at(keyp->id()); + } + const T_Value& operator[](const T_Key* keyp) const { + UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale"); + UASSERT_OBJ(m_cfgp == keyp->m_cfgp, m_cfgp->m_enterp, "Key not in this CFG"); + return m_map.at(keyp->id()); + } +}; + +template +class CfgBlockMap final : public CfgMap { + friend class CfgGraph; + // CONSTRUCTOR - Create one via CfgGraph::makeBlockMap + explicit CfgBlockMap(const CfgGraph* cfgp) + : CfgMap{cfgp, cfgp->nBlocks()} {} + +public: + // Can create an empty map + CfgBlockMap() = default; + // Copyable, movable + CfgBlockMap(const CfgBlockMap&) = default; + CfgBlockMap(CfgBlockMap&&) = default; + CfgBlockMap& operator=(const CfgBlockMap&) = default; + CfgBlockMap& operator=(CfgBlockMap&&) = default; +}; + +template +class CfgEdgeMap final : public CfgMap { + friend class CfgGraph; + // CONSTRUCTOR - Create one via CfgGraph::makeEdgeMap + explicit CfgEdgeMap(const CfgGraph* cfgp) + : CfgMap{cfgp, cfgp->nEdges()} {} + +public: + // Can create an empty map + CfgEdgeMap() = default; + // Copyable, movable + CfgEdgeMap(const CfgEdgeMap&) = default; + CfgEdgeMap(CfgEdgeMap&&) = default; + CfgEdgeMap& operator=(const CfgEdgeMap&) = default; + CfgEdgeMap& operator=(CfgEdgeMap&&) = default; +}; //###################################################################### -// ControlFlowGraph functions +// Innline method definitions + +// --- CfgBlock --- + +CfgBlock::CfgBlock(CfgGraph* cfgp) + : V3GraphVertex{cfgp} + , m_cfgp{cfgp} {} + +size_t CfgBlock::id() { return m_cfgp->idOf(this); } +size_t CfgBlock::id() const { return m_cfgp->idOf(this); } + +// Successor edges +CfgEdge* CfgBlock::takenEdgep() { // It's always the first edge + return isExit() ? nullptr : static_cast(outEdges().frontp()); +} +const CfgEdge* CfgBlock::takenEdgep() const { // It's always the first edge + return isExit() ? nullptr : static_cast(outEdges().frontp()); +} +CfgEdge* CfgBlock::untknEdgep() { // It's always the second (last) edge + return isBranch() ? static_cast(outEdges().backp()) : nullptr; +} +const CfgEdge* CfgBlock::untknEdgep() const { // It's always the second (last) edge + return isBranch() ? static_cast(outEdges().backp()) : nullptr; +} +CfgBlock* CfgBlock::takenp() { + return isExit() ? nullptr : static_cast(outEdges().frontp()->top()); +} +const CfgBlock* CfgBlock::takenp() const { + return isExit() ? nullptr : static_cast(outEdges().frontp()->top()); +} +CfgBlock* CfgBlock::untknp() { + return isBranch() ? static_cast(outEdges().backp()->top()) : nullptr; +} +const CfgBlock* CfgBlock::untknp() const { + return isBranch() ? static_cast(outEdges().backp()->top()) : nullptr; +} + +// Predecessor edges +CfgEdge* CfgBlock::firstPredecessorEdgep() { // + return static_cast(inEdges().frontp()); +} +const CfgEdge* CfgBlock::firstPredecessorEdgep() const { // + return static_cast(inEdges().frontp()); +} +CfgEdge* CfgBlock::lastPredecessorEdgep() { // + return static_cast(inEdges().backp()); +} +const CfgEdge* CfgBlock::lastPredecessorEdgep() const { // + return static_cast(inEdges().backp()); +} +CfgBlock* CfgBlock::firstPredecessorp() { // + return isEnter() ? nullptr : firstPredecessorEdgep()->srcp(); +} +const CfgBlock* CfgBlock::firstPredecessorp() const { // + return isEnter() ? nullptr : firstPredecessorEdgep()->srcp(); +} +CfgBlock* CfgBlock::lastPredecessorp() { // + return isEnter() ? nullptr : lastPredecessorEdgep()->srcp(); +} +const CfgBlock* CfgBlock::lastPredecessorp() const { // + return isEnter() ? nullptr : lastPredecessorEdgep()->srcp(); +} + +// --- CfgEdge --- + +CfgEdge::CfgEdge(CfgGraph* cfgp, CfgBlock* srcp, CfgBlock* dstp) + : V3GraphEdge{cfgp, srcp, dstp, 1, false} + , m_cfgp{cfgp} {} + +size_t CfgEdge::id() { return m_cfgp->idOf(this); } +size_t CfgEdge::id() const { return m_cfgp->idOf(this); } + +// --- CfgGraph --- + +template +CfgBlockMap CfgGraph::makeBlockMap() const { + return CfgBlockMap{this}; +} +template +CfgEdgeMap CfgGraph::makeEdgeMap() const { + return CfgEdgeMap{this}; +} + +//###################################################################### +// CfgDominatorTree + +class CfgDominatorTree final { + // STATE + CfgBlockMap m_bb2Idom; // Map from CfgBlock to its immediate dominator + + // PRIVATE METHODS + + // Part of algorithm to compute m_bb2Idom, see consructor + const CfgBlock* intersect(const CfgBlock* ap, const CfgBlock* bp); + +public: + // CONSTRUCTOR + explicit CfgDominatorTree(const CfgGraph& cfg); + // Can create an empty map + CfgDominatorTree() = default; + // Copyable, movable + CfgDominatorTree(const CfgDominatorTree&) = default; + CfgDominatorTree(CfgDominatorTree&&) = default; + CfgDominatorTree& operator=(const CfgDominatorTree&) = default; + CfgDominatorTree& operator=(CfgDominatorTree&&) = default; + + // PUBLIC METHODS + + // Return unique CfgBlock that dominates both of the given blocks, but does + // not strictly dominate any other block that dominates both blocks. It + // will return 'ap' or 'bp' if one dominates the other (or are the same) + const CfgBlock* closestCommonDominator(const CfgBlock* ap, const CfgBlock* bp) const { + while (ap != bp) { + if (*ap < *bp) { + bp = m_bb2Idom[bp]; + } else { + ap = m_bb2Idom[ap]; + } + } + return ap; + } + + // Returns true if 'ap' dominates 'bp' + bool dominates(const CfgBlock* const ap, const CfgBlock* bp) { + // Walk up the dominator tree from 'bp' until reaching 'ap' or the root + while (true) { + // True if 'ap' is above (or same as) 'bp' + if (ap == bp) return true; + // Step up the dominator tree + bp = m_bb2Idom[bp]; + // False if reached the root + if (!bp) return false; + } + } +}; + +//###################################################################### +// V3Cfg namespace V3Cfg { -// Build control flow graph for given node -std::unique_ptr build(const AstNodeProcedure*); // Compute AstVars live on entry to given CFG. That is, variables that might // be read before wholly assigned in the CFG. Returns nullptr if the analysis // failed due to unhandled statements or data types involved in the CFG. // On success, returns a vector of AstVar or AstVarScope nodes live on entry. -std::unique_ptr> liveVars(const ControlFlowGraph&); +std::unique_ptr> liveVars(const CfgGraph&); // Same as liveVars, but return AstVarScopes insted -std::unique_ptr> liveVarScopes(const ControlFlowGraph&); +std::unique_ptr> liveVarScopes(const CfgGraph&); + } //namespace V3Cfg #endif // VERILATOR_V3CFG_H_ diff --git a/src/V3CfgBuilder.cpp b/src/V3CfgBuilder.cpp index a4b315b99..407c0aba3 100644 --- a/src/V3CfgBuilder.cpp +++ b/src/V3CfgBuilder.cpp @@ -31,35 +31,18 @@ VL_DEFINE_DEBUG_FUNCTIONS; -class CfgBuilder final : VNVisitorConst { +class CfgBuilder final : public VNVisitorConst { // STATE // The graph being built, or nullptr if failed to build one - std::unique_ptr m_cfgp{new ControlFlowGraph}; + std::unique_ptr m_cfgp{new CfgGraph}; // Current basic block to add statements to - BasicBlock* m_currBBp = nullptr; + CfgBlock* m_currBBp = nullptr; // Continuation block for given JumpBlock - std::unordered_map m_jumpBlockContp; + std::unordered_map m_jumpBlockContp; // METHODS - // Create new Basicblock block in the CFG - BasicBlock& addBasicBlock() { return *new BasicBlock{m_cfgp.get()}; } - - // Create new taken (or unconditional) ControlFlowEdge in the CFG - void addTakenEdge(BasicBlock& src, BasicBlock& dst) { - UASSERT_OBJ(src.outEmpty(), &src, "Taken edge should be added first"); - new ControlFlowEdge{m_cfgp.get(), &src, &dst}; - } - - // Create new untaken ControlFlowEdge in the CFG - void addUntknEdge(BasicBlock& src, BasicBlock& dst) { - UASSERT_OBJ(src.outSize1(), &src, "Untaken edge shold be added second"); - UASSERT_OBJ(src.outEdges().frontp()->top() != &dst, &src, - "Untaken branch targets the same block as the taken branch"); - new ControlFlowEdge{m_cfgp.get(), &src, &dst}; - } - - // Add the given statement to the current BasicBlock + // Add the given statement to the current CfgBlock void addStmt(AstNodeStmt* nodep) { m_currBBp->m_stmtps.emplace_back(nodep); } // Used to handle statements not representable in the CFG @@ -115,71 +98,71 @@ class CfgBuilder final : VNVisitorConst { addStmt(nodep); // Create then/else/continuation blocks - BasicBlock& thenBB = addBasicBlock(); - BasicBlock& elseBB = addBasicBlock(); - BasicBlock& contBB = addBasicBlock(); - addTakenEdge(*m_currBBp, thenBB); - addUntknEdge(*m_currBBp, elseBB); + CfgBlock* const thenBBp = m_cfgp->addBlock(); + CfgBlock* const elseBBp = m_cfgp->addBlock(); + CfgBlock* const contBBp = m_cfgp->addBlock(); + m_cfgp->addTakenEdge(m_currBBp, thenBBp); + m_cfgp->addUntknEdge(m_currBBp, elseBBp); // Build then branch - m_currBBp = &thenBB; + m_currBBp = thenBBp; iterateAndNextConstNull(nodep->thensp()); if (!m_cfgp) return; - if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, contBBp); // Build else branch - m_currBBp = &elseBB; + m_currBBp = elseBBp; iterateAndNextConstNull(nodep->elsesp()); if (!m_cfgp) return; - if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, contBBp); // Set continuation - m_currBBp = &contBB; + m_currBBp = contBBp; } void visit(AstWhile* nodep) override { if (!m_cfgp) return; // Create the header block - BasicBlock& headBB = addBasicBlock(); - addTakenEdge(*m_currBBp, headBB); + CfgBlock* const headBBp = m_cfgp->addBlock(); + m_cfgp->addTakenEdge(m_currBBp, headBBp); // The While goes in the header block - semantically the condition check only ... - m_currBBp = &headBB; + m_currBBp = headBBp; addStmt(nodep); // Create the body/continuation blocks - BasicBlock& bodyBB = addBasicBlock(); - BasicBlock& contBB = addBasicBlock(); - addTakenEdge(headBB, bodyBB); - addUntknEdge(headBB, contBB); + CfgBlock* const bodyBBp = m_cfgp->addBlock(); + CfgBlock* const contBBp = m_cfgp->addBlock(); + m_cfgp->addTakenEdge(headBBp, bodyBBp); + m_cfgp->addUntknEdge(headBBp, contBBp); // Build the body - m_currBBp = &bodyBB; + m_currBBp = bodyBBp; iterateAndNextConstNull(nodep->stmtsp()); iterateAndNextConstNull(nodep->incsp()); if (!m_cfgp) return; - if (m_currBBp) addTakenEdge(*m_currBBp, headBB); + if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, headBBp); // Set continuation - m_currBBp = &contBB; + m_currBBp = contBBp; } void visit(AstJumpBlock* nodep) override { if (!m_cfgp) return; - // Don't acutally need to add this 'nodep' to any block - but we could later if needed + // Don't acutally need to add this 'nodep' to any block // Create continuation block - BasicBlock& contBB = addBasicBlock(); - const bool newEntry = m_jumpBlockContp.emplace(nodep, &contBB).second; + CfgBlock* const contBBp = m_cfgp->addBlock(); + const bool newEntry = m_jumpBlockContp.emplace(nodep, contBBp).second; UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twice"); // Build the body iterateAndNextConstNull(nodep->stmtsp()); if (!m_cfgp) return; - if (m_currBBp) addTakenEdge(*m_currBBp, contBB); + if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, contBBp); // Set continuation - m_currBBp = &contBB; + m_currBBp = contBBp; } void visit(AstJumpGo* nodep) override { if (!m_cfgp) return; @@ -190,102 +173,61 @@ class CfgBuilder final : VNVisitorConst { return; } - // Don't acutally need to add this 'nodep' to any block - but we could later if needed + // Don't acutally need to add this 'nodep' to any block // Make current block go to the continuation of the JumpBlock - addTakenEdge(*m_currBBp, *m_jumpBlockContp.at(nodep->blockp())); + m_cfgp->addTakenEdge(m_currBBp, m_jumpBlockContp.at(nodep->blockp())); // There should be no statements after a JumpGo! m_currBBp = nullptr; } // CONSTRUCTOR - explicit CfgBuilder(const AstNodeProcedure* nodep) { + explicit CfgBuilder(AstNode* stmtsp) { // Build the graph, starting from the entry block - m_currBBp = &addBasicBlock(); + m_currBBp = m_cfgp->addBlock(); m_cfgp->m_enterp = m_currBBp; // Visit each statement to build the control flow graph - iterateAndNextConstNull(nodep->stmtsp()); + iterateAndNextConstNull(stmtsp); + // If failed, stop now if (!m_cfgp) return; // The final block is the exit block m_cfgp->m_exitp = m_currBBp; - - // Remove empty blocks - except enter/exit - for (V3GraphVertex* const vtxp : m_cfgp->vertices().unlinkable()) { - if (vtxp == &m_cfgp->enter()) continue; - if (vtxp == &m_cfgp->exit()) continue; - BasicBlock* const bbp = vtxp->as(); - if (!bbp->stmtps().empty()) continue; - UASSERT(bbp->outSize1(), "Empty block should have a single successor"); - BasicBlock* const succp = const_cast(bbp->takenSuccessorp()); - for (V3GraphEdge* const edgep : bbp->inEdges().unlinkable()) edgep->relinkTop(succp); - vtxp->unlinkDelete(m_cfgp.get()); - } - // Remove redundant entry block - while (m_cfgp->enter().stmtps().empty() && m_cfgp->enter().outSize1()) { - BasicBlock* const succp = m_cfgp->enter().outEdges().frontp()->top()->as(); - if (!succp->inSize1()) break; - m_cfgp->m_enterp->unlinkDelete(m_cfgp.get()); - m_cfgp->m_enterp = succp; - } - // Remove redundant exit block - while (m_cfgp->exit().stmtps().empty() && m_cfgp->exit().inSize1()) { - BasicBlock* const prep = m_cfgp->exit().inEdges().frontp()->fromp()->as(); - if (!prep->outSize1()) break; - m_cfgp->m_exitp->unlinkDelete(m_cfgp.get()); - m_cfgp->m_exitp = prep; - } - - // Compute reverse post-order enumeration and sort blocks, assign IDs + // Some blocks might not have predecessors if they are unreachable, remove them { - // Simple recursive algorith will do ... - std::vector postOrderEnumeration; - std::unordered_set visited; - const std::function visitBasicBlock = [&](BasicBlock* bbp) { - // Mark and skip if already visited - if (!visited.emplace(bbp).second) return; - // Visit successors - for (const V3GraphEdge& e : bbp->outEdges()) { - visitBasicBlock(static_cast(e.top())); + std::vector unreachableps; + for (V3GraphVertex* const vtxp : m_cfgp->vertices().unlinkable()) { + if (vtxp == m_cfgp->m_enterp) continue; + if (vtxp == m_cfgp->m_exitp) continue; + UASSERT_OBJ(!vtxp->outEmpty(), vtxp, "Block with no successor other than exit"); + if (vtxp->inEmpty()) unreachableps.emplace_back(vtxp); + } + while (!unreachableps.empty()) { + V3GraphVertex* const vtxp = unreachableps.back(); + unreachableps.pop_back(); + for (V3GraphEdge& edge : vtxp->outEdges()) { + --m_cfgp->m_nEdges; + if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top()); } - // Add to post order enumeration - postOrderEnumeration.emplace_back(bbp); - }; - visitBasicBlock(m_cfgp->m_enterp); - const uint32_t n = postOrderEnumeration.size(); - UASSERT_OBJ(n == m_cfgp->vertices().size(), nodep, "Inconsistent enumeration size"); - - // Set size in graph - m_cfgp->m_nBasicBlocks = n; - - // Assign ids equal to the reverse post order number and sort vertices - for (uint32_t i = 0; i < postOrderEnumeration.size(); ++i) { - BasicBlock* const bbp = postOrderEnumeration[n - 1 - i]; - bbp->user(i); - m_cfgp->vertices().unlink(bbp); - m_cfgp->vertices().linkBack(bbp); + --m_cfgp->m_nBlocks; + VL_DO_DANGLING(vtxp->unlinkDelete(m_cfgp.get()), vtxp); } } - - // Assign IDs to edges - { - size_t nEdges = 0; - for (V3GraphVertex& v : m_cfgp->vertices()) { - for (V3GraphEdge& e : v.outEdges()) e.user(nEdges++); - } - // Set size in graph - m_cfgp->m_nEdges = nEdges; + // Dump the initial graph + if (dumpGraphLevel() >= 9) { + m_cfgp->rpoBlocks(); + m_cfgp->dumpDotFilePrefixed("cfg-builder-initial"); } - - if (dumpGraphLevel() >= 9) m_cfgp->dumpDotFilePrefixed("cfgbuilder"); + // Minimize it + m_cfgp->minimize(); + // Dump the final graph + if (dumpGraphLevel() >= 8) m_cfgp->dumpDotFilePrefixed("cfg-builder"); } public: - static std::unique_ptr apply(const AstNodeProcedure* nodep) { - return std::move(CfgBuilder{nodep}.m_cfgp); + static std::unique_ptr apply(AstNode* stmtsp) { + return std::move(CfgBuilder{stmtsp}.m_cfgp); } }; -std::unique_ptr V3Cfg::build(const AstNodeProcedure* nodep) { - return CfgBuilder::apply(nodep); -} +std::unique_ptr CfgGraph::build(AstNode* stmtsp) { return CfgBuilder::apply(stmtsp); } diff --git a/src/V3CfgLiveVariables.cpp b/src/V3CfgLiveVariables.cpp index b0db3aa76..f8db52ba7 100644 --- a/src/V3CfgLiveVariables.cpp +++ b/src/V3CfgLiveVariables.cpp @@ -49,9 +49,9 @@ class CfgLiveVariables final : VNVisitorConst { }; // STATE - const ControlFlowGraph& m_cfg; // The CFG beign analysed + const CfgGraph& m_cfg; // The CFG beign analysed // State for each block - BasicBlockMap m_blockState = m_cfg.makeBasicBlockMap(); + CfgBlockMap m_blockState{m_cfg.makeBlockMap()}; BlockState* m_currp = nullptr; // State of current block being analysed bool m_abort = false; // Abort analysis - unhandled construct @@ -140,7 +140,7 @@ class CfgLiveVariables final : VNVisitorConst { } // Apply transfer function of block, return true if changed - bool transfer(const BasicBlock& bb) { + bool transfer(const CfgBlock& bb) { BlockState& state = m_blockState[bb]; // liveIn = gen union (liveOut - kill) @@ -172,20 +172,21 @@ class CfgLiveVariables final : VNVisitorConst { void visit(AstWhile* nodep) override { single(nodep->condp()); } // CONSTRUCTOR - explicit CfgLiveVariables(const ControlFlowGraph& cfg) + explicit CfgLiveVariables(const CfgGraph& cfg) : m_cfg{cfg} { // For each basic block, compute the gen and kill set via visit - cfg.foreach([&](const BasicBlock& bb) { + for (const V3GraphVertex& vtx : cfg.vertices()) { + const CfgBlock& bb = static_cast(vtx); if (m_abort) return; VL_RESTORER(m_currp); m_currp = &m_blockState[bb]; for (AstNode* const stmtp : bb.stmtps()) iterateConst(stmtp); - }); + } if (m_abort) return; // Perform the flow analysis - std::deque workList; - const auto enqueue = [&](const BasicBlock& bb) { + std::deque workList; + const auto enqueue = [&](const CfgBlock& bb) { BlockState& state = m_blockState[bb]; if (state.m_isOnWorkList) return; state.m_isOnWorkList = true; @@ -195,13 +196,13 @@ class CfgLiveVariables final : VNVisitorConst { enqueue(cfg.exit()); while (!workList.empty()) { - const BasicBlock* const currp = workList.front(); + const CfgBlock* const currp = workList.front(); workList.pop_front(); BlockState& state = m_blockState[*currp]; state.m_isOnWorkList = false; // Compute meet (liveOut = union liveIn of successors) - currp->forEachSuccessor([&](const BasicBlock& bb) { + currp->forEachSuccessor([&](const CfgBlock& bb) { auto& liveIn = m_blockState[bb].m_liveIn; state.m_liveOut.insert(liveIn.begin(), liveIn.end()); }); @@ -216,7 +217,7 @@ class CfgLiveVariables final : VNVisitorConst { } public: - static std::unique_ptr> apply(const ControlFlowGraph& cfg) { + static std::unique_ptr> apply(const CfgGraph& cfg) { CfgLiveVariables analysis{cfg}; // If failed, return nullptr if (analysis.m_abort) return nullptr; @@ -231,10 +232,10 @@ public: } }; -std::unique_ptr> V3Cfg::liveVars(const ControlFlowGraph& cfg) { +std::unique_ptr> V3Cfg::liveVars(const CfgGraph& cfg) { return CfgLiveVariables::apply(cfg); } -std::unique_ptr> V3Cfg::liveVarScopes(const ControlFlowGraph& cfg) { +std::unique_ptr> V3Cfg::liveVarScopes(const CfgGraph& cfg) { return CfgLiveVariables::apply(cfg); } diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index ec18a8212..57e310df7 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -57,7 +57,7 @@ class AstToDfgVisitor final : public VNVisitor { } } - std::unique_ptr> getLiveVariables(const ControlFlowGraph& cfg) { + std::unique_ptr> getLiveVariables(const CfgGraph& cfg) { // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works if VL_CONSTEXPR_CXX17 (T_Scoped) { std::unique_ptr> result = V3Cfg::liveVarScopes(cfg); @@ -139,7 +139,7 @@ class AstToDfgVisitor final : public VNVisitor { // Gather variables live in to the given CFG. // Return nullptr if any are not supported. - std::unique_ptr> gatherLive(const ControlFlowGraph& cfg) { + std::unique_ptr> gatherLive(const CfgGraph& cfg) { // Run analysis std::unique_ptr> varps = getLiveVariables(cfg); if (!varps) { @@ -206,7 +206,7 @@ class AstToDfgVisitor final : public VNVisitor { // Potentially convertible block ++m_ctx.m_inputs; // Attempt to build CFG of AstAlways, give up if failed - std::unique_ptr cfgp = V3Cfg::build(nodep); + std::unique_ptr cfgp = CfgGraph::build(nodep->stmtsp()); if (!cfgp) { ++m_ctx.m_nonRepCfg; return false; diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index a32daedd0..a95be5b48 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -251,7 +251,13 @@ public: // Reverted VDouble0 revertNonSyn; // Reverted due to being driven from non-synthesizable vertex VDouble0 revertMultidrive; // Reverted due to multiple drivers - + // Additional stats + VDouble0 cfgTrivial; // Trivial input CFGs + VDouble0 cfgSp; // Series-paralel input CFGs + VDouble0 cfgDag; // Generic loop free input CFGs + VDouble0 cfgCyclic; // Cyclic input CFGs + VDouble0 joinUsingPathPredicate; // Num control flow joins using full path predicate + VDouble0 joinUsingBranchCondition; // Num Control flow joins using dominating branch cond } m_synt; private: @@ -314,6 +320,14 @@ private: nSyntExpect -= m_synt.synthAlways; nSyntExpect -= m_synt.synthAssign; UASSERT(nSyntNonSyn == nSyntExpect, "Inconsistent statistics / synt"); + + addStat("synt / input CFG trivial", m_synt.cfgTrivial); + addStat("synt / input CFG sp", m_synt.cfgSp); + addStat("synt / input CFG dag", m_synt.cfgDag); + addStat("synt / input CFG cyclic", m_synt.cfgCyclic); + + addStat("synt / joins using path predicate", m_synt.joinUsingPathPredicate); + addStat("synt / joins using branch condition", m_synt.joinUsingBranchCondition); } }; diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 3819542ec..230c73f70 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -508,15 +508,23 @@ class AstToDfgSynthesize final { bool operator<=(const Driver& other) const { return !(other < *this); } }; - // STATE + // STATE - Persistent DfgGraph& m_dfg; // The graph being built V3DfgSynthesisContext& m_ctx; // The context for stats AstToDfgConverter m_converter; // The convert instance to use for each construct - DfgLogic* m_logicp = nullptr; // Current logic vertex we are synthesizing - // Some debug aid: We stop after synthesizing s_dfgSynthDebugLimit vertices (if non-zero). - // This is the problematic logic (last one we synthesize), assuming a bisection search - // over s_dfgSynthDebugLimit. + // STATE - for current DfgLogic being synthesized + DfgLogic* m_logicp = nullptr; // Current logic vertex we are synthesizing + CfgBlockMap m_bbToISymTab; // Map from CfgBlock -> input symbol table + CfgBlockMap m_bbToOSymTab; // Map from CfgBlock -> output symbol table + CfgBlockMap m_bbToCondp; // Map from CfgBlock -> terminating branch condition + CfgEdgeMap m_edgeToPredicatep; // Map CfgGraphEdge -> path predicate to get there + CfgDominatorTree m_domTree; // The dominator tree of the current CFG + + // STATE - Some debug aid + // We stop after synthesizing s_dfgSynthDebugLimit vertices (if non-zero). + // This is the problematic logic (last one we synthesize), assuming a + // bisection search over s_dfgSynthDebugLimit. DfgLogic* m_debugLogicp = nullptr; // Source (upstream) cone of outputs of m_debugLogicp std::unique_ptr> m_debugOSrcConep{nullptr}; @@ -869,7 +877,7 @@ class AstToDfgSynthesize final { }); } - // Initialzie input symbol table of entry BasicBlock + // Initialzie input symbol table of entry CfgBlock void initializeEntrySymbolTable(SymTab& iSymTab) { m_logicp->forEachSource([&](DfgVertex& src) { DfgVertexVar* const vvp = src.as(); @@ -991,75 +999,152 @@ class AstToDfgSynthesize final { return joinp; } + // Merge 'thenSymTab' into 'elseSymTab' using the given predicate to join values + bool joinSymbolTables(SymTab& elseSymTab, DfgVertex* predicatep, const SymTab& thenSymTab) { + // Give up if something is not assigned on all paths ... Latch? + if (thenSymTab.size() != elseSymTab.size()) { + ++m_ctx.m_synt.nonSynLatch; + return false; + } + // Join each symbol + for (std::pair& pair : elseSymTab) { + Variable* const varp = pair.first; + // Find same variable on the else path + auto it = thenSymTab.find(varp); + // Give up if something is not assigned on all paths ... Latch? + if (it == thenSymTab.end()) { + ++m_ctx.m_synt.nonSynLatch; + return false; + } + // Join paths with the predicate + DfgVertexVar* const thenp = it->second; + DfgVertexVar* const elsep = pair.second; + DfgVertexVar* const newp = joinDrivers(varp, predicatep, thenp, elsep); + if (!newp) return false; + pair.second = newp; + } + // Done + return true; + } + + // Given two joining control flow edges, compute how to join their symbols. + // Returns the predicaete to join over, and the 'then' and 'else' blocks. + std::tuple // + howToJoin(const CfgEdge* const ap, const CfgEdge* const bp) { + // Find the closest common dominator of the two paths + const CfgBlock* const domp = m_domTree.closestCommonDominator(ap->srcp(), bp->srcp()); + // These paths join here, so 'domp' must be a branch, otherwise it's not the closest + UASSERT_OBJ(domp->isBranch(), domp, "closestCommonDominator is not a branch"); + + // The branches of the common dominator + const CfgEdge* const takenEdgep = domp->takenEdgep(); + const CfgEdge* const untknEdgep = domp->untknEdgep(); + + // We check if the taken branch dominates the path to either blocks, + // and if the untaken branch dominates the path to the other block. + // If so, we can use the branch condition as predicate, otherwise + // we must use the path predicate as there are ways to get from one + // branch of the dominator to the other. We need to be careful if + // either branches are directly to the join block. This is fine, + // it's as if there was an empty block on that critical edge which + // is dominated by that path. + + if (takenEdgep == ap || m_domTree.dominates(takenEdgep->dstp(), ap->srcp())) { + if (untknEdgep == bp || m_domTree.dominates(untknEdgep->dstp(), bp->srcp())) { + // Taken path dominates 'ap' and untaken dominates 'bp', use the branch condition + ++m_ctx.m_synt.joinUsingBranchCondition; + return std::make_tuple(m_bbToCondp[domp], ap->srcp(), bp->srcp()); + } + } else if (takenEdgep == bp || m_domTree.dominates(takenEdgep->dstp(), bp->srcp())) { + if (untknEdgep == ap || m_domTree.dominates(untknEdgep->dstp(), ap->srcp())) { + // Taken path dominates 'bp' and untaken dominates 'ap', use the branch condition + ++m_ctx.m_synt.joinUsingBranchCondition; + return std::make_tuple(m_bbToCondp[domp], bp->srcp(), ap->srcp()); + } + } + + // The branches don't dominate the joined blocks, must use the path predicate + ++m_ctx.m_synt.joinUsingPathPredicate; + + // TODO: We could do better here: use the path predicate of the closest + // cominating blocks, pick the one from the lower rank, etc, but this + // generic case is very rare, most synthesizable logic has + // series-parallel CFGs which are covered by the earlier cases. + return std::make_tuple(m_edgeToPredicatep[ap], ap->srcp(), bp->srcp()); + } + // Combine the output symbol tables of the predecessors of the given - // BasicBlock to compute the input symtol table for the given block. - bool createInputSymbolTable(SymTab& joined, const BasicBlock& bb, - const BasicBlockMap& bbToOSymTab, - const ControlFlowEdgeMap& edgeToPredicatep) { - // Input symbol table of entry block was previously initialzied - if (bb.inEmpty()) return true; + // block to compute the input symtol table for the given block. + bool createInputSymbolTable(const CfgBlock& bb) { + // The input symbol table of the given block, we are computing it now + SymTab& joined = m_bbToISymTab[bb]; - // We will fill it in here - UASSERT(joined.empty(), "Unresolved input symbol table should be empty"); - - // Fast path if there is only one predecessor - if (bb.inSize1()) { - joined = bbToOSymTab[*(bb.inEdges().frontp()->fromp()->as())]; + // Input symbol table of entry block is special + if (bb.isEnter()) { + initializeEntrySymbolTable(joined); return true; } - // Gather predecessors and the path predicates + // Current input symbol table should be empty, we will fill it in here + UASSERT(joined.empty(), "Unprocessed input symbol table should be empty"); + + // Fast path if there is only one predecessor - TODO: use less copying + if (!bb.isJoin()) { + joined = m_bbToOSymTab[bb.firstPredecessorp()]; + return true; + } + + // We also have a simpler job if there are 2 predecessors + if (bb.isTwoWayJoin()) { + DfgVertex* predicatep = nullptr; + const CfgBlock* thenp = nullptr; + const CfgBlock* elsep = nullptr; + std::tie(predicatep, thenp, elsep) + = howToJoin(bb.firstPredecessorEdgep(), bb.lastPredecessorEdgep()); + // Copy from else + joined = m_bbToOSymTab[elsep]; + // Join with then + return joinSymbolTables(joined, predicatep, m_bbToOSymTab[*thenp]); + } + + // General hard way + + // Gather predecessors struct Predecessor final { - const BasicBlock* m_bbp; - DfgVertex* m_predicatep; + const CfgBlock* m_bbp; // Predeccessor block + DfgVertex* m_predicatep; // Predicate predecessor reached this block with + const SymTab* m_oSymTabp; // Output symbol table or predecessor Predecessor() = delete; - Predecessor(const BasicBlock* bbp, DfgVertex* predicatep) + Predecessor(const CfgBlock* bbp, DfgVertex* predicatep, const SymTab* oSymTabp) : m_bbp{bbp} - , m_predicatep{predicatep} {} + , m_predicatep{predicatep} + , m_oSymTabp{oSymTabp} {} }; const std::vector predecessors = [&]() { std::vector res; for (const V3GraphEdge& edge : bb.inEdges()) { - const ControlFlowEdge& cfgEdge = static_cast(edge); - res.emplace_back(&cfgEdge.src(), edgeToPredicatep[cfgEdge]); + const CfgEdge& cfgEdge = static_cast(edge); + const CfgBlock* const predecessorp = cfgEdge.srcp(); + DfgVertex* const predicatep = m_edgeToPredicatep[cfgEdge]; + const SymTab* const oSymTabp = &m_bbToOSymTab[predecessorp]; + res.emplace_back(predecessorp, predicatep, oSymTabp); } - // Sort predecessors topologically. This way later blocks will come - // after earlier blocks, and the entry block will be first if present. + // Sort predecessors reverse topologically. This way earlier blocks + // will come after later blocks, and the entry block is last if present. std::sort(res.begin(), res.end(), [](const Predecessor& a, const Predecessor& b) { // - return a.m_bbp->id() < b.m_bbp->id(); + return *a.m_bbp > *b.m_bbp; }); return res; }(); - // Start by copying the bindings from the oldest predecessor - joined = bbToOSymTab[*predecessors[0].m_bbp]; + // Start by copying the bindings from the frist predecessor + joined = *predecessors[0].m_oSymTabp; // Join over all other predecessors for (size_t i = 1; i < predecessors.size(); ++i) { DfgVertex* const predicatep = predecessors[i].m_predicatep; - const SymTab& oSymTab = bbToOSymTab[*predecessors[i].m_bbp]; - // Give up if something is not assigned on all paths ... Latch? - if (joined.size() != oSymTab.size()) { - ++m_ctx.m_synt.nonSynLatch; - return false; - } - // Join each symbol - for (auto& pair : joined) { - Variable* const varp = pair.first; - // Find same variable on other path - auto it = oSymTab.find(varp); - // Give up if something is not assigned on all paths ... Latch? - if (it == oSymTab.end()) { - ++m_ctx.m_synt.nonSynLatch; - return false; - } - // Join paths with the block predicate - DfgVertexVar* const thenp = it->second; - DfgVertexVar* const elsep = pair.second; - DfgVertexVar* const newp = joinDrivers(varp, predicatep, thenp, elsep); - if (!newp) return false; - pair.second = newp; - } + const SymTab& oSymTab = *predecessors[i].m_oSymTabp; + if (!joinSymbolTables(joined, predicatep, oSymTab)) return false; } return true; @@ -1169,7 +1254,7 @@ class AstToDfgSynthesize final { } // Synthesize the given statements with the given input symbol table. - // Returnt true if successfolly synthesized. + // Returns true if successfolly synthesized. // Populates the given output symbol table. // Populates the given reference with the condition of the terminator branch, if any. bool synthesizeBasicBlock(SymTab& oSymTab, DfgVertex*& condpr, @@ -1259,56 +1344,53 @@ class AstToDfgSynthesize final { return true; } - // Given a basic block, and the condition of the terminating branch (if any), - // assign perdicates to the block's outgoing control flow edges. - void assignSuccessorPredicates(ControlFlowEdgeMap& edgeToPredicatep, - const BasicBlock& bb, DfgVertex* condp) { + // Assign path perdicates to the outgoing control flow edges of the given block + void assignPathPredicates(const CfgBlock& bb) { // Nothing to do for the exit block - if (bb.outEmpty()) return; + if (bb.isExit()) return; // Get the predicate of this block DfgVertex* const predp = [&]() -> DfgVertex* { // Entry block has no predecessors, use constant true - if (bb.inEmpty()) return make(m_logicp->fileline(), 1U, 1U); + if (bb.isEnter()) return make(m_logicp->fileline(), 1U, 1U); // For any other block, 'or' together all the incoming predicates const auto& inEdges = bb.inEdges(); auto it = inEdges.begin(); - DfgVertex* resp = edgeToPredicatep[static_cast(*it)]; + DfgVertex* resp = m_edgeToPredicatep[static_cast(*it)]; while (++it != inEdges.end()) { DfgOr* const orp = make(resp->fileline(), resp->dtypep()); orp->rhsp(resp); - orp->lhsp(edgeToPredicatep[static_cast(*it)]); + orp->lhsp(m_edgeToPredicatep[static_cast(*it)]); resp = orp; } return resp; }(); - if (!condp) { - // There should be 1 successors for a block with an unconditional terminator - UASSERT_OBJ(!bb.untknEdgep(), predp, "Expecting 1 successor for BasicBlock"); - // Successor predicate edge is the same - edgeToPredicatep[*bb.takenEdgep()] = predp; - } else { - // There should be 2 successors for a block with an conditional terminator - UASSERT_OBJ(bb.untknEdgep(), predp, "Expecting 2 successors for BasicBlock"); - FileLine* const flp = condp->fileline(); - AstNodeDType* const dtypep = condp->dtypep(); // Single bit - - // Predicate for taken branch: 'predp & condp' - DfgAnd* const takenPredp = make(flp, dtypep); - takenPredp->lhsp(predp); - takenPredp->rhsp(condp); - edgeToPredicatep[*bb.takenEdgep()] = takenPredp; - - // Predicate for untaken branch: 'predp & ~condp' - DfgAnd* const untknPredp = make(flp, dtypep); - untknPredp->lhsp(predp); - DfgNot* const notp = make(flp, dtypep); - notp->srcp(condp); - untknPredp->rhsp(notp); - edgeToPredicatep[*bb.untknEdgep()] = untknPredp; + // For uncondional branches, the successor predicate edge is the same + if (!bb.isBranch()) { + m_edgeToPredicatep[bb.takenEdgep()] = predp; + return; } + + // For branches, we need to factor in the branch condition + DfgVertex* const condp = m_bbToCondp[bb]; + FileLine* const flp = condp->fileline(); + AstNodeDType* const dtypep = condp->dtypep(); // Single bit + + // Predicate for taken branch: 'predp & condp' + DfgAnd* const takenPredp = make(flp, dtypep); + takenPredp->lhsp(predp); + takenPredp->rhsp(condp); + m_edgeToPredicatep[bb.takenEdgep()] = takenPredp; + + // Predicate for untaken branch: 'predp & ~condp' + DfgAnd* const untknPredp = make(flp, dtypep); + untknPredp->lhsp(predp); + DfgNot* const notp = make(flp, dtypep); + notp->srcp(condp); + untknPredp->rhsp(notp); + m_edgeToPredicatep[bb.untknEdgep()] = untknPredp; } // Add the synthesized values as drivers to the output variables of the current DfgLogic @@ -1359,7 +1441,7 @@ class AstToDfgSynthesize final { // Initialzie input symbol table initializeEntrySymbolTable(iSymTab); - // Synthesize as if it was in a single BasicBlock CFG + // Synthesize as if it was in a single CfgBlock CFG DfgVertex* condp = nullptr; const bool success = synthesizeBasicBlock(oSymTab, condp, {assignp}, iSymTab); UASSERT_OBJ(!condp, nodep, "Conditional AstAssignW ???"); @@ -1372,7 +1454,7 @@ class AstToDfgSynthesize final { } // Synthesize the given AstAlways. Returns true on success. - bool synthesizeCfg(const ControlFlowGraph& cfg) { + bool synthesizeCfg(CfgGraph& cfg) { ++m_ctx.m_synt.inputAlways; if (hasExternallyWrittenVariable(*m_logicp)) { @@ -1383,37 +1465,48 @@ class AstToDfgSynthesize final { // If there is a backward edge (loop), we can't synthesize it if (cfg.containsLoop()) { ++m_ctx.m_synt.nonSynLoop; + ++m_ctx.m_synt.cfgCyclic; return false; } - // Maps from BasicBlock to its input and output symbol tables - BasicBlockMap bbToISymTab = cfg.makeBasicBlockMap(); - BasicBlockMap bbToOSymTab = cfg.makeBasicBlockMap(); + // If it's a trivial CFG we can save on some work + if (cfg.nBlocks() == 1) { + ++m_ctx.m_synt.cfgTrivial; + } else { + // Insert two-way join blocks to aid multiplexer ordering + if (cfg.insertTwoWayJoins()) { + ++m_ctx.m_synt.cfgSp; + } else { + ++m_ctx.m_synt.cfgDag; + } + // Initialize maps needed for non-trivial CFGs + m_domTree = CfgDominatorTree{cfg}; + m_edgeToPredicatep = cfg.makeEdgeMap(); + } - // Map from ControlFlowGraphEdge to its predicate - ControlFlowEdgeMap edgeToPredicatep = cfg.makeEdgeMap(); - - // Initialzie input symbol table of entry block - initializeEntrySymbolTable(bbToISymTab[cfg.enter()]); + // Initialize CfgMaps + m_bbToISymTab = cfg.makeBlockMap(); + m_bbToOSymTab = cfg.makeBlockMap(); + m_bbToCondp = cfg.makeBlockMap(); // Synthesize all blocks - for (const V3GraphVertex& cfgVtx : cfg.vertices()) { - const BasicBlock& bb = *cfgVtx.as(); - // Symbol tables of the block - SymTab& iSymTab = bbToISymTab[bb]; - SymTab& oSymTab = bbToOSymTab[bb]; - // Join symbol tables from predecessor blocks - if (!createInputSymbolTable(iSymTab, bb, bbToOSymTab, edgeToPredicatep)) return false; - // Condition of the terminating branch, if any - DfgVertex* condp = nullptr; - // Synthesize the block - if (!synthesizeBasicBlock(oSymTab, condp, bb.stmtps(), iSymTab)) return false; - // Set the predicates on the successor edges - assignSuccessorPredicates(edgeToPredicatep, bb, condp); + for (const V3GraphVertex& vtx : cfg.vertices()) { + const CfgBlock& bb = static_cast(vtx); + // Prepare the input symbol table of this block (enter, or join predecessor blocks) + if (!createInputSymbolTable(bb)) return false; + // Synthesize this block + if (!synthesizeBasicBlock(m_bbToOSymTab[bb], // + m_bbToCondp[bb], // + bb.stmtps(), // + m_bbToISymTab[bb])) { + return false; + } + // Set the path predicates on the successor edges + assignPathPredicates(bb); } // Add resolved output variable drivers - return addSynthesizedOutput(bbToOSymTab[cfg.exit()]); + return addSynthesizedOutput(m_bbToOSymTab[cfg.exit()]); } // Synthesize a DfgLogic into regular vertices. Returns ture on success. @@ -1676,7 +1769,7 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) { if (VN_IS(logicp->nodep(), AssignW)) return true; // Synthesize always blocks with no more than 4 basic blocks and 4 edges // These are usually simple branches (if (rst) ... else ...), or close to it - return logicp->cfg().nBasicBlocks() <= 4 && logicp->cfg().nEdges() <= 4; + return logicp->cfg().nBlocks() <= 4 && logicp->cfg().nEdges() <= 4; }); if (doIt) varps.emplace_back(&var); } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 9e45add00..316f2c3ae 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -346,7 +346,7 @@ public: class DfgLogic final : public DfgVertexVariadic { // Generic vertex representing a whole combinational process AstNode* const m_nodep; // The Ast logic represented by this vertex - const std::unique_ptr m_cfgp; + const std::unique_ptr m_cfgp; // Vertices this logic was synthesized into. Excluding variables std::vector m_synth; @@ -356,7 +356,7 @@ public: , m_nodep{nodep} , m_cfgp{nullptr} {} - DfgLogic(DfgGraph& dfg, AstAlways* nodep, std::unique_ptr cfgp) + DfgLogic(DfgGraph& dfg, AstAlways* nodep, std::unique_ptr cfgp) : DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u} , m_nodep{nodep} , m_cfgp{std::move(cfgp)} {} @@ -366,7 +366,8 @@ public: void addInput(DfgVertexVar* varp) { addSource()->relinkSource(varp); } AstNode* nodep() const { return m_nodep; } - const ControlFlowGraph& cfg() const { return *m_cfgp; } + CfgGraph& cfg() { return *m_cfgp; } + const CfgGraph& cfg() const { return *m_cfgp; } std::vector& synth() { return m_synth; } const std::vector& synth() const { return m_synth; } diff --git a/src/V3List.h b/src/V3List.h index 89d6d36b3..0383b420d 100644 --- a/src/V3List.h +++ b/src/V3List.h @@ -241,6 +241,7 @@ public: bool empty() const { return !m_headp; } bool hasSingleElement() const { return m_headp && m_headp == m_lastp; } bool hasMultipleElements() const { return m_headp && m_headp != m_lastp; } + bool hasTwoElements() const { return m_headp && toLinks(m_headp).m_nextp == m_lastp; } // These return pointers, as we often want to unlink/delete them, and can also signal empty T_Element* frontp() { return static_cast(m_headp); } diff --git a/test_regress/t/t_dfg_break_cycles.py b/test_regress/t/t_dfg_break_cycles.py index 48fe8d28f..03eab0623 100755 --- a/test_regress/t/t_dfg_break_cycles.py +++ b/test_regress/t/t_dfg_break_cycles.py @@ -44,9 +44,10 @@ with open(rdFile, 'r', encoding="utf8") as rdFh, \ open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \ open(checkFile, 'w', encoding="utf8") as checkFh: for line in rdFh: - if "// UNOPTFLAT" in line: + line, _, cmt = line.partition("//") + cmt, _, _ = cmt.partition("//") + if "UNOPTFLAT" in cmt: nExpectedCycles += 1 - line = line.split("//")[0] m = re.search(r'`signal\((\w+),', line) if not m: continue diff --git a/test_regress/t/t_dfg_synthesis.py b/test_regress/t/t_dfg_synthesis.py index bd229b216..6d767dc14 100755 --- a/test_regress/t/t_dfg_synthesis.py +++ b/test_regress/t/t_dfg_synthesis.py @@ -70,13 +70,14 @@ test.compile(verilator_flags2=[ "--stats", "--build", "--fdfg-synthesize-all", + "-fno-dfg-pre-inline", "-fno-dfg-post-inline", - "-fno-dfg-scoped", "--exe", "+incdir+" + test.obj_dir, "-Mdir", test.obj_dir + "/obj_opt", "--prefix", "Vopt", "-fno-const-before-dfg", # Otherwise V3Const makes testing painful + "-fno-split", # Dfg will take care of it "--debug", "--debugi", "0", "--dumpi-tree", "0", "-CFLAGS \"-I .. -I ../obj_ref\"", "../obj_ref/Vref__ALL.a", @@ -84,14 +85,13 @@ test.compile(verilator_flags2=[ ]) # yapf:disable test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", - r'DFG pre inline Synthesis, synt / always blocks considered\s+(\d+)$', + r'DFG scoped Synthesis, synt / always blocks considered\s+(\d+)$', nAlwaysSynthesized + nAlwaysReverted + nAlwaysNotSynthesized) test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", - r'DFG pre inline Synthesis, synt / always blocks synthesized\s+(\d+)$', + r'DFG scoped Synthesis, synt / always blocks synthesized\s+(\d+)$', nAlwaysSynthesized + nAlwaysReverted) test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", - r'DFG pre inline Synthesis, synt / reverted \(multidrive\)\s+(\d)$', - nAlwaysReverted) + r'DFG scoped Synthesis, synt / reverted \(multidrive\)\s+(\d)$', nAlwaysReverted) # Execute test to check equivalence test.execute(executable=test.obj_dir + "/obj_opt/Vopt") diff --git a/test_regress/t/t_dfg_synthesis.v b/test_regress/t/t_dfg_synthesis.v index 46f3e298b..ad76285cf 100644 --- a/test_regress/t/t_dfg_synthesis.v +++ b/test_regress/t/t_dfg_synthesis.v @@ -154,6 +154,176 @@ module t ( end `signal(CONDITONAL_F, condigional_f); + logic [2:0] conditional_g; + always_comb begin + if (rand_a[0]) begin + if (rand_a[1]) begin + if (rand_a[2]) begin + conditional_g = 3'b111; + end else begin + conditional_g = 3'b011; + end + end else begin + if (rand_a[2]) begin + conditional_g = 3'b101; + end else begin + conditional_g = 3'b001; + end + end + end else begin + if (rand_a[1]) begin + if (rand_a[2]) begin + conditional_g = 3'b110; + end else begin + conditional_g = 3'b010; + end + end else begin + if (rand_a[2]) begin + conditional_g = 3'b100; + end else begin + conditional_g = 3'b000; + end + end + end + end + `signal(CONDITONAL_G, conditional_g); + + logic [2:0] conditional_h; + always_comb begin + if (rand_a[0]) begin + if (rand_a[1]) begin + conditional_h = 3'b011; + if (rand_a[2]) begin + conditional_h = 3'b111; + end + end else begin + conditional_h = 3'b001; + if (rand_a[2]) begin + conditional_h = 3'b101; + end + end + end else begin + if (rand_a[1]) begin + conditional_h = 3'b010; + if (rand_a[2]) begin + conditional_h = 3'b110; + end + end else begin + conditional_h = 3'b000; + if (rand_a[2]) begin + conditional_h = 3'b100; + end + end + end + end + `signal(CONDITONAL_H, conditional_h); + + logic [2:0] conditional_i; + always_comb begin // Dumbass trailing zeroes count + do begin + conditional_i = 3'd0; + if (rand_a[0]) break; + conditional_i = 3'd1; + if (rand_a[1]) break; + conditional_i = 3'd2; + if (rand_a[2]) break; + conditional_i = 3'd3; + if (rand_a[3]) break; + conditional_i = 3'd4; + end while (0); + end + `signal(CONDITONAL_I, conditional_i); + + logic [2:0] conditional_j; + always_comb begin // Even more dumbass trailing ones count + do begin + conditional_j = 3'd0; + if (rand_a[0]) begin + conditional_j = 3'd1; + end else begin + break; + end + if (rand_a[1]) begin + conditional_j = 3'd2; + end else begin + break; + end + if (rand_a[2]) begin + conditional_j = 3'd3; + end else begin + break; + end + if (rand_a[3]) begin + conditional_j = 3'd4; + end + end while (0); + end + `signal(CONDITONAL_J, conditional_j); + + logic [2:0] conditional_k; + always_comb begin + if (rand_b[0]) begin + do begin + conditional_k = 3'd0; + if (rand_a[0]) break; + conditional_k = 3'd1; + if (rand_a[1]) break; + conditional_k = 3'd2; + if (rand_a[2]) break; + conditional_k = 3'd3; + if (rand_a[3]) break; + conditional_k = 3'd4; + end while (0); + end else begin + do begin + conditional_k = 3'd0; + if (rand_a[0]) begin + conditional_k = 3'd1; + end else begin + break; + end + if (rand_a[1]) begin + conditional_k = 3'd2; + end else begin + break; + end + if (rand_a[2]) begin + conditional_k = 3'd3; + end else begin + break; + end + if (rand_a[3]) begin + conditional_k = 3'd4; + end + end while (0); + end + end + `signal(CONDITONAL_K, conditional_k); + + logic [1:0] conditional_l_a; + logic [1:0] conditional_l_b; + always_comb begin + do begin + conditional_l_a = 2'd0; + if (rand_a[1:0] == 2'd0) break; + conditional_l_a = 2'd1; + if (rand_a[1:0] == 2'd1) break; + conditional_l_a = 2'd2; + if (rand_a[1:0] == 2'd2) break; + conditional_l_a = 2'd3; + end while (0); + do begin + conditional_l_b = 2'd0; + if (rand_b[1:0] == 2'd0) break; + conditional_l_b = 2'd1; + if (rand_b[1:0] == 2'd1) break; + conditional_l_b = 2'd2; + if (rand_b[1:0] == 2'd2) break; + conditional_l_b = 2'd3; + end while (0); + end + `signal(CONDITONAL_L, {conditional_l_b, conditional_l_a}); + logic [7:0] partial_conditional_a; always_comb begin partial_conditional_a[1:0] = 2'd0; @@ -225,4 +395,111 @@ module t ( partial_temporary_a = partial_temporary_tmp; end `signal(PARTIAL_TEMPORARY, partial_temporary_a); + + logic circular_0_x; + logic circular_0_y; + logic circular_0_z; + always_comb begin + circular_0_x = 1'b0; + circular_0_y = 1'd0; + if (rand_b[0]) begin + circular_0_x = 1'b1; + if (circular_0_z) begin + circular_0_y = 1'd1; + end + end + end + assign circular_0_z = circular_0_x & rand_a[0]; + `signal(CIRCULAR_0, {circular_0_x, circular_0_y, circular_0_z}); + + logic [2:0] nsp_a; // Non series-parallel CFG + always_comb begin + do begin + nsp_a = 3'd0; // BB 0 -> BB 1 / BB 2 + if (rand_a[1:0] == 2'd0) begin + nsp_a = 3'd1; // BB 1 -> BB 4 + end else begin + nsp_a = 3'd2; // BB 2 -> BB 3 / BB 4 + if (rand_a[1:0] == 2'd1) begin + nsp_a = 3'd3; // BB3 -> BB 5 + break; + end + end + nsp_a = 3'd4; // BB 4 -> BB 5 + end while (0); + //nsp_a = 3'd5; // BB 5 + end + `signal(NSP_A, nsp_a); + + logic [2:0] nsp_b; // Non series-parallel CFG + always_comb begin + do begin + nsp_b = 3'd0; + if (rand_a[1:0] == 2'd0) begin + nsp_b = 3'd1; + end else begin + nsp_b = 3'd2; + if (rand_a[1:0] == 2'd1) begin + nsp_b = 3'd3; + break; + end else begin + nsp_b = 3'd4; + if (rand_a[1:0] == 2'd2) begin + nsp_b = 3'd5; + end else begin + nsp_b = 3'd6; + break; + end + end + end + nsp_b = 3'd7; + end while (0); + end + `signal(NSP_B, nsp_b); + + logic [2:0] part_sp_a; // Contains series-parallel sub-graph CFG + always_comb begin + do begin + part_sp_a = 3'd0; + if (rand_a[0]) begin + part_sp_a = 3'd1; + if (rand_a[1]) begin + part_sp_a = 3'd2; + end + end else begin + part_sp_a = 3'd3; + if (rand_a[2]) begin + part_sp_a = 3'd4; + if (rand_a[3]) begin + part_sp_a = 3'd5; + end + break; + end + end + part_sp_a = 3'd6; + if (rand_a[4]) begin + part_sp_a = 3'd7; + end + end while (0); + end + `signal(PART_SP_A, part_sp_a); + + logic [1:0] both_break; + always_comb begin + do begin + if (rand_a[0]) begin + both_break = 2'd0; + break; + end else begin + both_break = 2'd1; + break; + end + // Unreachable + if (rand_a[1]) begin + both_break = 2'd2; + end + end while(0); + end + `signal(BOTH_BREAK, both_break); + endmodule From 3d76bb994461584d8c989ebc6f048b70bf749cbd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 25 Aug 2025 17:50:29 -0400 Subject: [PATCH 227/252] Commentary: Changes update --- Changes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changes b/Changes index f30fd991d..0013c80da 100644 --- a/Changes +++ b/Changes @@ -52,6 +52,7 @@ Verilator 5.039 devel * Optimize combinational cycles through arrays in DFG (#6210). [Geza Lore] * Optimize variable removal in scoped DFG (#6260). [Geza Lore] * Optimize acyclic DFG components into the original acyclic sub-graph. (#6261). [Geza Lore] +* Optimize multiplexers in DFG synthesis (#6331). [Geza Lore] * Fix loop initialization visibility outside loop (#4237). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). @@ -98,6 +99,7 @@ Verilator 5.039 devel * Fix gathering sensitivities from virtual interface members (#6325). [Aleksander Kiryk] * Fix FreeBSD missing headers (#6326). [Aleksander Kiryk] * Fix to select UDPs when they are the only candidate for a top module. +* Fix to localize for super constructors with function calls as arguments (#6330). [Igor Zaworski, Antmicro Ltd.] Verilator 5.038 2025-07-08 From 703f0d8c5d3276d0e46885bee4f5e3e27e52efcf Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 25 Aug 2025 18:47:08 -0400 Subject: [PATCH 228/252] Commentary: spelling --- docs/spelling.txt | 7 +++++++ include/verilated_trace_imp.h | 2 +- include/verilated_vcd_c.cpp | 2 +- include/verilated_vpi.cpp | 2 +- src/V3AstNodeExpr.h | 2 +- src/V3AstNodeOther.h | 2 +- src/V3Control.cpp | 2 +- src/V3Delayed.cpp | 6 +++--- src/V3DfgSynthesize.cpp | 8 ++++---- src/V3FuncOpt.cpp | 2 +- src/V3Gate.cpp | 4 ++-- src/V3HierBlock.cpp | 8 ++++---- src/V3Inline.cpp | 6 +++--- src/V3LifePost.cpp | 2 +- src/V3LinkDot.cpp | 2 +- src/V3Number.cpp | 2 +- src/V3Param.cpp | 2 +- src/V3Timing.cpp | 2 +- test_regress/t/t_clk_inp_init.v | 2 +- test_regress/t/t_interface_import_param.v | 2 +- test_regress/t/t_lint_removed_unused_loop_bad.v | 8 ++++---- 21 files changed, 41 insertions(+), 34 deletions(-) diff --git a/docs/spelling.txt b/docs/spelling.txt index 34dd7da01..3f35bb844 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -589,7 +589,9 @@ combinational combinatorial commandArgsPlusMatch compilable +computable concat +concatenatable concats conf config @@ -629,6 +631,7 @@ defparam demangling dep deparametrized +deque der dereference dereferenced @@ -642,6 +645,7 @@ dev devcontainer devel dir +disambiguates displayb distcc doxygen @@ -670,6 +674,7 @@ elab eliasphanna elike elsif +enablement endcase endcelldefine endclass @@ -975,6 +980,8 @@ realtime realtobits recoding recrem +recurse +recurses redeclaring regs reloop diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index 5f0d2bd87..f6f10435f 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -905,7 +905,7 @@ VerilatedTraceOffloadBuffer::VerilatedTraceOffloadBuffer(VL_SUB_T& own using This = VerilatedTraceBuffer*; // Tack on the buffer address static_assert(2 * sizeof(uint32_t) >= sizeof(This), - "This should be enough on all plafrorms"); + "This should be enough on all platforms"); *m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER; *reinterpret_cast(m_offloadBufferWritep) = static_cast(this); m_offloadBufferWritep += 2; diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index ccab11a6d..350f349fb 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -178,7 +178,7 @@ void VerilatedVcd::openNextImp(bool incFilename) { } } m_isOpen = true; - constDump(true); // First dump must containt the const signals + constDump(true); // First dump must contain the const signals fullDump(true); // First dump must be full m_wroteBytes = 0; } diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 8ee39197b..f46ce0e25 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -3069,7 +3069,7 @@ void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned siz const unsigned packedSize, const bool leftIsLow, const bool fourState, const T* src, PLI_BYTE8* dst) { static_assert(std::is_unsigned::value, - "type T is not unsigned"); //ensure loigcal right shift + "type T is not unsigned"); //ensure logical right shift const unsigned element_size_bytes VL_BYTES_I(packedSize); const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); size_t dst_index = 0; diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ae918af30..7ff9ccbf9 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -832,7 +832,7 @@ public: bool rhsIsValue() const { return m_rhsIsValue; } }; class AstConsPackMember final : public AstNodeExpr { - // Construct a packed array single emement [member1: value1] + // Construct a packed array single element [member1: value1] // Don't need the member we are constructing, as the dtypep can get us to it // @astgen op2 := rhsp : AstNodeExpr public: diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d362f49e4..a2693a6e7 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -290,7 +290,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode { bool m_hasGParam : 1; // Has global parameter (for link) bool m_hasParameterList : 1; // Has #() for parameter declaration bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma - bool m_hierParams : 1; // Block containing params for parametrized hier blocks + bool m_hierParams : 1; // Block containing params for parameterized hier blocks bool m_internal : 1; // Internally created bool m_recursive : 1; // Recursive module bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 9e745a1fb..43d2bb01f 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -494,7 +494,7 @@ public: } bool getScopeTraceOn(const string& scope) { - // Apply in the order the user provided them, so they can choose on/off preferencing + // Apply in the order the user provided them, so they can choose on/off preferences int maxLevel = 1; for (const auto& ch : scope) { if (ch == '.') ++maxLevel; diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 964a80593..c2ece6fd0 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -19,7 +19,7 @@ // For the Pre/Post scheduling semantics, see V3OrderGraph. // // There are several "Schemes" we can choose from for implementing a -// non-blocking assignment (NBA), repserented by an AstAssignDly. +// non-blocking assignment (NBA), represented by an AstAssignDly. // // It is assumed and required in this pass that each NBA updates at // most one variable. Earlier passes should have ensured this. @@ -442,7 +442,7 @@ class DelayedVisitor final : public VNVisitor { // Check for mixed usage (this also warns if not OK) if (checkMixedUsage(vscp, isIntegralOrPacked)) { // If it's a variable updated by both blocking and non-blocking - // asignments, use the ShadowVarMasked schem if masked update is + // assignments, use the ShadowVarMasked schem if masked update is // possible. This can handle blocking and non-blocking updates to // inpdendent parts correctly at run-time, and always works, even // in loops or other dynamic context. @@ -1122,7 +1122,7 @@ class DelayedVisitor final : public VNVisitor { // There were some timing domains involved in the process. Add all of them as sensitivities // of all NBA targets in this process. Note this is a bit of a sledgehammer, we should only - // need those that directly preceed the NBA in control flow, but that is hard to compute, + // need those that directly precede the NBA in control flow, but that is hard to compute, // so we will hammer away. // First gather all senItems diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 230c73f70..e36a4e91e 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -862,7 +862,7 @@ class AstToDfgSynthesize final { } // If any written variables are forced or otherwise udpated from outside, - // we generally cannot synthesie the construct, as we will likely need to + // we generally cannot synthesize the construct, as we will likely need to // introduce intermediate values that would not be updated. static bool hasExternallyWrittenVariable(DfgLogic& vtx) { return vtx.findSink([](const DfgVertex& sink) -> bool { @@ -896,7 +896,7 @@ class AstToDfgSynthesize final { UASSERT_OBJ(thenVarp == elseVarp, varp, "Attempting to join unrelated variables"); // If both bindings are the the same (variable not updated through either path), - // then there is nothing to do, canuse the same binding + // then there is nothing to do, can use the same binding if (thenp == elsep) return thenp; // We can't join the input variable just yet, so bail @@ -1344,7 +1344,7 @@ class AstToDfgSynthesize final { return true; } - // Assign path perdicates to the outgoing control flow edges of the given block + // Assign path predicates to the outgoing control flow edges of the given block void assignPathPredicates(const CfgBlock& bb) { // Nothing to do for the exit block if (bb.isExit()) return; @@ -1398,7 +1398,7 @@ class AstToDfgSynthesize final { // It's possible we think a variable is written by the DfgLogic when // it actauly isn't, e.g.: '{a[0], b[0]}[1] = ...' does not write 'b'. // These LHS forms can happen after some earlier tranforms. We - // should just run V3Const on them earleir, but we will do belt and + // should just run V3Const on them earlier, but we will do belt and // braces and check here too. We can't touch any output variables if so. const bool missing = m_logicp->findSink([&](const DfgVertex& sink) -> bool { const DfgUnresolved* const unresolvedp = sink.as(); diff --git a/src/V3FuncOpt.cpp b/src/V3FuncOpt.cpp index 6462542fa..7b79c7309 100644 --- a/src/V3FuncOpt.cpp +++ b/src/V3FuncOpt.cpp @@ -280,7 +280,7 @@ class FuncOptVisitor final : public VNVisitor { // VISIT void visit(AstNodeAssign* nodep) override { // TODO: Only thing remaining inside functions should be AstAssign (that is, an actual - // assignment statemant), but we stil use AstAssignW, AstAssignDly, and all, fix. + // assignment statement), but we stil use AstAssignW, AstAssignDly, and all, fix. iterateChildren(nodep); if (v3Global.opt.fFuncSplitCat()) { diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 578799b04..4052c2d1d 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -874,8 +874,8 @@ class GateInline final { // (driver is same as sink), however, okVisitor rejects a circular driver // and we would not reach here if the driver logic was actually circular. // The reason we end up here is because during graph building, the driver - // was ciruclar, however, after committing some substituions to it, it - // has become non-circualr due to V3Const being applied inside + // was circular, however, after committing some substitutions to it, it + // has become non-circular due to V3Const being applied inside // 'commitSubstitutions'. We will trust GateOkVisitor telling the truth // that the logic is not actually circular, meaning this edge is not // actually needed, can just delete it and move on. diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index a142df2fa..7b9e621dd 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -40,7 +40,7 @@ // that are marked with /*verilator hier_block*/ metacomment in Verilator run a). // 2) If module type parameters are present, V3Control marks hier param modules // (marked with hier_params verilator config pragma) as modp->hierParams(true). -// This is done in run b), de-parametrized modules are mapped with their params one-to-one. +// This is done in run b), de-parameterized modules are mapped with their params one-to-one. // 3) AstModule with HIER_BLOCK pragma is marked modp->hierBlock(true) // in V3LinkResolve.cpp during run a). // 4) In V3LinkCells.cpp, the following things are done during run b) and c). @@ -56,8 +56,8 @@ // 5) In V3LinkDot.cpp, // 5-1) Dotted access across hierarchical block boundary is checked. Currently hierarchical // block references are not supported. -// 5-2) If present, parameters in hier params module replace parameter values of de-parametrized -// module in run b). +// 5-2) If present, parameters in hier params module replace parameter values of +// de-parameterized module in run b). // 6) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even // if the parameter is not referred. This protection is necessary to match step 6) below. // 7) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c). @@ -82,7 +82,7 @@ // filename :Name of a hierarchical parameters file // // Added in a), used for b). -// Each de-parametrized module version has exactly one hier params file specified. +// Each de-parameterized module version has exactly one hier params file specified. #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 7b9c6c3f7..9bbe9e384 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -431,7 +431,7 @@ namespace ModuleInliner { // the instance and the port variable, or simply inline the pin expression // in place of the port variable. We will prefer to do the later whenever // possible (and sometimes required). When inlining, we need to create an -// alias for the inined variable, in order to resovle hierarchical refrences +// alias for the inlined variable, in order to resovle hierarchical references // against it later in V3Scope (and also for tracing, which is inserted //later). Returns ture iff the given port variable should be inlined, // and false if a continuous assignment should be used. @@ -445,7 +445,7 @@ bool inlinePort(AstVar* nodep) { if (nodep->isForced()) return false; // Note: For singls marked 'public' (and not 'public_flat') inlining - // of their contaning modules is disabled so they wont reach here. + // of their containing modules is disabled so they wont reach here. // TODO: For now, writable public signals inside the cell cannot be // eliminated as they are entered into the VerilatedScope, and @@ -618,7 +618,7 @@ void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) { while (AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) { nodep = nodep->backp(); - // Consider each cell inside the current module for inling + // Consider each cell inside the current module for inlining for (AstCell* const cellp : moduleStates(modp).m_childCells) { ModuleState& childState = moduleStates(cellp->modp()); if (!childState.m_inlined) continue; diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index d8b191900..4703ea0f9 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -199,7 +199,7 @@ class LifePostDlyVisitor final : public VNVisitorConst { // We need to be able to pick up the first write of each variable. // V3Order serializes all writes, and we trace AstExecGraph in - // dependency order, so the first one we encouner during tracing should + // dependency order, so the first one we encounter during tracing should // always be the one. It's somewhat expensive to assert so only with debugCheck(). if (v3Global.opt.debugCheck()) { for (auto& pair : m_writes) { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 00328fb4f..708470185 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2678,7 +2678,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } void classExtendImport(AstClass* nodep) { // A class reference might be to a class that is later in Ast due to - // e.g. parmaeterization or referring to a "class (type T) extends T" + // e.g. parameterization or referring to a "class (type T) extends T" // Resolve it so later Class:: references into its base classes work symIterateNull(nodep, m_statep->getNodeSym(nodep)); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 16e4e097b..02a83f925 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1835,7 +1835,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) + // Correct number of zero bits/width matters (hence 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 diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 527972e8d..cbaba3391 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -548,7 +548,7 @@ class ParamProcessor final { // constp can be nullptr if (const AstConst* const p = VN_CAST(nodep, Const)) { - // Treat modules parametrized with the same values but with different type as the + // Treat modules parameterized with the same values but with different type as the // same. longname += p->num().ascii(false); } else if (nodep) { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index d70f3ed21..aa64abdb5 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -685,7 +685,7 @@ class TimingControlVisitor final : public VNVisitor { } // Move `insertBeforep` into `AstCLocalScope` if necessary to avoid jumping over // a variable initialization that whould be inserted before `insertBeforep`. All - // access to this variable shoule be contained within returned `AstCLocalScope`. + // access to this variable should be contained within returned `AstCLocalScope`. AstCLocalScope* addCLocalScope(FileLine* const flp, AstNode* const insertBeforep) const { if (!insertBeforep || !m_underJumpBlock) return nullptr; VNRelinker handle; diff --git a/test_regress/t/t_clk_inp_init.v b/test_regress/t/t_clk_inp_init.v index a17e544db..cc8ae8b47 100644 --- a/test_regress/t/t_clk_inp_init.v +++ b/test_regress/t/t_clk_inp_init.v @@ -1,6 +1,6 @@ // DESCRIPTION: Verilator: Check initialisation of cloned clock variables // -// This tests issue #1327 (Strange initialisation behaviour with +// This tests issue #1327 (Strange initialization behavior with // "VinpClk" cloned clock variables) // // This file ONLY is placed into the Public Domain, for any use, diff --git a/test_regress/t/t_interface_import_param.v b/test_regress/t/t_interface_import_param.v index af94c649a..4e6860b45 100644 --- a/test_regress/t/t_interface_import_param.v +++ b/test_regress/t/t_interface_import_param.v @@ -1,6 +1,6 @@ // DESCRIPTION: Verilator: Verilog Test module // -// A test that a package import declaration can preceed a parameter port list +// A test that a package import declaration can precede a parameter port list // in an interface declaration. See IEEE 1800-2023 25.3. // // This file ONLY is placed into the Public Domain, for any use, 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 5ec480ede..212869165 100644 --- a/test_regress/t/t_lint_removed_unused_loop_bad.v +++ b/test_regress/t/t_lint_removed_unused_loop_bad.v @@ -62,7 +62,7 @@ module unused(input clk); end endmodule -// no warning for loops under parametrized module +// no warning for loops under parameterized module module parametrized_initial #(parameter REPETITIONS = 0); int prints_while = 0; int prints_do_while = 0; @@ -228,21 +228,21 @@ module if_with_param; initial begin if (ZERO_PARAM) begin - // loop under false parametrized if - no warning + // loop under false parameterized if - no warning int prints = 0; while(prints < 5) begin prints++; end $write("Prints %d\n", prints); end else if (!ONE_PARAM) begin - // loop under false parametrized if - no warning + // loop under false parameterized if - no warning int prints = 0; while(prints < 5) begin prints++; end $write("Prints %d\n", prints); end else begin - // loop under true parametrized if - no warning + // loop under true parameterized if - no warning int prints = 0; while(prints < 5) begin prints++; From ac2859bf245307274bd4dfb83b9efac2b380a438 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 25 Aug 2025 20:47:48 -0400 Subject: [PATCH 229/252] Internals: Upgrade to clang-format-18 (#6333) --- .github/workflows/format.yml | 10 ++++----- Makefile.in | 6 +++--- configure.ac | 2 +- docs/guide/install.rst | 2 +- docs/internals.rst | 29 +++++++++++++++----------- include/verilated_funcs.h | 12 +++++------ include/verilated_sc_trace.h | 2 +- include/verilated_vcd_c.h | 2 +- src/V3Ast.cpp | 10 ++++----- src/V3Ast.h | 5 +++-- src/V3Control.cpp | 3 +-- src/V3Delayed.cpp | 24 +++++++++++----------- src/V3EmitXml.cpp | 10 ++++----- src/V3FileLine.cpp | 4 ++-- src/V3Graph.cpp | 7 +++---- src/V3LinkDot.cpp | 29 +++++++++++++------------- src/V3Options.cpp | 5 ++--- src/V3PreLex.l | 4 ++-- src/V3SenExprBuilder.h | 5 ++--- src/V3Table.cpp | 3 +-- src/V3Timing.cpp | 2 +- src/V3TraceDecl.cpp | 2 +- src/V3Unknown.cpp | 4 ++-- src/V3Width.cpp | 33 +++++++++++++++--------------- test_regress/t/t_var_sc_bv.cpp | 2 +- test_regress/t/t_wrapper_clone.cpp | 3 +-- 26 files changed, 108 insertions(+), 112 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index d0b16f48b..aca23c017 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -11,11 +11,11 @@ permissions: contents: write jobs: format: - runs-on: ubuntu-22.04 - name: Ubuntu 22.04 | format + runs-on: ubuntu-24.04 + name: Ubuntu 24.04 | format env: CI_OS_NAME: linux - CI_RUNS_ON: ubuntu-22.04 + CI_RUNS_ON: ubuntu-24.04 CI_COMMIT: ${{ github.sha }} steps: - name: Checkout @@ -27,7 +27,7 @@ jobs: CI_BUILD_STAGE_NAME: build run: | bash ci/ci-install.bash && - sudo apt-get install clang-format-14 yapf3 && + sudo apt-get install clang-format-18 yapf3 && sudo pip3 install gersemi mbake && git config --global user.email "action@example.com" && git config --global user.name "github action" @@ -35,7 +35,7 @@ jobs: run: | autoconf && ./configure && - make -j 2 format CLANGFORMAT=clang-format-14 && + make -j 2 format CLANGFORMAT=clang-format-18 && git status - name: Push run: | diff --git a/Makefile.in b/Makefile.in index 42ce2987d..e879202c5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -462,14 +462,14 @@ analyzer-include: format: $(MAKE) -j 5 format-c format-cmake format-exec format-py -CLANGFORMAT = clang-format-14 +CLANGFORMAT = clang-format-18 CLANGFORMAT_FLAGS = -i CLANGFORMAT_FILES = $(CHECK_CPP) $(CHECK_H) $(CHECK_YL) test_regress/t/*.c* test_regress/t/*.h format-c clang-format: $(CLANGFORMAT) --version - @$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \ - || echo "*** You are not using clang-format-14, indents may differ from master's ***" + @$(CLANGFORMAT) --version | fgrep 'version 18' > /dev/null \ + || echo "*** You are not using clang-format-18, indents may differ from master's ***" $(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES) YAMLFIX = YAMLFIX_WHITELINES=1 YAMLFIX_LINE_LENGTH=130 YAMLFIX_preserve_quotes=true yamlfix diff --git a/configure.ac b/configure.ac index 5efee99b2..5e9f2458d 100644 --- a/configure.ac +++ b/configure.ac @@ -178,7 +178,7 @@ AC_PROG_INSTALL AC_LANG_PUSH(C++) CFG_CXX_VERSION=`$CXX --version | head -1` -AC_MSG_RESULT([compiler $CXX --version = $CFG_CXX_VERSION]) +AC_MSG_RESULT([compiler version... $CXX --version = $CFG_CXX_VERSION]) AC_SUBST(CFG_CXX_VERSION) AC_MSG_CHECKING([that C++ compiler can compile simple program]) diff --git a/docs/guide/install.rst b/docs/guide/install.rst index e7438b289..57e9f72cd 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -157,7 +157,7 @@ Those developing Verilator itself also need these (see internals.rst): .. code-block:: shell - sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov + sudo apt-get install clang clang-format-18 cmake gdb gprof graphviz lcov sudo apt-get install python3-clang python3-distro yapf3 bear jq sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe gersemi mbake ruff sarif-tools sudo pip3 install git+https://github.com/antmicro/astsee.git diff --git a/docs/internals.rst b/docs/internals.rst index 3ce56b65d..4230a966c 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1131,7 +1131,7 @@ Indentation and Naming Style ---------------------------- We will work with contributors to fix up indentation style issues, but it -is appreciated if you could match our style: +is appreciated if you could match our style for C/C++: - Use "mixedCapsSymbols" instead of "underlined_symbols". @@ -1143,9 +1143,8 @@ is appreciated if you could match our style: calls. (This convention has not been applied retroactively.) C and Python indentation is automatically maintained with "make format" -using clang-format version 14.0.0, and yapf for Python, and is -automatically corrected in the CI actions. For those manually formatting C -code: +using clang-format version 18, and yapf for Python, and is automatically +corrected in the CI actions. For those manually formatting C code: - Use four spaces per level, and no tabs. @@ -1157,12 +1156,20 @@ code: - No spaces before semicolons, nor between a function's name and open parenthesis (only applies to functions; if/else has a following space). +Verilog indentation is not yet automatically maintained, but: + +- Use two spaces per level, and no tabs. + +- Place `begin` on the same line as the earlier starting statement + e.g. `if`, and place the `end` on a separate line indented the same as + the starting statement. + Commit messages --------------- Pull requests do not typically edit ``Changes`` in order to reduce -potential merge conflicts. Instead, contibutors should use an appropriate +potential merge conflicts. Instead, contributors should use an appropriate first line of the git commit message. Maintainers periodically run ``nodist/log_changes`` which analyzes the commit messages to suggest edits to the ``Changes`` file. @@ -1199,7 +1206,7 @@ The line's grammar should match the phrasing used in the Changes file. If the change does not affect user-visible function, also add "No functional change." if it's 99% certain not to change behavior, or "No functional change expected." if no change was expected but there may be -uncertanty. +uncertainty. The ``astgen`` Script @@ -1208,7 +1215,7 @@ The ``astgen`` Script The ``astgen`` script is used to generate some of the repetitive C++ code related to the ``AstNode`` type hierarchy. An example is the abstract ``visit`` methods in ``VNVisitor``. There are other uses; please see the ``*__gen*`` -files in the bulid directories and the ``astgen`` script for details. A +files in the build directories and the ``astgen`` script for details. A description of the more advanced features of ``astgen`` are provided here. @@ -1617,7 +1624,7 @@ Benchmarking ------------ For benchmarking the effects of changes (simulation speed, memory consumption, -verilation time, etc.), you can use `RTLMeter +Verilation time, etc.), you can use `RTLMeter `__, a benchmark suite designed for this purpose. The scripts provided with RTLMeter have many capabilities. For full details, see the `documentation of RTLMeter @@ -2039,10 +2046,8 @@ Generally, what would you do to add a new feature? 2. Make a testcase in the test_regress/t/t_EXAMPLE format, see `Testing`. -3. If grammar changes are needed, look at the git version of VerilogPerl's - src/VParseGrammar.y, as this grammar supports the full SystemVerilog - language and has a lot of back-and-forth with Verilator's grammar. Copy - the appropriate rules to src/verilog.y and modify the productions. +3. If grammar changes are needed, look at the IEEE 1800-2023 Appendix A, as + src/verilog.y generally follows the same rule layout. 4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h. Follow the convention described above about the AstNode type hierarchy. diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index c18c32022..3a4f2a427 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -279,11 +279,11 @@ static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast> VL_BITBIT_I((nbits)-VL_UL(1))) -#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits)-1ULL)) -#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits)-VL_EUL(1))) +#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits) - VL_UL(1))) +#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits) - 1ULL)) +#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits) - VL_EUL(1))) #define VL_SIGN_W(nbits, rwp) \ - ((rwp)[VL_BITWORD_E((nbits)-VL_EUL(1))] >> VL_BITBIT_E((nbits)-VL_EUL(1))) + ((rwp)[VL_BITWORD_E((nbits) - VL_EUL(1))] >> VL_BITBIT_E((nbits) - VL_EUL(1))) #define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) // Sign bit extended up to MSB, doesn't include unsigned portion @@ -558,8 +558,8 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { lsb += BITS_PER_DIGIT; \ } \ if (lsb < (obits)) { \ - const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \ - *chunkp = msb_data & VL_MASK_E((obits)-lsb); \ + const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits) - lsb); \ + *chunkp = msb_data & VL_MASK_E((obits) - lsb); \ } \ _butemp.set(0, *(rwp).data() & 1); /* force update the sign */ \ (svar).write(_butemp); \ diff --git a/include/verilated_sc_trace.h b/include/verilated_sc_trace.h index 99bffa19f..4a8e18f30 100644 --- a/include/verilated_sc_trace.h +++ b/include/verilated_sc_trace.h @@ -60,7 +60,7 @@ class VerilatedScTraceBase VL_NOT_FINAL : private sc_core::sc_object, private sc_core::sc_trace_file { bool m_enableDeltaCycles = false; bool m_traceFileAdded = false; - static void stubReportHandler(const sc_core::sc_report&, const sc_core::sc_actions&){}; + static void stubReportHandler(const sc_core::sc_report&, const sc_core::sc_actions&) {}; public: void enableDeltaCycles(bool flag = true) { diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index 21a69afdb..500ec755f 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -105,7 +105,7 @@ protected: void commitTraceBuffer(Buffer*) override; // Configure sub-class - void configure(const VerilatedTraceConfig&) override{}; + void configure(const VerilatedTraceConfig&) override {}; public: //========================================================================= diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 607f42591..d5ce5544c 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1394,8 +1394,8 @@ void AstNode::dumpTreeFile(const string& filename, bool doDump) { static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp, const std::string& childName) { if (childp) { - os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " [" - << "label=\"" << childName << "\" color=red];\n"; + os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " [" << "label=\"" + << childName << "\" color=red];\n"; for (const AstNode* nodep = childp; nodep; nodep = nodep->nextp()) { nodep->dumpTreeDot(os); if (nodep->nextp()) { @@ -1409,8 +1409,7 @@ static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* } void AstNode::dumpTreeDot(std::ostream& os) const { - os << "\tn" << cvtToHex(this) << "\t[" - << "label=\"" << typeName() << "\\n" + os << "\tn" << cvtToHex(this) << "\t[" << "label=\"" << typeName() << "\\n" << name() << "\"];\n"; drawChildren(os, this, m_op1p, "op1"); drawChildren(os, this, m_op2p, "op2"); @@ -1447,8 +1446,7 @@ void AstNode::dumpTreeDotFile(const string& filename, bool doDump) { const std::unique_ptr treedotp{V3File::new_ofstream(filename)}; if (treedotp->fail()) v3fatal("Can't write file: " << filename); *treedotp << "digraph vTree{\n"; - *treedotp << "\tgraph\t[label=\"" << filename + ".dot" - << "\",\n"; + *treedotp << "\tgraph\t[label=\"" << filename + ".dot" << "\",\n"; *treedotp << "\t\t labelloc=t, labeljust=l,\n"; *treedotp << "\t\t //size=\"7.5,10\",\n" << "];\n"; diff --git a/src/V3Ast.h b/src/V3Ast.h index 0e86c79a4..380247b5d 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2135,7 +2135,7 @@ protected: AstNode(VNType t, FileLine* fl); virtual AstNode* clone() = 0; // Generally, cloneTree is what you want instead virtual void cloneRelink() { cloneRelinkGen(); } - virtual void cloneRelinkGen(){}; // Overrides generated by 'astgen' + virtual void cloneRelinkGen() {}; // Overrides generated by 'astgen' void cloneRelinkTree(); // METHODS @@ -2174,7 +2174,8 @@ protected: virtual bool sameNode(const AstNode*) const { return true; } // Generated by 'astgen'. If do an oldp->replaceNode(newp), would cause a broken() virtual bool wouldBreakGen(const AstNode* const oldp, - const AstNode* const newp) const = 0; // Generated by 'astgen' + const AstNode* const newp) const + = 0; // Generated by 'astgen' public: // ACCESSORS diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 43d2bb01f..7b4225264 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -479,8 +479,7 @@ class V3ControlScopeTraceResolver final { public: void addScopeTraceOn(bool on, const string& scope, int levels) { - UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' " - << " levels=" << levels); + UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' " << " levels=" << levels); m_entries.emplace_back(V3ControlScopeTraceEntry{scope, on, levels}); m_matchCache.clear(); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index c2ece6fd0..e5bfd8522 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -387,18 +387,18 @@ class DelayedVisitor final : public VNVisitor { if (bIt != blkRefs.end() && nIt != nbaRefs.end()) { const Ref& blkRef = *bIt; const Ref& nbaRef = *nIt; - vscp->v3warn(BLKANDNBLK, - "Unsupported: Blocking and non-blocking assignments to " - "potentially overlapping bits of same packed variable: " - << vscp->varp()->prettyNameQ() << '\n' - << vscp->warnContextPrimary() << '\n' - << blkRef.m_refp->warnOther() << "... Location of blocking assignment" - << " (bits [" << blkRef.m_msb << ":" << blkRef.m_lsb << "])\n" - << blkRef.m_refp->warnContextSecondary() << '\n' - << nbaRef.m_refp->warnOther() - << "... Location of nonblocking assignment" - << " (bits [" << nbaRef.m_msb << ":" << nbaRef.m_lsb << "])\n" - << nbaRef.m_refp->warnContextSecondary()); + vscp->v3warn(BLKANDNBLK, "Unsupported: Blocking and non-blocking assignments to " + "potentially overlapping bits of same packed variable: " + << vscp->varp()->prettyNameQ() << '\n' + << vscp->warnContextPrimary() << '\n' + << blkRef.m_refp->warnOther() + << "... Location of blocking assignment" << " (bits [" + << blkRef.m_msb << ":" << blkRef.m_lsb << "])\n" + << blkRef.m_refp->warnContextSecondary() << '\n' + << nbaRef.m_refp->warnOther() + << "... Location of nonblocking assignment" << " (bits [" + << nbaRef.m_msb << ":" << nbaRef.m_lsb << "])\n" + << nbaRef.m_refp->warnContextSecondary()); } return true; diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 6545aaf35..ac90ba445 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -384,9 +384,8 @@ class HierCellsXmlVisitor final : public VNVisitorConst { && nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode m_os << "\n"; m_os << "fileline()->xmlDetailedLocation() // - << " name=\"" << nodep->prettyName() << "\"" - << " submodname=\"" << nodep->prettyName() << "\"" - << " hier=\"" << nodep->prettyName() << "\""; + << " name=\"" << nodep->prettyName() << "\"" << " submodname=\"" + << nodep->prettyName() << "\"" << " hier=\"" << nodep->prettyName() << "\""; m_hier = nodep->prettyName() + "."; m_hasChildren = false; iterateChildrenConst(nodep); @@ -402,9 +401,8 @@ class HierCellsXmlVisitor final : public VNVisitorConst { if (nodep->modp() && nodep->modp()->dead()) return; if (!m_hasChildren) m_os << ">\n"; m_os << "fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() - << "\"" - << " submodname=\"" << nodep->modName() << "\"" - << " hier=\"" << m_hier + nodep->name() << "\""; + << "\"" << " submodname=\"" << nodep->modName() << "\"" << " hier=\"" + << m_hier + nodep->name() << "\""; const std::string hier = m_hier; m_hier += nodep->name() + "."; m_hasChildren = false; diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 7e7cae443..5a3eb58c7 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -432,8 +432,8 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) wsstr << '\n'; std::ostringstream extrass; // extra spaced out for prefix if (!extra.empty()) { - extrass << V3Error::warnContextBegin() << std::setw(ascii().length()) << " " - << ": " << V3Error::warnContextEnd() << extra; + extrass << V3Error::warnContextBegin() << std::setw(ascii().length()) << " " << ": " + << V3Error::warnContextEnd() << extra; } if (warnIsOff(V3Error::s().errorCode())) { V3Error::s().suppressThisWarning(); diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index feb3d327c..ced3a7d26 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -330,8 +330,8 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { } } if (subgr != "") *logp << "\t"; - *logp << "\tn" << vertexp->dotName() << (n++) << "\t[fontsize=8 " - << "label=\"" << (vertexp->name() != "" ? vertexp->name() : "\\N"); + *logp << "\tn" << vertexp->dotName() << (n++) << "\t[fontsize=8 " << "label=\"" + << (vertexp->name() != "" ? vertexp->name() : "\\N"); if (vertexp->rank()) *logp << " r" << vertexp->rank(); if (vertexp->fanout() != 0.0) *logp << " f" << vertexp->fanout(); if (vertexp->color()) *logp << "\\n c" << vertexp->color(); @@ -354,8 +354,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { << " [" // <<"fontsize=8 label=\""<<(edge.name()!="" ? edge.name() : "\\E")<<"\"" << "fontsize=8 label=\"" << (edge.dotLabel() != "" ? edge.dotLabel() : "") - << "\"" - << " weight=" << edge.weight() << " color=" << edge.dotColor(); + << "\"" << " weight=" << edge.weight() << " color=" << edge.dotColor(); if (edge.dotStyle() != "") *logp << " style=" << edge.dotStyle(); // if (edge.cutable()) *logp << ",constraint=false"; // to rank without // following edges diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 708470185..f6e8573fd 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3697,9 +3697,9 @@ class LinkDotResolveVisitor final : public VNVisitor { if (foundp) { nodep->v3error("Found definition of '" << m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".") - << nodep->prettyName() << "'" - << " as a " << foundp->nodep()->typeName() - << " but expected a " << expectWhat); + << nodep->prettyName() << "'" << " as a " + << foundp->nodep()->typeName() << " but expected a " + << expectWhat); } else if (m_ds.m_dotText == "") { UINFO(1, " ErrParseRef curSymp=se" << cvtToHex(m_curSymp) << " ds=" << m_ds.ascii()); @@ -3710,15 +3710,16 @@ class LinkDotResolveVisitor final : public VNVisitor { << expectWhat << ": " << nodep->prettyNameQ() << '\n' << (suggest.empty() ? "" : nodep->warnMore() + suggest)); } else { - nodep->v3error("Can't find definition of " - << (!baddot.empty() ? AstNode::prettyNameQ(baddot) - : nodep->prettyNameQ()) - << " in dotted " << expectWhat << ": '" - << m_ds.m_dotText + "." + nodep->prettyName() << "'\n" - << nodep->warnContextPrimary() - << (okSymp ? okSymp->cellErrorScopes( - nodep, AstNode::prettyName(m_ds.m_dotText)) - : "")); + nodep->v3error( + "Can't find definition of " + << (!baddot.empty() ? AstNode::prettyNameQ(baddot) + : nodep->prettyNameQ()) + << " in dotted " << expectWhat << ": '" + << m_ds.m_dotText + "." + nodep->prettyName() << "'\n" + << nodep->warnContextPrimary() + << (okSymp ? okSymp->cellErrorScopes( + nodep, AstNode::prettyName(m_ds.m_dotText)) + : "")); } m_ds.m_dotErr = true; } @@ -4233,8 +4234,8 @@ class LinkDotResolveVisitor final : public VNVisitor { } else { nodep->v3error("Found definition of '" << m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".") - << nodep->prettyName() << "'" - << " as a " << foundp->nodep()->typeName() + << nodep->prettyName() << "'" << " as a " + << foundp->nodep()->typeName() << " but expected a task/function"); } } else if (VN_IS(nodep, New) && m_statep->forPrearray()) { diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 96d57f204..35a81cc48 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1399,9 +1399,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_hierBlocks.emplace(opt.mangledName(), opt); }); DECL_OPTION("-hierarchical-child", Set, &m_hierChild); - DECL_OPTION("-hierarchical-params-file", CbVal, [this](const char* optp) { - m_hierParamsFile.push_back({optp, work()}); - }); + DECL_OPTION("-hierarchical-params-file", CbVal, + [this](const char* optp) { m_hierParamsFile.push_back({optp, work()}); }); DECL_OPTION("-I", CbPartialMatch, [this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); }); diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 8e62cdd5b..c3b533fe5 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -740,8 +740,8 @@ void V3PreLex::dumpStack() { // LCOV_EXCL_START std::stack tmpstack = LEXP->m_streampStack; while (!tmpstack.empty()) { const VPreStream* const streamp = tmpstack.top(); - cout << "- bufferStack[" << cvtToHex(streamp) << "]: " - << " at=" << streamp->m_curFilelinep << " nBuf=" << streamp->m_buffers.size() + cout << "- bufferStack[" << cvtToHex(streamp) + << "]: " << " at=" << streamp->m_curFilelinep << " nBuf=" << streamp->m_buffers.size() << " size0=" << (streamp->m_buffers.empty() ? 0 : streamp->m_buffers.front().length()) << (streamp->m_eof ? " [EOF]" : "") << (streamp->m_file ? " [FILE]" : "") << endl; tmpstack.pop(); diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index 879ff3165..498f3d61c 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -149,9 +149,8 @@ private: AstNodeExpr* const senp = senItemp->sensp(); const auto currp = [this, senp]() { return getCurr(senp); }; - const auto prevp = [this, flp, senp]() { - return new AstVarRef{flp, getPrev(senp), VAccess::READ}; - }; + const auto prevp + = [this, flp, senp]() { return new AstVarRef{flp, getPrev(senp), VAccess::READ}; }; const auto lsb = [=](AstNodeExpr* opp) { return new AstSel{flp, opp, 0, 1}; }; // All event signals should be 1-bit at this point diff --git a/src/V3Table.cpp b/src/V3Table.cpp index e2d1b8cd8..8c7d0669f 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -234,8 +234,7 @@ private: << chkvis.instrCount() << " Data=" << chkvis.dataCount() << " in width (bits)=" << m_inWidthBits << " out width (bytes)=" << m_outWidthBytes << " Spacetime=" << (space / time) << "(" - << space << "/" << time << ")" - << ": " << nodep); + << space << "/" << time << ")" << ": " << nodep); if (chkvis.optimizable()) { UINFO(3, " Table Optimize spacetime=" << (space / time) << " " << nodep); m_totalBytes += space; diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index aa64abdb5..d25a40346 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -95,7 +95,7 @@ enum ForkType : uint8_t { enum PropagationType : uint8_t { P_CALL = 1, // Propagation through call to a function/task/method - P_FORK = 2, // Propagation due to fork's behaviour + P_FORK = 2, // Propagation due to fork's behavior P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements }; diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index d244f1d19..026d7a230 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -685,7 +685,7 @@ public: // Now that we have everything ready, remove redundant pushPrefix/popPrefix // pairs. While functionally this is not really necessary (the trace files // might have some empty scope declarations), we do it to preserve previous - // behaviour. Note: unfortunately generating these without the redundant + // behavior. Note: unfortunately generating these without the redundant // push/pop pairs is a bit hard. It is cleaner to remove them. removeRedundantPrefixPushPop(); diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 3a83bd043..2b1d1308b 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -129,9 +129,9 @@ class UnknownVisitor final : public VNVisitor { fl, condp, (needDly ? static_cast(new AstAssignDly{ - fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}) + fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}) : static_cast(new AstAssign{ - fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}))}; + fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}))}; newp->branchPred(VBranchPred::BP_LIKELY); newp->isBoundsCheck(true); UINFOTREE(9, newp, "", "_new"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a12da7ed2..a487ce473 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4561,11 +4561,12 @@ class WidthVisitor final : public VNVisitor { refp = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr; } if (!refp) { // e.g. int a = new; - nodep->v3error("new() assignment not legal to non-class " - + (VN_IS(warnp, NodeDType) ? ( - "data type "s + VN_AS(warnp, NodeDType)->prettyDTypeNameQ()) - : warnp ? warnp->prettyNameQ() - : "")); + nodep->v3error( + "new() assignment not legal to non-class " + + (VN_IS(warnp, NodeDType) + ? ("data type "s + VN_AS(warnp, NodeDType)->prettyDTypeNameQ()) + : warnp ? warnp->prettyNameQ() + : "")); nodep->dtypep(m_vup->dtypep()); return; } @@ -5981,8 +5982,8 @@ class WidthVisitor final : public VNVisitor { // Must be a error according to spec // (Because we need to know if to connect to one or all instants) nodep->v3error(ucfirst(nodep->prettyOperatorName()) - << " as part of a module instance array" - << " requires " << modwidth << " or " << modwidth * numInsts + << " as part of a module instance array" << " requires " + << modwidth << " or " << modwidth * numInsts << " bits, but connection's " << nodep->exprp()->prettyTypeName() << " generates " << conwidth << " bits. (IEEE 1800-2023 23.3.3)"); @@ -5993,8 +5994,8 @@ class WidthVisitor final : public VNVisitor { if (nodep->modVarp()->direction() == VDirection::REF) { nodep->v3error("Ref connection " << nodep->modVarp()->prettyNameQ() - << " requires matching types;" - << " ref requires " << modDTypep->prettyDTypeNameQ() + << " requires matching types;" << " ref requires " + << modDTypep->prettyDTypeNameQ() << " data type but connection is " << conDTypep->prettyDTypeNameQ() << " data type."); } else if (nodep->modVarp()->isTristate()) { @@ -6023,8 +6024,7 @@ class WidthVisitor final : public VNVisitor { nodep->v3error("Illegal " << nodep->prettyOperatorName() << "," << " mismatch between port which is" << (VN_CAST(modDTypep, IfaceRefDType) ? "" : " not") - << " an interface," - << " and expression which is" + << " an interface," << " and expression which is" << (VN_CAST(conDTypep, IfaceRefDType) ? "" : " not") << " an interface."); } @@ -6045,12 +6045,11 @@ class WidthVisitor final : public VNVisitor { UINFO(1, " Related lo: " << modDTypep); UINFO(1, " Related hi: " << conDTypep); } else if ((exprArrayp && !modArrayp) || (!exprArrayp && modArrayp)) { - nodep->v3error("Illegal " << nodep->prettyOperatorName() << "," - << " mismatch between port which is" - << (modArrayp ? "" : " not") << " an array," - << " and expression which is" - << (exprArrayp ? "" : " not") - << " an array. (IEEE 1800-2023 7.6)"); + nodep->v3error( + "Illegal " + << nodep->prettyOperatorName() << "," << " mismatch between port which is" + << (modArrayp ? "" : " not") << " an array," << " and expression which is" + << (exprArrayp ? "" : " not") << " an array. (IEEE 1800-2023 7.6)"); UINFO(1, " Related lo: " << modDTypep); UINFO(1, " Related hi: " << conDTypep); } diff --git a/test_regress/t/t_var_sc_bv.cpp b/test_regress/t/t_var_sc_bv.cpp index a9be7508c..97d45745f 100644 --- a/test_regress/t/t_var_sc_bv.cpp +++ b/test_regress/t/t_var_sc_bv.cpp @@ -44,7 +44,7 @@ void compareWls(int obits, WDataInP const lwp, WDataInP const rwp) { sc_biguint<(obits)> _butemp; \ for (int i = 0; i < VL_WORDS_I(obits); ++i) { \ int msb = ((i + 1) * VL_IDATASIZE) - 1; \ - msb = (msb >= (obits)) ? ((obits)-1) : msb; \ + msb = (msb >= (obits)) ? ((obits) - 1) : msb; \ _butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \ } \ (svar).write(_butemp); \ diff --git a/test_regress/t/t_wrapper_clone.cpp b/test_regress/t/t_wrapper_clone.cpp index 0c10674e9..296c00321 100644 --- a/test_regress/t/t_wrapper_clone.cpp +++ b/test_regress/t/t_wrapper_clone.cpp @@ -72,8 +72,7 @@ int main(int argc, char** argv) { } else if (pid == 0) { printf("child: here we go\n"); } else { - while (wait(nullptr) > 0) - ; + while (wait(nullptr) > 0); printf("parent: here we go\n"); topp->is_parent = 1; } From 5f23bf95f67c4537921d3977347e0c6317c58adb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 25 Aug 2025 22:05:40 -0400 Subject: [PATCH 230/252] Internals: Refactor V3EmitUtil to not be base class. No functional change. --- src/V3AstNodes.cpp | 6 +- src/V3CCtors.cpp | 2 +- src/V3Common.cpp | 8 +-- src/V3DepthBlock.cpp | 2 +- src/V3EmitCBase.cpp | 18 +++--- src/V3EmitCBase.h | 22 +++---- src/V3EmitCConstInit.h | 2 +- src/V3EmitCConstPool.cpp | 7 ++- src/V3EmitCFunc.cpp | 1 + src/V3EmitCFunc.h | 48 +++++++-------- src/V3EmitCHeaders.cpp | 41 +++++++------ src/V3EmitCImp.cpp | 65 ++++++++++---------- src/V3EmitCMain.cpp | 9 +-- src/V3EmitCModel.cpp | 125 +++++++++++++++++++++------------------ src/V3EmitCPch.cpp | 10 ++-- src/V3EmitCSyms.cpp | 102 +++++++++++++++++--------------- src/V3EmitV.cpp | 2 +- src/V3ExecGraph.cpp | 4 +- src/V3File.cpp | 3 + src/V3File.h | 2 + src/V3SchedTiming.cpp | 2 +- src/V3Task.cpp | 4 +- src/V3Trace.cpp | 8 +-- src/V3VariableOrder.cpp | 2 +- 24 files changed, 259 insertions(+), 236 deletions(-) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 12ab9539e..0e91f5fca 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -954,10 +954,10 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe info.m_type = "VlSampleQueue<" + sub.m_type + ">"; } else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) { UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union"); - info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">"; + info.m_type = "VlClassRef<" + EmitCUtil::prefixNameProtect(adtypep) + ">"; } else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) { UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union"); - info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*"; + info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*"; } else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union"); if (adtypep->isCompound()) compound = true; @@ -991,7 +991,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType); UASSERT_OBJ(!packed || sdtypep->packed(), this, "Unsupported type for packed struct or union"); - info.m_type = EmitCBase::prefixNameProtect(sdtypep); + info.m_type = EmitCUtil::prefixNameProtect(sdtypep); } else if (const AstBasicDType* const bdtypep = dtypep->basicp()) { // We don't print msb()/lsb() as multidim packed would require recursion, // and may confuse users as C++ data is stored always with bit 0 used diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 319ca6e87..f82eb8717 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -67,7 +67,7 @@ class V3CCtorsBuilder final { funcp->slow(!m_type.isClass()); // Only classes construct on fast path string preventUnusedStmt; if (m_type.isClass()) { - funcp->argTypes(EmitCBase::symClassVar()); + funcp->argTypes(EmitCUtil::symClassVar()); preventUnusedStmt = "(void)vlSymsp; // Prevent unused variable warning\n"; } else if (m_type.isCoverage()) { funcp->argTypes("bool first"); diff --git a/src/V3Common.cpp b/src/V3Common.cpp index 08b310e2b..fd48b2028 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -52,7 +52,7 @@ string V3Common::makeToStringCall(AstNodeDType* nodep, const std::string& lhs) { static void makeVlToString(AstClass* nodep) { AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const VlClassRef<" + EmitCBase::prefixNameProtect(nodep) + ">& obj"); + funcp->argTypes("const VlClassRef<" + EmitCUtil::prefixNameProtect(nodep) + ">& obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -66,7 +66,7 @@ static void makeVlToString(AstClass* nodep) { static void makeVlToString(AstIface* nodep) { AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "* obj"); + funcp->argTypes("const " + EmitCUtil::prefixNameProtect(nodep) + "* obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -81,7 +81,7 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) { UASSERT_OBJ(modp, nodep, "Unlinked struct package"); AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj"); + funcp->argTypes("const " + EmitCUtil::prefixNameProtect(nodep) + "& obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -145,7 +145,7 @@ static void makeToStringMiddle(AstClass* nodep) { string stmt = "out += "; if (!comma.empty()) stmt += "\", \"+ "; // comma = ", "; // Nothing further so not needed - stmt += EmitCBase::prefixNameProtect(nodep->extendsp()->dtypep()); + stmt += EmitCUtil::prefixNameProtect(nodep->extendsp()->dtypep()); stmt += "::to_string_middle();\n"; nodep->user1(true); // So what we extend dumps this funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index cbd5bd771..8513e0abd 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -57,7 +57,7 @@ class DepthBlockVisitor final : public VNVisitor { AstCCall* const callp = new AstCCall{nodep->fileline(), funcp}; callp->dtypeSetVoid(); if (VN_IS(m_modp, Class)) { - funcp->argTypes(EmitCBase::symClassVar()); + funcp->argTypes(EmitCUtil::symClassVar()); callp->argTypes("vlSymsp"); } UINFO(6, " New " << callp); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index ed88656af..a6530a2d1 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -36,9 +36,9 @@ EmitCParentModule::EmitCParentModule() { } //###################################################################### -// EmitCBase implementation +// EmitCUtil implementation -string EmitCBase::prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { +string EmitCUtil::prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { const string prefix = v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name()); // If all-uppercase prefix conflicts with a previous usage of the // prefix with different capitalization, rename to avoid conflict. @@ -78,13 +78,13 @@ string EmitCBaseVisitorConst::funcNameProtect(const AstCFunc* nodep, const AstNo modp = modp ? modp : EmitCParentModule::get(nodep); string name; if (nodep->isConstructor()) { - name += prefixNameProtect(modp); + name += EmitCUtil::prefixNameProtect(modp); } else if (nodep->isDestructor()) { name += "~"; - name += prefixNameProtect(modp); + name += EmitCUtil::prefixNameProtect(modp); } else { if (nodep->isLoose()) { - name += prefixNameProtect(modp); + name += EmitCUtil::prefixNameProtect(modp); name += "__"; } name += nodep->nameProtect(); @@ -112,7 +112,7 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) { string args; if (nodep->isLoose() && !nodep->isStatic()) { if (nodep->isConst().trueKnown()) args += "const "; - args += prefixNameProtect(EmitCParentModule::get(nodep)); + args += EmitCUtil::prefixNameProtect(EmitCParentModule::get(nodep)); args += "* vlSelf"; } if (nodep->needProcess()) { @@ -150,9 +150,9 @@ void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNode } if (withScope) { if (funcp->dpiExportDispatcher()) { - putns(funcp, topClassName() + "::"); + putns(funcp, EmitCUtil::topClassName() + "::"); } else if (funcp->isProperMethod()) { - putns(funcp, prefixNameProtect(modp) + "::"); + putns(funcp, EmitCUtil::prefixNameProtect(modp) + "::"); } } putns(funcp, funcNameProtect(funcp, modp)); @@ -313,7 +313,7 @@ std::pair EmitCBaseVisitorConst::textSection(const AstNodeMod string::size_type pos; while ((pos = text.find("`systemc_class_name")) != string::npos) { text.replace(pos, std::strlen("`systemc_class_name"), - EmitCBase::prefixNameProtect(modp)); + EmitCUtil::prefixNameProtect(modp)); } } return std::make_pair(text, fl); diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index e9baa02d0..c8b332e0b 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -45,9 +45,9 @@ public: }; //###################################################################### -// Base Visitor class -- holds output file pointer +// EmitC-related utility functions -class EmitCBase VL_NOT_FINAL { +class EmitCUtil final { public: static string voidSelfAssign(const AstNodeModule* modp) { const string className = prefixNameProtect(modp); @@ -81,7 +81,10 @@ public: } }; -class EmitCBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst, public EmitCBase { +//###################################################################### +// Base Visitor class -- holds output file pointer + +class EmitCBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { public: // STATE V3OutCFile* m_ofp = nullptr; @@ -130,15 +133,6 @@ public: void ensureNewLine() { ofp()->ensureNewLine(); } bool optSystemC() { return v3Global.opt.systemC(); } static string protect(const string& name) VL_MT_SAFE { return VIdProtect::protect(name); } - static string protectIf(const string& name, bool doIt) { - return VIdProtect::protectIf(name, doIt); - } - static string protectWordsIf(const string& name, bool doIt) { - return VIdProtect::protectWordsIf(name, doIt); - } - static string ifNoProtect(const string& in) VL_MT_SAFE { - return v3Global.opt.protectIds() ? "" : in; - } static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr); static AstCFile* newCFile(const string& filename, bool slow, bool source); static AstCFile* createCFile(const string& filename, bool slow, bool source) VL_MT_SAFE; @@ -153,11 +147,11 @@ public: if (const AstCUse* const usep = VN_CAST(itemp, CUse)) { if (usep->useType().containsAny(useType)) { if (usep->useType().containsAny(VUseType::INT_INCLUDE)) { - action("#include \"" + prefixNameProtect(usep) + ".h\"\n"); + action("#include \"" + EmitCUtil::prefixNameProtect(usep) + ".h\"\n"); continue; // Forward declaration is not necessary } if (usep->useType().containsAny(VUseType::INT_FWD_CLASS)) { - action("class " + prefixNameProtect(usep) + ";\n"); + action("class " + EmitCUtil::prefixNameProtect(usep) + ";\n"); } } } diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h index 051b75fb4..63c176350 100644 --- a/src/V3EmitCConstInit.h +++ b/src/V3EmitCConstInit.h @@ -32,7 +32,6 @@ class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitorConst { bool m_inUnpacked = false; // METHODS - uint32_t tabModulus(const AstNodeDType* dtypep) { const uint32_t elemBytes = dtypep->widthTotalBytes(); return dtypep->isString() ? 1 // String @@ -99,6 +98,7 @@ protected: } // LCOV_EXCL_STOP void visit(AstConst* nodep) override { + // TODO merge with EmitCFunc::emitConstant const V3Number& num = nodep->num(); UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool"); const AstNodeDType* const dtypep = nodep->dtypep(); diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index 3f9565d0a..6dc2043c0 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -42,8 +42,8 @@ class EmitCConstPool final : public EmitCConstInit { // METHODS OutCFilePair newOutCFile() const { - const string fileName = v3Global.opt.makeDir() + "/" + topClassName() + "__ConstPool_" - + cvtToStr(m_outFileCount) + ".cpp"; + const string fileName = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + + "__ConstPool_" + cvtToStr(m_outFileCount) + ".cpp"; AstCFile* const cfilep = newCFile(fileName, /* slow: */ true, /* source: */ true); V3OutCFile* const ofp = new V3OutCFile{fileName}; ofp->putsHeader(); @@ -84,7 +84,8 @@ class EmitCConstPool final : public EmitCConstInit { for (const AstVar* varp : varps) { maybeSplitCFile(); - const string nameProtect = topClassName() + "__ConstPool__" + varp->nameProtect(); + const string nameProtect + = EmitCUtil::topClassName() + "__ConstPool__" + varp->nameProtect(); puts("\n"); putns(varp, "extern const "); putns(varp, varp->dtypep()->cType(nameProtect, false, false)); diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 833127d24..05bb63602 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -488,6 +488,7 @@ void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) { void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) { // Put out constant set to the specified variable, or given variable in a string + // TODO merge with V3EmitCConstInit::visit(AstConst) putns(nodep, ""); if (nodep->num().isNull()) { putns(nodep, "VlNull{}"); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 46a42d316..5506ebb83 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -64,7 +64,7 @@ class EmitCLazyDecls final : public VNVisitorConst { void lazyDeclareConstPoolVar(AstVar* varp) { if (!declaredOnce(varp)) return; // Already declared const string nameProtect - = m_emitter.topClassName() + "__ConstPool__" + varp->nameProtect(); + = EmitCUtil::topClassName() + "__ConstPool__" + varp->nameProtect(); m_emitter.putns(varp, "extern const "); m_emitter.puts(varp->dtypep()->cType(nameProtect, false, false)); m_emitter.puts(";\n"); @@ -85,7 +85,7 @@ class EmitCLazyDecls final : public VNVisitorConst { void visit(AstVarRef* nodep) override { AstVar* const varp = nodep->varp(); // Only constant pool symbols are lazy declared for now ... - if (EmitCBase::isConstPoolMod(EmitCParentModule::get(varp))) { + if (EmitCUtil::isConstPoolMod(EmitCParentModule::get(varp))) { lazyDeclareConstPoolVar(varp); } } @@ -272,7 +272,7 @@ public: if (doneClasses.count(vbase)) continue; puts(doneClasses.empty() ? "" : "\n , "); doneClasses.emplace(vbase); - puts(prefixNameProtect(vbase)); + puts(EmitCUtil::EmitCUtil::prefixNameProtect(vbase)); if (constructorNeedsProcess(vbase)) { puts("(vlProcess, vlSymsp)"); } else { @@ -287,7 +287,7 @@ public: if (doneClasses.count(extp->classp())) continue; puts(doneClasses.empty() ? "" : "\n , "); doneClasses.emplace(extp->classp()); - puts(prefixNameProtect(extp->classp())); + puts(EmitCUtil::EmitCUtil::prefixNameProtect(extp->classp())); if (constructorNeedsProcess(extp->classp())) { puts("(vlProcess, vlSymsp"); } else { @@ -349,7 +349,7 @@ public: // "+" in the debug indicates a print from the model puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); for (int i = 0; i < m_modp->level(); ++i) puts(" "); - puts(prefixNameProtect(m_modp)); + puts(EmitCUtil::prefixNameProtect(m_modp)); puts(nodep->isLoose() ? "__" : "::"); puts(nodep->nameProtect() + "\\n\"); );\n"); @@ -358,7 +358,7 @@ public: if (!nodep->isStatic()) { // Standard prologue m_useSelfForThis = true; if (!VN_IS(m_modp, Class)) { - puts(symClassAssign()); // Uses vlSelf + puts(EmitCUtil::symClassAssign()); // Uses vlSelf } else { puts("(void)vlSelf; // Prevent unused variable warning\n"); } @@ -606,11 +606,11 @@ public: putns(nodep, funcp->name()); } else if (funcp->isProperMethod() && funcp->isStatic()) { // Call static method via the containing class - putns(funcModp, prefixNameProtect(funcModp) + "::"); + putns(funcModp, EmitCUtil::prefixNameProtect(funcModp) + "::"); putns(nodep, funcp->nameProtect()); } else if (nodep->superReference()) { // Calling superclass method - putns(funcModp, prefixNameProtect(funcModp) + "::"); + putns(funcModp, EmitCUtil::prefixNameProtect(funcModp) + "::"); putns(nodep, funcp->nameProtect()); } else if (funcp->isLoose()) { // Calling loose method @@ -642,7 +642,7 @@ public: return; } // assignment case; - putns(nodep, "VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", " + putns(nodep, "VL_NEW(" + EmitCUtil::prefixNameProtect(nodep->dtypep()) + ", " + optionalProcArg(nodep->dtypep()) + "vlSymsp"); putCommaIterateNext(nodep->argsp(), true); puts(")"); @@ -716,11 +716,11 @@ public: puts(cvtToStr(nodep->offset() + nodep->fileline()->firstColumn())); puts(", "); putsQuoted((!nodep->hier().empty() ? "." : "") - + protectWordsIf(nodep->hier(), nodep->protect())); + + VIdProtect::protectWordsIf(nodep->hier(), nodep->protect())); puts(", "); - putsQuoted(protectWordsIf(nodep->page(), nodep->protect())); + putsQuoted(VIdProtect::protectWordsIf(nodep->page(), nodep->protect())); puts(", "); - putsQuoted(protectWordsIf(nodep->comment(), nodep->protect())); + putsQuoted(VIdProtect::protectWordsIf(nodep->comment(), nodep->protect())); puts(", "); putsQuoted(nodep->linescov()); puts(");\n"); @@ -750,11 +750,11 @@ public: puts(cvtToStr(nodep->fileline()->firstColumn())); puts(", "); putsQuoted((!nodep->hier().empty() ? "." : "") - + protectWordsIf(nodep->hier(), nodep->protect())); + + VIdProtect::protectWordsIf(nodep->hier(), nodep->protect())); puts(", "); - putsQuoted(protectWordsIf(nodep->page(), nodep->protect())); + putsQuoted(VIdProtect::protectWordsIf(nodep->page(), nodep->protect())); puts(", "); - putsQuoted(protectWordsIf(nodep->comment(), nodep->protect())); + putsQuoted(VIdProtect::protectWordsIf(nodep->comment(), nodep->protect())); puts(");\n"); } void visit(AstCoverInc* nodep) override { @@ -1254,8 +1254,8 @@ public: VL_RESTORER(m_inUC); m_inUC = true; putnbs(nodep, ""); - putsDecoration(nodep, - ifNoProtect("// $c statement at " + nodep->fileline()->ascii() + "\n")); + putsDecoration(nodep, VIdProtect::ifNoProtect("// $c statement at " + + nodep->fileline()->ascii() + "\n")); iterateAndNextConstNull(nodep->exprsp()); puts("\n"); } @@ -1264,8 +1264,8 @@ public: m_inUC = true; puts("\n"); putnbs(nodep, ""); - putsDecoration(nodep, - ifNoProtect("// $c function at " + nodep->fileline()->ascii() + "\n")); + putsDecoration(nodep, VIdProtect::ifNoProtect("// $c function at " + + nodep->fileline()->ascii() + "\n")); iterateAndNextConstNull(nodep->exprsp()); puts("\n"); } @@ -1385,7 +1385,7 @@ public: puts(")"); } void visit(AstNewCopy* nodep) override { - putns(nodep, "VL_NEW(" + prefixNameProtect(nodep->dtypep())); + putns(nodep, "VL_NEW(" + EmitCUtil::prefixNameProtect(nodep->dtypep())); puts(", *"); // i.e. make into a reference iterateAndNextConstNull(nodep->rhsp()); puts(")"); @@ -1467,15 +1467,15 @@ public: void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); const AstNodeModule* const varModp = EmitCParentModule::get(varp); - if (isConstPoolMod(varModp)) { + if (EmitCUtil::isConstPoolMod(varModp)) { // Reference to constant pool variable - putns(nodep, topClassName() + "__ConstPool__"); + putns(nodep, EmitCUtil::topClassName() + "__ConstPool__"); } else if (varp->isStatic()) { // Access static variable via the containing class - putns(nodep, prefixNameProtect(varModp) + "::"); + putns(nodep, EmitCUtil::prefixNameProtect(varModp) + "::"); } else if (VN_IS(varModp, Class) && varModp != m_modp) { // Superclass member reference - putns(nodep, prefixNameProtect(varModp) + "::"); + putns(nodep, EmitCUtil::prefixNameProtect(varModp) + "::"); } else if (varp->isIfaceRef()) { putns(nodep, nodep->selfPointerProtect(m_useSelfForThis)); return; diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index c7a5824f1..f3875a7e9 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -47,8 +47,8 @@ class EmitCHeader final : public EmitCConstInit { for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstCell* const cellp = VN_CAST(nodep, Cell)) { decorateFirst(first, "// CELLS\n"); - putns(cellp, - prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n"); + putns(cellp, EmitCUtil::prefixNameProtect(cellp->modp()) + "* " + + cellp->nameProtect() + ";\n"); } } } @@ -111,7 +111,7 @@ class EmitCHeader final : public EmitCConstInit { if (const AstVar* const varp = VN_CAST(nodep, Var)) { if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() || varp->isGenVar()) { - const bool anon = isAnonOk(varp); + const bool anon = EmitCUtil::isAnonOk(varp); if (anon != lastAnon) emitCurrentList(); lastAnon = anon; varList.emplace_back(varp); @@ -130,7 +130,7 @@ class EmitCHeader final : public EmitCConstInit { } } else { // not class putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n"); - puts(symClassName() + "* const vlSymsp;\n"); + puts(EmitCUtil::symClassName() + "* const vlSymsp;\n"); } } void emitParamDecls(const AstNodeModule* modp) { @@ -156,9 +156,10 @@ class EmitCHeader final : public EmitCConstInit { } void emitCtorDtorDecls(const AstNodeModule* modp) { if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor - const string& name = prefixNameProtect(modp); + const string& name = EmitCUtil::prefixNameProtect(modp); putsDecoration(nullptr, "\n// CONSTRUCTORS\n"); - putns(modp, name + "(" + symClassName() + "* symsp, const char* v__name);\n"); + putns(modp, + name + "(" + EmitCUtil::symClassName() + "* symsp, const char* v__name);\n"); putns(modp, "~" + name + "();\n"); putns(modp, "VL_UNCOPYABLE(" + name + ");\n"); } @@ -290,7 +291,7 @@ class EmitCHeader final : public EmitCConstInit { } void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) { putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union" - puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n"); + puts(" " + EmitCUtil::prefixNameProtect(sdtypep) + " {\n"); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false)); @@ -347,7 +348,7 @@ class EmitCHeader final : public EmitCConstInit { } puts(");\n}\n"); } - putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep) + putns(sdtypep, "\nbool operator==(const " + EmitCUtil::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return "); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; @@ -357,10 +358,10 @@ class EmitCHeader final : public EmitCConstInit { } puts(";\n"); puts("}\n"); - putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep) + putns(sdtypep, "bool operator!=(const " + EmitCUtil::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return !(*this == rhs);\n}\n"); - putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep) + putns(sdtypep, "\nbool operator<(const " + EmitCUtil::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return "); puts("std::tie("); @@ -379,7 +380,7 @@ class EmitCHeader final : public EmitCConstInit { puts("}\n"); puts("};\n"); puts("template <>\n"); - putns(sdtypep, "struct VlIsCustomStruct<" + EmitCBase::prefixNameProtect(sdtypep) + putns(sdtypep, "struct VlIsCustomStruct<" + EmitCUtil::prefixNameProtect(sdtypep) + "> : public std::true_type {};\n"); } @@ -457,7 +458,7 @@ class EmitCHeader final : public EmitCConstInit { } void emitPackedUOrSBody(AstNodeUOrStructDType* sdtypep) { putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union" - puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n"); + puts(" " + EmitCUtil::prefixNameProtect(sdtypep) + " {\n"); AstMemberDType* itemp; AstMemberDType* lastItemp; @@ -553,13 +554,14 @@ class EmitCHeader final : public EmitCConstInit { if (const AstClass* const classp = VN_CAST(modp, Class)) { for (const AstClassExtends* extp = classp->extendsp(); extp; extp = VN_AS(extp->nextp(), ClassExtends)) { - putns(extp, "#include \"" + prefixNameProtect(extp->classp()->classOrPackagep()) + putns(extp, "#include \"" + + EmitCUtil::prefixNameProtect(extp->classp()->classOrPackagep()) + ".h\"\n"); } } // Forward declarations required by this AstNodeModule - puts("\nclass " + symClassName() + ";\n"); + puts("\nclass " + EmitCUtil::symClassName() + ";\n"); // From `systemc_header emitTextSection(modp, VNType::atScHdr); @@ -570,7 +572,7 @@ class EmitCHeader final : public EmitCConstInit { puts("\n"); putns(modp, "class "); if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) "); - puts(prefixNameProtect(modp)); + puts(EmitCUtil::prefixNameProtect(modp)); if (const AstClass* const classp = VN_CAST(modp, Class)) { puts(" : "); if (classp->extendsp()) { @@ -581,7 +583,7 @@ class EmitCHeader final : public EmitCConstInit { // Use virtual only for interfaces for class inheritance // (extends) puts(extp->classp()->useVirtualPublic() ? "virtual public " : "public "); - putns(extp, prefixNameProtect(extp->classp())); + putns(extp, EmitCUtil::prefixNameProtect(extp->classp())); needComma = true; } } else { @@ -618,10 +620,11 @@ class EmitCHeader final : public EmitCConstInit { } explicit EmitCHeader(const AstNodeModule* modp) { - UINFO(5, " Emitting header for " << prefixNameProtect(modp)); + UINFO(5, " Emitting header for " << EmitCUtil::prefixNameProtect(modp)); // Open output file - const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h"; + const string filename + = v3Global.opt.makeDir() + "/" + EmitCUtil::prefixNameProtect(modp) + ".h"; AstCFile* const cfilep = newCFile(filename, /* slow: */ false, /* source: */ false); V3OutCFile* const ofilep = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; @@ -630,7 +633,7 @@ class EmitCHeader final : public EmitCConstInit { ofp()->putsHeader(); puts("// DESCRIPTION: Verilator output: Design internal header\n"); - puts("// See " + topClassName() + ".h for the primary calling header\n"); + puts("// See " + EmitCUtil::topClassName() + ".h for the primary calling header\n"); ofp()->putsGuard(); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index febb88222..dacde7180 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -35,23 +35,23 @@ class EmitCGatherDependencies final : VNVisitorConst { std::set m_dependencies; // Header names to be included in output C++ file // METHODS - void addSymsDependency() { m_dependencies.insert(EmitCBase::symClassName()); } + void addSymsDependency() { m_dependencies.insert(EmitCUtil::symClassName()); } void addModDependency(const AstNodeModule* modp) { if (const AstClass* const classp = VN_CAST(modp, Class)) { - m_dependencies.insert(EmitCBase::prefixNameProtect(classp->classOrPackagep())); + m_dependencies.insert(EmitCUtil::prefixNameProtect(classp->classOrPackagep())); } else { - m_dependencies.insert(EmitCBase::prefixNameProtect(modp)); + m_dependencies.insert(EmitCUtil::prefixNameProtect(modp)); } } void addDTypeDependency(const AstNodeDType* nodep) { if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) { m_dependencies.insert( - EmitCBase::prefixNameProtect(dtypep->classp()->classOrPackagep())); + EmitCUtil::prefixNameProtect(dtypep->classp()->classOrPackagep())); } else if (const AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) { if (!dtypep->packed()) { UASSERT_OBJ(dtypep->classOrPackagep(), nodep, "Unlinked struct package"); - m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep())); + m_dependencies.insert(EmitCUtil::prefixNameProtect(dtypep->classOrPackagep())); } } } @@ -177,7 +177,8 @@ class EmitCImp final : EmitCFunc { V3OutCFile* const ofilep = new V3OutCFile{filename}; setOutputFile(ofilep, filep); } else { - string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); + string filename + = v3Global.opt.makeDir() + "/" + EmitCUtil::prefixNameProtect(m_fileModp); if (!subFileName.empty()) { filename += "__" + subFileName; filename = m_uniqueNames.get(filename); @@ -193,10 +194,10 @@ class EmitCImp final : EmitCFunc { putsHeader(); puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); - puts("// See " + topClassName() + ".h for the primary calling header\n"); + puts("// See " + EmitCUtil::topClassName() + ".h for the primary calling header\n"); puts("\n"); - puts("#include \"" + pchClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n"); for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); emitTextSection(m_modp, VNType::atScImpHdr); @@ -204,7 +205,7 @@ class EmitCImp final : EmitCFunc { void emitStaticVarDefns(const AstNodeModule* modp) { // Emit static variable definitions - const string modName = prefixNameProtect(modp); + const string modName = EmitCUtil::prefixNameProtect(modp); for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { if (varp->isStatic()) { @@ -215,7 +216,7 @@ class EmitCImp final : EmitCFunc { } } void emitParamDefns(const AstNodeModule* modp) { - const string modName = prefixNameProtect(modp); + const string modName = EmitCUtil::prefixNameProtect(modp); bool first = true; for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { @@ -242,15 +243,15 @@ class EmitCImp final : EmitCFunc { if (!first) puts("\n"); } void emitCtorImp(const AstNodeModule* modp) { - const string modName = prefixNameProtect(modp); + const string modName = EmitCUtil::prefixNameProtect(modp); puts("\n"); m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), "(" + modName + "* vlSelf);"); puts("\n"); - putns(modp, - modName + "::" + modName + "(" + symClassName() + "* symsp, const char* v__name)\n"); + putns(modp, modName + "::" + modName + "(" + EmitCUtil::symClassName() + + "* symsp, const char* v__name)\n"); puts(" : VerilatedModule{v__name}\n"); ofp()->indentInc(); @@ -290,7 +291,7 @@ class EmitCImp final : EmitCFunc { puts("}\n"); } void emitConfigureImp(const AstNodeModule* modp) { - const string modName = prefixNameProtect(modp); + const string modName = EmitCUtil::prefixNameProtect(modp); if (v3Global.opt.coverage()) { puts("\n"); @@ -312,7 +313,7 @@ class EmitCImp final : EmitCFunc { // arguments. if (v3Global.opt.coverage()) { puts("\n// Coverage\n"); - puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); + puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "::__vlCoverInsert("); puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); puts("const char* hierp, const char* pagep, const char* commentp, const char* " @@ -344,7 +345,7 @@ class EmitCImp final : EmitCFunc { } if (v3Global.opt.coverageToggle()) { puts("\n// Toggle Coverage\n"); - puts("void " + prefixNameProtect(m_modp) + "::__vlCoverToggleInsert("); + puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "::__vlCoverToggleInsert("); puts("int begin, int end, bool ranged, "); puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); @@ -390,7 +391,8 @@ class EmitCImp final : EmitCFunc { } void emitDestructorImp(const AstNodeModule* modp) { puts("\n"); - putns(modp, prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n"); + putns(modp, EmitCUtil::prefixNameProtect(modp) + "::~" + EmitCUtil::prefixNameProtect(modp) + + "() {\n"); emitTextSection(modp, VNType::atScDtor); puts("}\n"); splitSizeInc(10); @@ -403,8 +405,8 @@ class EmitCImp final : EmitCFunc { const string funcname = de ? "__Vdeserialize" : "__Vserialize"; const string op = de ? ">>" : "<<"; // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - putns(modp, "void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" - + classname + "& os) {\n"); + putns(modp, "void " + EmitCUtil::prefixNameProtect(modp) + "::" + protect(funcname) + + "(" + classname + "& os) {\n"); // Place a computed checksum to ensure proper structure save/restore formatting // OK if this hash includes some things we won't dump, since // just looking for loading the wrong model @@ -514,8 +516,8 @@ class EmitCImp final : EmitCFunc { if (hasCommonImp(modp) || hasCommonImp(classp)) { std::set headers; - headers.insert(prefixNameProtect(m_fileModp)); - headers.insert(symClassName()); + headers.insert(EmitCUtil::prefixNameProtect(m_fileModp)); + headers.insert(EmitCUtil::symClassName()); openNextOutputFile(headers, ""); @@ -595,7 +597,7 @@ class EmitCImp final : EmitCFunc { : m_fileModp{modp} , m_slow{slow} , m_cfilesr{cfilesr} { - UINFO(5, " Emitting implementation of " << prefixNameProtect(modp)); + UINFO(5, " Emitting implementation of " << EmitCUtil::prefixNameProtect(modp)); m_modp = modp; @@ -644,7 +646,7 @@ class EmitCTrace final : EmitCFunc { m_lazyDecls.reset(); // Need to emit new lazy declarations string filename - = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); + = (v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "_" + protect("_Trace")); filename = m_uniqueNames.get(filename); if (m_slow) filename += "__Slow"; filename += ".cpp"; @@ -663,7 +665,7 @@ class EmitCTrace final : EmitCFunc { // Includes puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n"); puts("\n"); } @@ -672,8 +674,8 @@ class EmitCTrace final : EmitCFunc { void openNextTypesFile() { UASSERT(!m_typesFp, "Declarations output file already open"); - string filename - = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_TraceDecls")); + string filename = (v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "_" + + protect("_TraceDecls")); filename = m_uniqueNames.get(filename); filename += "__Slow.cpp"; @@ -694,7 +696,7 @@ class EmitCTrace final : EmitCFunc { typesFp()->puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); typesFp()->puts("\n"); - typesFp()->puts("\nvoid " + prefixNameProtect(m_modp) + "__" + typesFp()->puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__" + protect("traceDeclTypesSub" + cvtToStr(m_traceTypeSubs++)) + "(" + v3Global.opt.traceClassBase() + "* tracep) {\n"); } @@ -709,15 +711,16 @@ class EmitCTrace final : EmitCFunc { // Forward declarations for subs in other files for (int i = 0; i < m_traceTypeSubs - 1; ++i) { - typesFp()->puts("void " + prefixNameProtect(m_modp) + "__" + typesFp()->puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "__" + protect("traceDeclTypesSub" + cvtToStr(i)) + "(" + v3Global.opt.traceClassBase() + "* tracep);\n"); } - typesFp()->puts("\nvoid " + prefixNameProtect(m_modp) + "__" + protect("trace_decl_types") - + "(" + v3Global.opt.traceClassBase() + "* tracep) {\n"); + typesFp()->puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__" + + protect("trace_decl_types") + "(" + v3Global.opt.traceClassBase() + + "* tracep) {\n"); for (int i = 0; i < m_traceTypeSubs; ++i) { - typesFp()->puts(prefixNameProtect(m_modp) + "__" + typesFp()->puts(EmitCUtil::prefixNameProtect(m_modp) + "__" + protect("traceDeclTypesSub" + cvtToStr(i)) + "(tracep);\n"); } } diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 8ee6fe5a2..0648d4564 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -41,7 +41,8 @@ public: private: // MAIN METHOD void emitInt() { - const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp"; + const string filename + = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__main.cpp"; AstCFile* const cfilep = newCFile(filename, false /*slow*/, true /*source*/); V3OutCFile cf{filename}; setOutputFile(&cf, cfilep); @@ -59,7 +60,7 @@ private: puts("\n"); puts("#include \"verilated.h\"\n"); - puts("#include \"" + topClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); puts("\n//======================\n\n"); @@ -72,8 +73,8 @@ private: puts("\n"); puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n"); - puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() - + "{contextp.get(), \"" + topName + "\"}};\n"); + puts("const std::unique_ptr<" + EmitCUtil::topClassName() + "> topp{new " + + EmitCUtil::topClassName() + "{contextp.get(), \"" + topName + "\"}};\n"); puts("\n"); puts("// Simulate until $finish\n"); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 0b9f300de..73ecf202d 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -55,7 +55,7 @@ class EmitCModel final : public EmitCFunc { void emitHeader(AstNodeModule* modp) { UASSERT(!ofp(), "Output file should not be open"); - const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".h"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + ".h"; setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}, newCFile(filename, /* slow: */ false, /* source: */ false)); @@ -80,15 +80,16 @@ class EmitCModel final : public EmitCFunc { // Declare foreign instances up front to make C++ happy puts("\n"); - puts("class " + symClassName() + ";\n"); - puts("class " + prefixNameProtect(modp) + ";\n"); // For rootp pointer only + puts("class " + EmitCUtil::symClassName() + ";\n"); + puts("class " + EmitCUtil::prefixNameProtect(modp) + ";\n"); // For rootp pointer only if (v3Global.opt.trace()) puts("class " + v3Global.opt.traceClassLang() + ";\n"); emitModCUse(modp, VUseType::INT_FWD_CLASS); // Note: This is needed for cell forwarding puts("\n"); puts("// This class is the main interface to the Verilated model\n"); - putns(modp, "class alignas(VL_CACHE_LINE_BYTES) " + topClassName() + " VL_NOT_FINAL : "); + putns(modp, "class alignas(VL_CACHE_LINE_BYTES) " + EmitCUtil::topClassName() + + " VL_NOT_FINAL : "); if (optSystemC()) { // SC_MODULE, but with multiple-inheritance of VerilatedModel puts("public ::sc_core::sc_module, "); @@ -98,7 +99,7 @@ class EmitCModel final : public EmitCFunc { ofp()->putsPrivate(true); // private: puts("// Symbol table holding complete model state (owned by this class)\n"); - puts(symClassName() + "* const vlSymsp;\n"); + puts(EmitCUtil::symClassName() + "* const vlSymsp;\n"); puts("\n"); ofp()->putsPrivate(false); // public: @@ -138,36 +139,36 @@ class EmitCModel final : public EmitCFunc { "// Otherwise the application code can consider these internals.\n"); for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstCell* const cellp = VN_CAST(nodep, Cell)) { - putns(cellp, prefixNameProtect(cellp->modp()) + "* const " + cellp->nameProtect() - + ";\n"); + putns(cellp, EmitCUtil::prefixNameProtect(cellp->modp()) + "* const " + + cellp->nameProtect() + ";\n"); } } // root instance pointer (for access to internals, including public_flat items). puts("\n// Root instance pointer to allow access to model internals,\n" "// including inlined /* verilator public_flat_* */ items.\n"); - puts(prefixNameProtect(modp) + "* const rootp;\n"); + puts(EmitCUtil::prefixNameProtect(modp) + "* const rootp;\n"); puts("\n"); ofp()->putsPrivate(false); // public: puts("// CONSTRUCTORS\n"); if (optSystemC()) { - puts("SC_CTOR(" + topClassName() + ");\n"); - puts("virtual ~" + topClassName() + "();\n"); + puts("SC_CTOR(" + EmitCUtil::topClassName() + ");\n"); + puts("virtual ~" + EmitCUtil::topClassName() + "();\n"); } else { puts("/// Construct the model; called by application code\n"); puts("/// If contextp is null, then the model will use the default global " "context\n"); puts("/// If name is \"\", then makes a wrapper with a\n"); puts("/// single model invisible with respect to DPI scope names.\n"); - puts("explicit " + topClassName() + "(VerilatedContext* contextp," + puts("explicit " + EmitCUtil::topClassName() + "(VerilatedContext* contextp," + " const char* name = \"TOP\");\n"); - puts("explicit " + topClassName() + "(const char* name = \"TOP\");\n"); + puts("explicit " + EmitCUtil::topClassName() + "(const char* name = \"TOP\");\n"); puts("/// Destroy the model; called (often implicitly) by application code\n"); - puts("virtual ~" + topClassName() + "();\n"); + puts("virtual ~" + EmitCUtil::topClassName() + "();\n"); } ofp()->putsPrivate(true); - puts("VL_UNCOPYABLE(" + topClassName() + "); ///< Copying not allowed\n"); + puts("VL_UNCOPYABLE(" + EmitCUtil::topClassName() + "); ///< Copying not allowed\n"); puts("\n"); ofp()->putsPrivate(false); // public: @@ -243,9 +244,9 @@ class EmitCModel final : public EmitCFunc { puts("\n"); puts("// Serialization functions\n"); puts("friend VerilatedSerialize& operator<<(VerilatedSerialize& os, " // - + topClassName() + "& rhs);\n"); + + EmitCUtil::topClassName() + "& rhs);\n"); puts("friend VerilatedDeserialize& operator>>(VerilatedDeserialize& os, " - + topClassName() + "& rhs);\n"); + + EmitCUtil::topClassName() + "& rhs);\n"); } puts("\n// Abstract methods from VerilatedModel\n"); @@ -277,15 +278,17 @@ class EmitCModel final : public EmitCFunc { putSectionDelimiter("Constructors"); puts("\n"); - putns(modp, topClassName() + "::" + topClassName()); + putns(modp, EmitCUtil::topClassName() + "::" + EmitCUtil::topClassName()); if (optSystemC()) { puts("(sc_core::sc_module_name /* unused */)\n"); puts(" : VerilatedModel{*Verilated::threadContextp()}\n"); - puts(" , vlSymsp{new " + symClassName() + "(contextp(), name(), this)}\n"); + puts(" , vlSymsp{new " + EmitCUtil::symClassName() + + "(contextp(), name(), this)}\n"); } else { puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n"); puts(" : VerilatedModel{*_vcontextp__}\n"); - puts(" , vlSymsp{new " + symClassName() + "(contextp(), _vcname__, this)}\n"); + puts(" , vlSymsp{new " + EmitCUtil::symClassName() + + "(contextp(), _vcname__, this)}\n"); } // Set up IO references @@ -356,8 +359,10 @@ class EmitCModel final : public EmitCFunc { if (!optSystemC()) { puts("\n"); - puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n"); - puts(" : " + topClassName() + "(Verilated::threadContextp(), _vcname__)\n{\n}\n"); + puts(EmitCUtil::topClassName() + "::" + EmitCUtil::topClassName() + + "(const char* _vcname__)\n"); + puts(" : " + EmitCUtil::topClassName() + + "(Verilated::threadContextp(), _vcname__)\n{\n}\n"); } } @@ -365,7 +370,7 @@ class EmitCModel final : public EmitCFunc { putSectionDelimiter("Destructor"); puts("\n"); - puts(topClassName() + "::~" + topClassName() + "() {\n"); + puts(EmitCUtil::topClassName() + "::~" + EmitCUtil::topClassName() + "() {\n"); puts("delete vlSymsp;\n"); puts("}\n"); } @@ -373,7 +378,7 @@ class EmitCModel final : public EmitCFunc { void emitStandardMethods1(AstNodeModule* modp) { UASSERT_OBJ(modp->isTop(), modp, "Attempting to emitWrapEval for non-top class"); - const string topModNameProtected = prefixNameProtect(modp); + const string topModNameProtected = EmitCUtil::prefixNameProtect(modp); const string selfDecl = "(" + topModNameProtected + "* vlSelf)"; putSectionDelimiter("Evaluation function"); @@ -391,7 +396,7 @@ class EmitCModel final : public EmitCFunc { if (optSystemC() && v3Global.usesTiming()) { // ::eval - puts("\nvoid " + topClassName() + "::eval() {\n"); + puts("\nvoid " + EmitCUtil::topClassName() + "::eval() {\n"); puts("eval_step();\n"); puts("if (eventsPending()) {\n"); puts("sc_core::sc_time dt = sc_core::sc_time::from_value(nextTimeSlot() - " @@ -403,14 +408,14 @@ class EmitCModel final : public EmitCFunc { puts("}\n"); // ::eval_sens - puts("\nvoid " + topClassName() + "::eval_sens() {\n"); + puts("\nvoid " + EmitCUtil::topClassName() + "::eval_sens() {\n"); puts("trigger_eval.notify();\n"); puts("}\n"); } // ::eval_step - puts("\nvoid " + topClassName() + "::eval_step() {\n"); - puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + topClassName() + puts("\nvoid " + EmitCUtil::topClassName() + "::eval_step() {\n"); + puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + EmitCUtil::topClassName() + "::eval_step\\n\"); );\n"); puts("#ifdef VL_DEBUG\n"); @@ -445,14 +450,14 @@ class EmitCModel final : public EmitCFunc { } void emitStandardMethods2(AstNodeModule* modp) { - const string topModNameProtected = prefixNameProtect(modp); + const string topModNameProtected = EmitCUtil::prefixNameProtect(modp); const string selfDecl = "(" + topModNameProtected + "* vlSelf)"; // ::eval_end_step if (v3Global.needTraceDumper() && !optSystemC()) { puts("\n"); - putns(modp, "void " + topClassName() + "::eval_end_step() {\n"); - puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+eval_end_step " + topClassName() + putns(modp, "void " + EmitCUtil::topClassName() + "::eval_end_step() {\n"); + puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+eval_end_step " + EmitCUtil::topClassName() + "::eval_end_step\\n\"); );\n"); puts("#ifdef VM_TRACE\n"); putsDecoration(nullptr, "// Tracing\n"); @@ -464,16 +469,19 @@ class EmitCModel final : public EmitCFunc { putSectionDelimiter("Events and timing"); if (auto* const delaySchedp = v3Global.rootp()->delaySchedulerp()) { - putns(modp, "bool " + topClassName() + "::eventsPending() { return !vlSymsp->TOP."); + putns(modp, "bool " + EmitCUtil::topClassName() + + "::eventsPending() { return !vlSymsp->TOP."); puts(delaySchedp->nameProtect()); puts(".empty(); }\n\n"); - putns(modp, "uint64_t " + topClassName() + "::nextTimeSlot() { return vlSymsp->TOP."); + putns(modp, "uint64_t " + EmitCUtil::topClassName() + + "::nextTimeSlot() { return vlSymsp->TOP."); puts(delaySchedp->nameProtect()); puts(".nextTimeSlot(); }\n"); } else { - putns(modp, "bool " + topClassName() + "::eventsPending() { return false; }\n\n"); - puts("uint64_t " + topClassName() + "::nextTimeSlot() {\n"); + putns(modp, + "bool " + EmitCUtil::topClassName() + "::eventsPending() { return false; }\n\n"); + puts("uint64_t " + EmitCUtil::topClassName() + "::nextTimeSlot() {\n"); puts("VL_FATAL_MT(__FILE__, __LINE__, \"\", \"No delays in the design\");\n"); puts("return 0;\n}\n"); } @@ -483,7 +491,7 @@ class EmitCModel final : public EmitCFunc { if (!optSystemC()) { // ::name puts("\n"); - putns(modp, "const char* " + topClassName() + "::name() const {\n"); + putns(modp, "const char* " + EmitCUtil::topClassName() + "::name() const {\n"); puts(/**/ "return vlSymsp->name();\n"); puts("}\n"); } @@ -494,23 +502,23 @@ class EmitCModel final : public EmitCFunc { putns(modp, "void " + topModNameProtected + "__" + protect("_eval_final") + selfDecl + ";\n"); // ::final - puts("\nVL_ATTR_COLD void " + topClassName() + "::final() {\n"); + puts("\nVL_ATTR_COLD void " + EmitCUtil::topClassName() + "::final() {\n"); puts(/**/ topModNameProtected + "__" + protect("_eval_final") + "(&(vlSymsp->TOP));\n"); puts("}\n"); putSectionDelimiter("Implementations of abstract methods from VerilatedModel\n"); - putns(modp, "const char* " + topClassName() + putns(modp, "const char* " + EmitCUtil::topClassName() + "::hierName() const { return vlSymsp->name(); }\n"); - putns(modp, "const char* " + topClassName() + "::modelName() const { return \"" - + topClassName() + "\"; }\n"); + putns(modp, "const char* " + EmitCUtil::topClassName() + "::modelName() const { return \"" + + EmitCUtil::topClassName() + "\"; }\n"); const int threads = v3Global.opt.hierChild() ? v3Global.opt.threads() : std::max(v3Global.opt.threads(), v3Global.opt.hierThreads()); - putns(modp, "unsigned " + topClassName() + "::threads() const { return " + putns(modp, "unsigned " + EmitCUtil::topClassName() + "::threads() const { return " + cvtToStr(threads) + "; }\n"); - putns(modp, "void " + topClassName() + putns(modp, "void " + EmitCUtil::topClassName() + "::prepareClone() const { contextp()->prepareClone(); }\n"); - putns(modp, "void " + topClassName() + "::atClone() const {\n"); + putns(modp, "void " + EmitCUtil::topClassName() + "::atClone() const {\n"); if (v3Global.opt.threads() > 1) { puts("vlSymsp->__Vm_threadPoolp = static_cast("); } @@ -519,7 +527,7 @@ class EmitCModel final : public EmitCFunc { puts(";\n}\n"); if (v3Global.opt.trace()) { - putns(modp, "std::unique_ptr " + topClassName() + putns(modp, "std::unique_ptr " + EmitCUtil::topClassName() + "::traceConfig() const {\n"); puts("return std::unique_ptr{new VerilatedTraceConfig{"); puts(v3Global.opt.useTraceParallel() ? "true" : "false"); @@ -531,7 +539,7 @@ class EmitCModel final : public EmitCFunc { } void emitTraceMethods(AstNodeModule* modp) { - const string topModNameProtected = prefixNameProtect(modp); + const string topModNameProtected = EmitCUtil::prefixNameProtect(modp); putSectionDelimiter("Trace configuration"); @@ -547,8 +555,8 @@ class EmitCModel final : public EmitCFunc { putns(modp, "VL_ATTR_COLD static void " + protect("trace_init") + "(void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n"); putsDecoration(modp, "// Callback from tracep->open()\n"); - puts(voidSelfAssign(modp)); - puts(symClassAssign()); + puts(EmitCUtil::voidSelfAssign(modp)); + puts(EmitCUtil::symClassAssign()); puts("if (!vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n"); puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n"); puts("\"Turning on wave traces requires Verilated::traceEverOn(true) call before time " @@ -570,11 +578,11 @@ class EmitCModel final : public EmitCFunc { // ::traceRegisterModel puts("\n"); - putns(modp, "VL_ATTR_COLD void " + topClassName() + "::traceBaseModel("); + putns(modp, "VL_ATTR_COLD void " + EmitCUtil::topClassName() + "::traceBaseModel("); puts("VerilatedTraceBaseC* tfp, int levels, int options) {\n"); if (optSystemC()) { puts(/**/ "if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {\n"); - puts(/****/ "vl_fatal(__FILE__, __LINE__, name(), \"" + topClassName() + puts(/****/ "vl_fatal(__FILE__, __LINE__, name(), \"" + EmitCUtil::topClassName() + +"::trace() is called before sc_core::sc_start(). " "Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before trace() to complete " "elaboration.\");\n"); @@ -584,7 +592,7 @@ class EmitCModel final : public EmitCFunc { puts(/**/ v3Global.opt.traceClassBase() + "C* const stfp = dynamic_cast<" + v3Global.opt.traceClassBase() + "C*>(tfp);\n"); puts(/**/ "if (VL_UNLIKELY(!stfp)) {\n"); - puts(/****/ "vl_fatal(__FILE__, __LINE__, __FILE__,\"'" + topClassName() + puts(/****/ "vl_fatal(__FILE__, __LINE__, __FILE__,\"'" + EmitCUtil::topClassName() + "::trace()' called on non-" + v3Global.opt.traceClassBase() + "C object;\"\n" + "\" use --trace-fst with VerilatedFst object," + " and --trace-vcd with VerilatedVcd object\");\n"); @@ -600,15 +608,15 @@ class EmitCModel final : public EmitCFunc { void emitSerializationFunctions() { putSectionDelimiter("Model serialization"); - puts("\nVerilatedSerialize& operator<<(VerilatedSerialize& os, " + topClassName() - + "& rhs) {\n"); + puts("\nVerilatedSerialize& operator<<(VerilatedSerialize& os, " + + EmitCUtil::topClassName() + "& rhs) {\n"); puts(/**/ "Verilated::quiesce();\n"); puts(/**/ "rhs.vlSymsp->" + protect("__Vserialize") + "(os);\n"); puts(/**/ "return os;\n"); puts("}\n"); - puts("\nVerilatedDeserialize& operator>>(VerilatedDeserialize& os, " + topClassName() - + "& rhs) {\n"); + puts("\nVerilatedDeserialize& operator>>(VerilatedDeserialize& os, " + + EmitCUtil::topClassName() + "& rhs) {\n"); puts(/**/ "Verilated::quiesce();\n"); puts(/**/ "rhs.vlSymsp->" + protect("__Vdeserialize") + "(os);\n"); puts(/**/ "return os;\n"); @@ -618,7 +626,7 @@ class EmitCModel final : public EmitCFunc { void emitImplementation(AstNodeModule* modp) { UASSERT(!ofp(), "Output file should not be open"); - const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".cpp"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + ".cpp"; setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}, newCFile(filename, /* slow: */ false, /* source: */ true)); @@ -628,7 +636,7 @@ class EmitCModel final : public EmitCFunc { "Model implementation (design independent parts)\n"); puts("\n"); - puts("#include \"" + pchClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n"); if (v3Global.opt.trace()) { puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); } @@ -659,7 +667,8 @@ class EmitCModel final : public EmitCFunc { } if (!ofp()) { - string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi_Export"; + string filename + = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi_Export"; filename = m_uniqueNames.get(filename); filename += ".cpp"; setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename} @@ -671,8 +680,8 @@ class EmitCModel final : public EmitCFunc { puts( "// DESCRIPTION: Verilator output: Implementation of DPI export functions.\n"); puts("//\n"); - puts("#include \"" + topClassName() + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n"); puts("#include \"verilated_dpi.h\"\n"); puts("\n"); } diff --git a/src/V3EmitCPch.cpp b/src/V3EmitCPch.cpp index e1c6df69b..fdac954d1 100644 --- a/src/V3EmitCPch.cpp +++ b/src/V3EmitCPch.cpp @@ -23,19 +23,19 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Precompiled header emitter -class EmitCPch final : EmitCBase { +class EmitCPch final { public: // METHODS void emitPch() { // Generate the makefile - V3OutCFile of{v3Global.opt.makeDir() + "/" + pchClassName() + ".h"}; + V3OutCFile of{v3Global.opt.makeDir() + "/" + EmitCUtil::pchClassName() + ".h"}; of.putsHeader(); of.puts("// DESCRIPTION: Verilator output: Precompiled header\n"); of.puts("//\n"); of.puts("// Internal details; most user sources do not need this header,\n"); of.puts("// unless using verilator public meta comments.\n"); - of.puts("// Suggest use " + topClassName() + ".h instead.\n"); + of.puts("// Suggest use " + EmitCUtil::topClassName() + ".h instead.\n"); of.puts("\n"); of.putsGuard(); @@ -54,8 +54,8 @@ public: if (v3Global.dpi()) of.puts("#include \"verilated_dpi.h\"\n"); of.puts("\n"); - of.puts("#include \"" + symClassName() + ".h\"\n"); - of.puts("#include \"" + topClassName() + ".h\"\n"); + of.puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n"); + of.puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); of.puts("\n// Additional include files added using '--compiler-include'\n"); for (const string& filename : v3Global.opt.compilerIncludes()) { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 1f9f7cf28..f26d98d3b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -399,7 +399,7 @@ public: void EmitCSyms::emitSymHdr() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".h"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + ".h"; AstCFile* const cfilep = newCFile(filename, true /*slow*/, false /*source*/); V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; setOutputFile(ofilep, cfilep); @@ -421,13 +421,13 @@ void EmitCSyms::emitSymHdr() { if (v3Global.opt.usesProfiler()) puts("#include \"verilated_profiler.h\"\n"); puts("\n// INCLUDE MODEL CLASS\n"); - puts("\n#include \"" + topClassName() + ".h\"\n"); + puts("\n#include \"" + EmitCUtil::topClassName() + ".h\"\n"); puts("\n// INCLUDE MODULE CLASSES\n"); for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_AS(nodep->nextp(), NodeModule)) { if (VN_IS(nodep, Class)) continue; // Class included earlier - putns(nodep, "#include \"" + prefixNameProtect(nodep) + ".h\"\n"); + putns(nodep, "#include \"" + EmitCUtil::prefixNameProtect(nodep) + ".h\"\n"); } if (v3Global.dpi()) { @@ -446,12 +446,12 @@ void EmitCSyms::emitSymHdr() { } puts("\n// SYMS CLASS (contains all model state)\n"); - puts("class alignas(VL_CACHE_LINE_BYTES)" + symClassName() + puts("class alignas(VL_CACHE_LINE_BYTES)" + EmitCUtil::symClassName() + " final : public VerilatedSyms {\n"); ofp()->putsPrivate(false); // public: puts("// INTERNAL STATE\n"); - puts(topClassName() + "* const __Vm_modelp;\n"); + puts(EmitCUtil::topClassName() + "* const __Vm_modelp;\n"); if (v3Global.needTraceDumper()) { // __Vm_dumperp is local, otherwise we wouldn't know what design's eval() @@ -496,9 +496,9 @@ void EmitCSyms::emitSymHdr() { const AstScope* const scopep = i.first; const AstNodeModule* const modp = i.second; if (VN_IS(modp, Class)) continue; - const string name = prefixNameProtect(modp); + const string name = EmitCUtil::prefixNameProtect(modp); ofp()->printf("%-30s ", name.c_str()); - putns(scopep, protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); + putns(scopep, VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); } if (m_coverBins) { @@ -528,12 +528,12 @@ void EmitCSyms::emitSymHdr() { } puts("\n// CONSTRUCTORS\n"); - puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName() - + "* modelp);\n"); - puts("~"s + symClassName() + "();\n"); + puts(EmitCUtil::symClassName() + "(VerilatedContext* contextp, const char* namep, " + + EmitCUtil::topClassName() + "* modelp);\n"); + puts("~"s + EmitCUtil::symClassName() + "();\n"); for (const auto& i : m_usesVfinal) { - puts("void " + symClassName() + "_" + cvtToStr(i.first) + "("); + puts("void " + EmitCUtil::symClassName() + "_" + cvtToStr(i.first) + "("); if (i.second) puts("int __Vfinal"); puts(");\n"); } @@ -599,8 +599,8 @@ void EmitCSyms::checkSplit(bool usesVfinal) { v3Global.useParallelBuild(true); m_numStmts = 0; - const string filename - = v3Global.opt.makeDir() + "/" + symClassName() + "__" + cvtToStr(++m_funcNum) + ".cpp"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + "__" + + cvtToStr(++m_funcNum) + ".cpp"; AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/); cfilep->support(true); m_usesVfinal[m_funcNum] = usesVfinal; @@ -609,12 +609,13 @@ void EmitCSyms::checkSplit(bool usesVfinal) { V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; setOutputFile(ofilep, cfilep); - m_ofpBase->puts(symClassName() + "_" + cvtToStr(m_funcNum) + "("); + m_ofpBase->puts(EmitCUtil::symClassName() + "_" + cvtToStr(m_funcNum) + "("); if (usesVfinal) m_ofpBase->puts("__Vfinal"); m_ofpBase->puts(");\n"); emitSymImpPreamble(); - puts("void " + symClassName() + "::" + symClassName() + "_" + cvtToStr(m_funcNum) + "("); + puts("void " + EmitCUtil::symClassName() + "::" + EmitCUtil::symClassName() + "_" + + cvtToStr(m_funcNum) + "("); if (usesVfinal) puts("int __Vfinal"); puts(") {\n"); } @@ -626,12 +627,12 @@ void EmitCSyms::emitSymImpPreamble() { puts("\n"); // Includes - puts("#include \"" + pchClassName() + ".h\"\n"); - puts("#include \"" + topClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_AS(nodep->nextp(), NodeModule)) { if (VN_IS(nodep, Class)) continue; // Class included earlier - putns(nodep, "#include \"" + prefixNameProtect(nodep) + ".h\"\n"); + putns(nodep, "#include \"" + EmitCUtil::prefixNameProtect(nodep) + ".h\"\n"); } puts("\n"); // Declarations for DPI Export implementation functions @@ -683,7 +684,7 @@ void EmitCSyms::emitScopeHier(bool destroy) { void EmitCSyms::emitSymImp() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".cpp"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + ".cpp"; AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/); cfilep->support(true); @@ -699,7 +700,7 @@ void EmitCSyms::emitSymImp() { const string funcname = de ? "__Vdeserialize" : "__Vserialize"; const string op = de ? ">>" : "<<"; // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void " + symClassName() + "::" + protect(funcname) + "(" + classname + puts("void " + EmitCUtil::symClassName() + "::" + protect(funcname) + "(" + classname + "& os) {\n"); puts("// Internal state\n"); if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n"); @@ -707,8 +708,8 @@ void EmitCSyms::emitSymImp() { puts("// Module instance state\n"); for (const auto& pair : m_scopes) { const AstScope* const scopep = pair.first; - puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect(funcname) - + "(os);\n"); + puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + + protect(funcname) + "(os);\n"); } puts("}\n"); } @@ -718,7 +719,7 @@ void EmitCSyms::emitSymImp() { puts("// FUNCTIONS\n"); // Destructor - puts(symClassName() + "::~" + symClassName() + "()\n"); + puts(EmitCUtil::symClassName() + "::~" + EmitCUtil::symClassName() + "()\n"); puts("{\n"); emitScopeHier(true); if (v3Global.needTraceDumper()) { @@ -730,21 +731,21 @@ void EmitCSyms::emitSymImp() { // Do not overwrite data during the last hierarchical stage. const string firstHierCall = (v3Global.opt.hierBlocks().empty() || v3Global.opt.hierChild()) ? "true" : "false"; - puts("_vm_pgoProfiler.write(\"" + topClassName() + puts("_vm_pgoProfiler.write(\"" + EmitCUtil::topClassName() + "\", _vm_contextp__->profVltFilename(), " + firstHierCall + ");\n"); } puts("}\n"); if (v3Global.needTraceDumper()) { if (!optSystemC()) { - puts("\nvoid " + symClassName() + "::_traceDump() {\n"); + puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDump() {\n"); // Caller checked for __Vm_dumperp non-nullptr puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumperp->dump(VL_TIME_Q());\n"); puts("}\n"); } - puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n"); + puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDumpOpen() {\n"); puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n"); puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); @@ -755,7 +756,7 @@ void EmitCSyms::emitSymImp() { puts("}\n"); puts("}\n"); - puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n"); + puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDumpClose() {\n"); puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumping = false;\n"); puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n"); @@ -764,8 +765,9 @@ void EmitCSyms::emitSymImp() { puts("\n"); // Constructor - puts(symClassName() + "::" + symClassName() - + "(VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp)\n"); + puts(EmitCUtil::symClassName() + "::" + EmitCUtil::symClassName() + + "(VerilatedContext* contextp, const char* namep, " + EmitCUtil::topClassName() + + "* modelp)\n"); puts(" : VerilatedSyms{contextp}\n"); puts(" // Setup internal state of the Syms class\n"); puts(" , __Vm_modelp{modelp}\n"); @@ -813,7 +815,7 @@ void EmitCSyms::emitSymImp() { } else { // The "." is added by catName puts(", Verilated::catName(namep, "); - putsQuoted(protectWordsIf(scopep->prettyName(), scopep->protect())); + putsQuoted(VIdProtect::protectWordsIf(scopep->prettyName(), scopep->protect())); puts(")"); } puts("}\n"); @@ -862,17 +864,18 @@ void EmitCSyms::emitSymImp() { const AstNodeModule* const modp = i.second; if (const AstScope* const aboveScopep = scopep->aboveScopep()) { checkSplit(false); - const string protName = protectWordsIf(scopep->name(), scopep->protect()); + const string protName = VIdProtect::protectWordsIf(scopep->name(), scopep->protect()); if (VN_IS(modp, ClassPackage)) { // ClassPackage modules seem to be a bit out of place, so hard code... putns(scopep, "TOP"); } else { - putns(scopep, protectIf(aboveScopep->nameDotless(), aboveScopep->protect())); + putns(scopep, + VIdProtect::protectIf(aboveScopep->nameDotless(), aboveScopep->protect())); } puts("."); puts(protName.substr(protName.rfind('.') + 1)); puts(" = &"); - puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); + puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); ++m_numStmts; } } @@ -885,7 +888,7 @@ void EmitCSyms::emitSymImp() { // first is used by AstCoverDecl's call to __vlCoverInsert const bool first = !modp->user1(); modp->user1(true); - putns(scopep, protectIf(scopep->nameDotless(), scopep->protect()) + "." + putns(scopep, VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect("__Vconfigure") + "(" + (first ? "true" : "false") + ");\n"); ++m_numStmts; } @@ -896,7 +899,7 @@ void EmitCSyms::emitSymImp() { checkSplit(false); putns(it->second.m_nodep, protect("__Vscope_" + it->second.m_symName) + ".configure(this, name(), "); - putsQuoted(protectWordsIf(it->second.m_prettyName, true)); + putsQuoted(VIdProtect::protectWordsIf(it->second.m_prettyName, true)); puts(", "); putsQuoted(protect(scopeDecodeIdentifier(it->second.m_prettyName))); puts(", "); @@ -926,7 +929,7 @@ void EmitCSyms::emitSymImp() { protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); putsQuoted(funcp->cname()); // Not protected - user asked for import/export puts(", (void*)(&"); - puts(prefixNameProtect(modp)); + puts(EmitCUtil::prefixNameProtect(modp)); puts("__"); puts(funcp->nameProtect()); puts("));\n"); @@ -975,7 +978,7 @@ void EmitCSyms::emitSymImp() { putsQuoted(protect(it->second.m_varBasePretty)); std::string varName; - varName += protectIf(scopep->nameDotless(), scopep->protect()) + "."; + varName += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "."; varName += protect(varp->name()); if (varp->isParam()) { @@ -1022,7 +1025,7 @@ void EmitCSyms::emitSymImp() { void EmitCSyms::emitDpiHdr() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi.h"; AstCFile* const cfilep = newCFile(filename, false /*slow*/, false /*source*/); cfilep->support(true); V3OutCFile hf{filename}; @@ -1051,14 +1054,16 @@ void EmitCSyms::emitDpiHdr() { for (AstCFunc* nodep : m_dpis) { if (nodep->dpiExportDispatcher()) { if (!firstExp++) puts("\n// DPI EXPORTS\n"); - putsDecoration(nodep, "// DPI export" - + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); + putsDecoration( + nodep, "// DPI export" + + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" + cFuncArgs(nodep) + ");\n"); } else if (nodep->dpiImportPrototype()) { if (!firstImp++) puts("\n// DPI IMPORTS\n"); - putsDecoration(nodep, "// DPI import" - + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); + putsDecoration( + nodep, "// DPI import" + + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" + cFuncArgs(nodep) + ");\n"); } @@ -1077,7 +1082,7 @@ void EmitCSyms::emitDpiHdr() { void EmitCSyms::emitDpiImp() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp"; + const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi.cpp"; AstCFile* const cfilep = newCFile(filename, false /*slow*/, true /*source*/); cfilep->support(true); V3OutCFile hf(filename); @@ -1092,14 +1097,14 @@ void EmitCSyms::emitDpiImp() { puts("// function names, you will get multiple definition link errors from here.\n"); puts("// This is an unfortunate result of the DPI specification.\n"); puts("// To solve this, either\n"); - puts("// 1. Call " + topClassName() + "::{export_function} instead,\n"); + puts("// 1. Call " + EmitCUtil::topClassName() + "::{export_function} instead,\n"); puts("// and do not even bother to compile this file\n"); puts("// or 2. Compile all __Dpi.cpp files in the same compiler run,\n"); puts("// and #ifdefs already inserted here will sort everything out.\n"); puts("\n"); - puts("#include \"" + topClassName() + "__Dpi.h\"\n"); - puts("#include \"" + topClassName() + ".h\"\n"); + puts("#include \"" + EmitCUtil::topClassName() + "__Dpi.h\"\n"); + puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); puts("\n"); for (AstCFunc* nodep : m_dpis) { @@ -1109,8 +1114,9 @@ void EmitCSyms::emitDpiImp() { puts("#define VL_DPIDECL_" + nodep->name() + "_\n"); putns(nodep, nodep->rtnTypeVoid() + " " + nodep->name() + "(" + cFuncArgs(nodep) + ") {\n"); - puts("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); - putns(nodep, "return " + topClassName() + "::" + nodep->name() + "("); + puts("// DPI export" + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) + + "\n"); + putns(nodep, "return " + EmitCUtil::topClassName() + "::" + nodep->name() + "("); string comma; for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) { if (const AstVar* const portp = VN_CAST(stmtp, Var)) { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 4f730c348..e2058af3f 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -73,7 +73,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { // VISITORS void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNodeModule* nodep) override { - putfs(nodep, nodep->verilogKwd() + " " + EmitCBase::prefixNameProtect(nodep) + ";\n"); + putfs(nodep, nodep->verilogKwd() + " " + EmitCUtil::prefixNameProtect(nodep) + ";\n"); iterateChildrenConst(nodep); putqs(nodep, "end" + nodep->verilogKwd() + "\n"); } diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 69735a309..0e27a9356 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -982,8 +982,8 @@ const std::vector createThreadFunctions(const ThreadSchedule& schedul funcp->argTypes("void* voidSelf, bool even_cycle"); // Setup vlSelf and vlSyms - funcp->addStmtsp(new AstCStmt{fl, EmitCBase::voidSelfAssign(modp)}); - funcp->addStmtsp(new AstCStmt{fl, EmitCBase::symClassAssign()}); + funcp->addStmtsp(new AstCStmt{fl, EmitCUtil::voidSelfAssign(modp)}); + funcp->addStmtsp(new AstCStmt{fl, EmitCUtil::symClassAssign()}); // Invoke each mtask scheduled to this thread from the thread function for (const ExecMTask* const mtaskp : thread) { diff --git a/src/V3File.cpp b/src/V3File.cpp index a0731db43..5b6f40e98 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -1118,6 +1118,9 @@ private: } }; +string VIdProtect::ifNoProtect(const string& in) VL_MT_SAFE { + return v3Global.opt.protectIds() ? "" : in; +} string VIdProtect::protectIf(const string& old, bool doIt) VL_MT_SAFE { return VIdProtectImp::singleton().protectIf(old, doIt); } diff --git a/src/V3File.h b/src/V3File.h index 746948386..54f532611 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -461,6 +461,8 @@ class VIdProtectImp; class VIdProtect final { public: // METHODS + // Return 'in' only if not protecting (e.g. for emitting a comment) + static string ifNoProtect(const string& in) VL_MT_SAFE; // Rename to a new encoded string (unless earlier passthru'ed) static string protect(const string& old) VL_MT_SAFE { return protectIf(old, true); } static string protectIf(const string& old, bool doIt = true) VL_MT_SAFE; diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 8fa275e2b..35fabd096 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -390,7 +390,7 @@ void transformForks(AstNetlist* const netlistp) { // If we're in a class, add a vlSymsp arg if (m_inClass) { newfuncp->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"}); - newfuncp->argTypes(EmitCBase::symClassVar()); + newfuncp->argTypes(EmitCUtil::symClassVar()); callp->argTypes("vlSymsp"); } // Put the begin's statements in the function, delete the begin diff --git a/src/V3Task.cpp b/src/V3Task.cpp index b9a00f833..df587935e 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -897,7 +897,7 @@ class TaskVisitor final : public VNVisitor { // Convert input/inout DPI arguments to Internal types string args; - args += ("(" + EmitCBase::symClassName() + args += ("(" + EmitCUtil::symClassName() + "*)(__Vscopep->symsp())"); // Upcast w/o overhead AstNode* argnodesp = nullptr; for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { @@ -1284,7 +1284,7 @@ class TaskVisitor final : public VNVisitor { if (!nodep->dpiImport() && !nodep->taskPublic()) { // Need symbol table - cfuncp->argTypes(EmitCBase::symClassVar()); + cfuncp->argTypes(EmitCUtil::symClassVar()); if (cfuncp->name() == "new") { const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);\n"; cfuncp->addInitsp(new AstCStmt{nodep->fileline(), stmt}); diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 1a14843f4..f64ff7347 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -519,8 +519,8 @@ class TraceVisitor final : public VNVisitor { if (isTopFunc) { // Top functions funcp->argTypes("void* voidSelf, " + bufArg); - addInitStr(EmitCBase::voidSelfAssign(m_topModp)); - addInitStr(EmitCBase::symClassAssign()); + addInitStr(EmitCUtil::voidSelfAssign(m_topModp)); + addInitStr(EmitCUtil::symClassAssign()); // Add global activity check to change dump functions if (traceType == VTraceType::CHANGE) { // addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); @@ -732,8 +732,8 @@ class TraceVisitor final : public VNVisitor { cleanupFuncp->isStatic(true); cleanupFuncp->isLoose(true); m_topScopep->addBlocksp(cleanupFuncp); - cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCBase::voidSelfAssign(m_topModp)}); - cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCBase::symClassAssign()}); + cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCUtil::voidSelfAssign(m_topModp)}); + cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCUtil::symClassAssign()}); // Register it m_regFuncp->addStmtsp(new AstText{fl, "tracep->addCleanupCb(", true}); diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index b2f83c7d1..bf4c12554 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -231,7 +231,7 @@ class VariableOrder final { : (sigbytes == 2) ? 3 : (sigbytes == 1) ? 2 : 10; - m_attributes.emplace(varp, VarAttributes{stratum, EmitCBase::isAnonOk(varp)}); + m_attributes.emplace(varp, VarAttributes{stratum, EmitCUtil::isAnonOk(varp)}); } } From a841a962ceb816b6775d298256d17f1558f53835 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 26 Aug 2025 11:24:15 +0100 Subject: [PATCH 231/252] Optimize interfaces in Dfg (#6332) Interfaces that have no corresponding virtual interface references can be safely optimized by Dfg after V3Scope. Enabling it to do so. --- src/V3AstNodeOther.h | 5 ++++- src/V3Dfg.h | 14 ++++++++++---- src/V3DfgOptimizer.cpp | 9 +++++++++ test_regress/t/t_interface_gen7.v | 2 ++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index a2693a6e7..d190df286 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2531,7 +2531,8 @@ public: void classp(AstClass* classp) { m_classp = classp; } }; class AstIface final : public AstNodeModule { - // A module declaration + // An interface declaration + bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface public: AstIface(FileLine* fl, const string& name, const string& libname) : ASTGEN_SUPER_Iface(fl, name, libname) {} @@ -2540,6 +2541,8 @@ public: // get false warnings if we enable this string verilogKwd() const override { return "interface"; } bool timescaleMatters() const override { return false; } + bool hasVirtualRef() const { return m_hasVirtualRef; } + void setHasVirtualRef() { m_hasVirtualRef = true; } }; class AstModule final : public AstNodeModule { // A module declaration diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 0297f975d..c76cc8bf2 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -798,10 +798,16 @@ public: // Returns true if variable can be represented in the graph static bool isSupported(const AstVarScope* vscp) { - // If the variable is not in a regular module, then we do not support it. - // This is especially needed for variabels in interfaces which might be - // referenced via virtual intefaces, which cannot be resovled statically. - if (!VN_IS(vscp->scopep()->modp(), Module)) return false; + AstNodeModule* const modp = vscp->scopep()->modp(); + if (VN_IS(modp, Module)) { + // Regular module supported + } else if (AstIface* const ifacep = VN_CAST(modp, Iface)) { + // Interfaces supported if there are no virtual interfaces for + // them, otherwise they cannot be resovled statically. + if (ifacep->hasVirtualRef()) return false; + } else { + return false; // Anything else (package, class, etc) not supported + } // Check the AstVar return isSupported(vscp->varp()); } diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index b4d0ef642..451a78841 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -321,6 +321,15 @@ class DataflowOptimize final { DataflowOptimize(AstNetlist* netlistp, const string& label) : m_ctx{label} { + + // Mark interfaces that might be referenced by a virtual interface + if (v3Global.hasVirtIfaces()) { + netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) { + if (!nodep->isVirtual()) return; + nodep->ifaceViaCellp()->setHasVirtualRef(); + }); + } + if (!netlistp->topScopep()) { // Pre V3Scope application. Run on each module separately. diff --git a/test_regress/t/t_interface_gen7.v b/test_regress/t/t_interface_gen7.v index 1bf047801..5ffb79c37 100644 --- a/test_regress/t/t_interface_gen7.v +++ b/test_regress/t/t_interface_gen7.v @@ -9,7 +9,9 @@ interface intf #(parameter PARAM = 0) (); + /* verilator lint_off MULTIDRIVEN */ logic val; + /* verilator lint_on MULTIDRIVEN */ function integer func (); return 5; endfunction endinterface From ac21d25d43b780f1ecd54c1abf6435b91b5d5033 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Tue, 26 Aug 2025 16:39:24 +0200 Subject: [PATCH 232/252] Support simple disable within task (#6334) --- src/V3LinkJump.cpp | 6 +++- test_regress/t/t_disable_inside.v | 4 +-- test_regress/t/t_disable_task_simple.py | 18 ++++++++++ test_regress/t/t_disable_task_simple.v | 45 +++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_disable_task_simple.py create mode 100644 test_regress/t/t_disable_task_simple.v diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index fe7e640c8..39244c091 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -187,7 +187,11 @@ class LinkJumpVisitor final : public VNVisitor { FileLine* const fl = nodep->fileline(); const std::string targetName = nodep->targetp()->name(); if (m_ftaskp) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); + if (!m_ftaskp->exists([targetp = nodep->targetp()](const AstNodeBlock* blockp) + -> bool { return blockp == targetp; })) { + // Disabling a fork, which is within the same task, is not a problem + nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); + } } AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); AstClass* const processClassp diff --git a/test_regress/t/t_disable_inside.v b/test_regress/t/t_disable_inside.v index 04b74b3bf..5e4e428e3 100644 --- a/test_regress/t/t_disable_inside.v +++ b/test_regress/t/t_disable_inside.v @@ -10,13 +10,11 @@ module t ( /*AUTOARG*/); fork : fork_blk begin x = 1; - #2; + disable fork_blk; x = 2; end join_none #1; - disable fork_blk; - #2; if (x != 1) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_disable_task_simple.py b/test_regress/t/t_disable_task_simple.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_task_simple.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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_task_simple.v b/test_regress/t/t_disable_task_simple.v new file mode 100644 index 000000000..cde398fbb --- /dev/null +++ b/test_regress/t/t_disable_task_simple.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + int x = 0; + int y = 0; + + task disable_outside_fork; + fork : fork_blk + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk; + endtask + + task disable_inside_fork; + fork : fork_blk + begin + y = 1; + disable fork_blk; + y = 2; + end + join_none + endtask +endclass + +module t ( /*AUTOARG*/); + initial begin + Cls c = new; + c.disable_outside_fork(); + #2; + if (c.x != 1) $stop; + c.disable_inside_fork(); + if (c.y != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 54efa86a6cc8c3c4e5dc9c02875e86cfa057bf4d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 27 Aug 2025 07:48:33 -0400 Subject: [PATCH 233/252] Fix error on too many UDP port inputs --- src/V3Udp.cpp | 6 ++++-- test_regress/t/t_udp_bad_line_inputs.out | 5 +++++ test_regress/t/t_udp_bad_line_inputs.py | 16 ++++++++++++++++ test_regress/t/t_udp_bad_line_inputs.v | 11 +++++++++++ test_regress/t/t_udp_bad_line_outputs.out | 9 +++++++++ test_regress/t/t_udp_bad_line_outputs.py | 16 ++++++++++++++++ test_regress/t/t_udp_bad_line_outputs.v | 17 +++++++++++++++++ 7 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test_regress/t/t_udp_bad_line_inputs.out create mode 100755 test_regress/t/t_udp_bad_line_inputs.py create mode 100644 test_regress/t/t_udp_bad_line_inputs.v create mode 100644 test_regress/t/t_udp_bad_line_outputs.out create mode 100755 test_regress/t/t_udp_bad_line_outputs.py create mode 100644 test_regress/t/t_udp_bad_line_outputs.v diff --git a/src/V3Udp.cpp b/src/V3Udp.cpp index 1570a2614..bc325fc88 100644 --- a/src/V3Udp.cpp +++ b/src/V3Udp.cpp @@ -110,7 +110,6 @@ class UdpVisitor final : public VNVisitor { } AstNode* iNodep = nodep->iFieldsp(); AstNode* oNodep = nodep->oFieldsp(); - uint32_t inputvars = 0; AstSenTree* edgetrigp = nullptr; AstLogAnd* logandp = new AstLogAnd{fl, new AstConst{fl, AstConst::BitTrue{}}, @@ -118,7 +117,6 @@ class UdpVisitor final : public VNVisitor { for (AstVar* itr : m_inputVars) { if (!iNodep) break; - inputvars++; if (AstUdpTableLineVal* linevalp = VN_CAST(iNodep, UdpTableLineVal)) { string valName = linevalp->name(); AstVarRef* const referencep = new AstVarRef{fl, itr, VAccess::READ}; @@ -141,6 +139,10 @@ class UdpVisitor final : public VNVisitor { } iNodep = iNodep->nextp(); } + + uint32_t inputvars = 0; + for (const AstNode* icountp = nodep->iFieldsp(); icountp; icountp = icountp->nextp()) + ++inputvars; if (inputvars != m_inputVars.size()) { nodep->v3error("Incorrect number of input values, expected " << m_inputVars.size() << ", got " << inputvars); diff --git a/test_regress/t/t_udp_bad_line_inputs.out b/test_regress/t/t_udp_bad_line_inputs.out new file mode 100644 index 000000000..be1d64be4 --- /dev/null +++ b/test_regress/t/t_udp_bad_line_inputs.out @@ -0,0 +1,5 @@ +%Error: t/t_udp_bad_line_inputs.v:9:5: Incorrect number of input values, expected 1, got 5 + 9 | ? 1 ? 0 0 : 0; + | ^ + ... 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_udp_bad_line_inputs.py b/test_regress/t/t_udp_bad_line_inputs.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_udp_bad_line_inputs.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_udp_bad_line_inputs.v b/test_regress/t/t_udp_bad_line_inputs.v new file mode 100644 index 000000000..98489c285 --- /dev/null +++ b/test_regress/t/t_udp_bad_line_inputs.v @@ -0,0 +1,11 @@ +// 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 + +primitive udp_0(output o, input i); + table + ? 1 ? 0 0 : 0; // <--- BAD too many inputs + endtable +endprimitive diff --git a/test_regress/t/t_udp_bad_line_outputs.out b/test_regress/t/t_udp_bad_line_outputs.out new file mode 100644 index 000000000..3ff068646 --- /dev/null +++ b/test_regress/t/t_udp_bad_line_outputs.out @@ -0,0 +1,9 @@ +%Error: t/t_udp_bad_line_outputs.v:9:11: syntax error, unexpected UDP table field, expecting : or UDP table line end + 9 | ? : 0 0 : 0; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_udp_bad_line_outputs.v:15:15: syntax error, unexpected UDP table field, expecting UDP table line end + 15 | ? : 0 : 0 0; + | ^ +%Error: Cannot continue + ... This fatal error may be caused by the earlier error(s); resolve those first. diff --git a/test_regress/t/t_udp_bad_line_outputs.py b/test_regress/t/t_udp_bad_line_outputs.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_udp_bad_line_outputs.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_udp_bad_line_outputs.v b/test_regress/t/t_udp_bad_line_outputs.v new file mode 100644 index 000000000..42c2186ba --- /dev/null +++ b/test_regress/t/t_udp_bad_line_outputs.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, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +primitive udp_1(output reg o, input i); + table + ? : 0 0 : 0; // <--- BAD too many recirc + endtable +endprimitive + +primitive udp_2(output reg o, input i); + table + ? : 0 : 0 0; // <--- BAD too many outputs + endtable +endprimitive From e32108713d18dbf63ac99e9e02820c99bf937d97 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 27 Aug 2025 21:25:40 -0400 Subject: [PATCH 234/252] Fix randomize on function-local variable (#6234). --- Changes | 1 + src/V3Randomize.cpp | 21 ++++++++++++-- .../t/t_randomize_inline_funclocal.py | 18 ++++++++++++ test_regress/t/t_randomize_inline_funclocal.v | 28 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_randomize_inline_funclocal.py create mode 100644 test_regress/t/t_randomize_inline_funclocal.v diff --git a/Changes b/Changes index 0013c80da..89dcee298 100644 --- a/Changes +++ b/Changes @@ -75,6 +75,7 @@ Verilator 5.039 devel * Fix `--stats` overridden by skipping identical build (#6220). [Geza Lore] * Fix MODDUP with duplicate packages to take first package (#6222). * Fix replicate with unsigned count but MSB set (#6231) (#6233). [Geza Lore] +* Fix randomize on function-local variable (#6234). * Fix queue typedef with unbounded slice (#6236). * Fix error when force assignment is used with ref function args (#6244). [Ryszard Rozak, Antmicro Ltd.] * Fix write of 0 in '%c' (#6248) (#6249). [Rodrigo Batista de Moraes] diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index f13b07e09..815a598a7 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1373,6 +1373,7 @@ class RandomizeVisitor final : public VNVisitor { AstVar* const stdgenp = new AstVar{modp->fileline(), VVarType::MEMBER, "stdrand", modp->findBasicDType(VBasicDTypeKwd::RANDOM_STDGENERATOR)}; + stdgenp->fileline()->warnOff(V3ErrorCode::IMPURE, true); modp->addStmtsp(stdgenp); m_stdMap.emplace(modp, stdgenp); return stdgenp; @@ -2283,6 +2284,8 @@ class RandomizeVisitor final : public VNVisitor { if (nodep->name() != "randomize") return; if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") { + // Handle std::randomize; create wrapper function that calls basicStdRandomization on + // each varref argument, then transform nodep to call that wrapper AstVar* const stdrand = createStdRandomGenerator(m_modp); AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc( m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep)); @@ -2291,19 +2294,32 @@ class RandomizeVisitor final : public VNVisitor { new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE}, new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}}); + int argn = 0; for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { AstArg* const argp = VN_CAST(pinp, Arg); if (!argp) continue; AstNodeExpr* exprp = argp->exprp(); + AstCMethodHard* const basicMethodp = new AstCMethodHard{ nodep->fileline(), new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE}, "basicStdRandomization"}; + AstVar* const refvarp + = new AstVar{exprp->fileline(), VVarType::MEMBER, + "__Varg"s + std::to_string(++argn), exprp->dtypep()}; + refvarp->direction(VDirection::REF); + refvarp->funcLocal(true); + refvarp->lifetime(VLifetime::AUTOMATIC); + randomizeFuncp->addStmtsp(refvarp); + const size_t width = exprp->width(); - basicMethodp->addPinsp(exprp->unlinkFrBack()); + basicMethodp->addPinsp( + new AstVarRef{exprp->fileline(), refvarp, VAccess::READWRITE}); + basicMethodp->addPinsp( new AstConst{nodep->fileline(), AstConst::Unsized64{}, width}); basicMethodp->dtypeSetBit(); + randomizeFuncp->addStmtsp(new AstAssign{ nodep->fileline(), new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), @@ -2318,7 +2334,8 @@ class RandomizeVisitor final : public VNVisitor { nodep->taskp(randomizeFuncp); nodep->dtypeFrom(randomizeFuncp->dtypep()); if (VN_IS(m_modp, Class)) nodep->classOrPackagep(m_modp); - if (nodep->pinsp()) pushDeletep(nodep->pinsp()->unlinkFrBackWithNext()); + UINFOTREE(9, nodep, "", "std::rnd-call"); + UINFOTREE(9, randomizeFuncp, "", "std::rnd-func"); return; } handleRandomizeArgs(nodep); diff --git a/test_regress/t/t_randomize_inline_funclocal.py b/test_regress/t/t_randomize_inline_funclocal.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_randomize_inline_funclocal.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_inline_funclocal.v b/test_regress/t/t_randomize_inline_funclocal.v new file mode 100644 index 000000000..12940c650 --- /dev/null +++ b/test_regress/t/t_randomize_inline_funclocal.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + function int do_randomize(); + int flocal, success; + success = std::randomize(flocal); + if (success !== 1) $stop; + do_randomize = flocal; + endfunction +endclass + +module t; + int r1, r2, r3; + initial begin + Cls c; + c = new; + r1 = c.do_randomize(); + r2 = c.do_randomize(); + r3 = c.do_randomize(); + $display("%x %x %x", r1, r2, r3); + if (r1 == r2 && r2 == r3) $stop; // Not impossible but 2^63 odds of failure + $finish; + end +endmodule From 409e036eca1a4c352f11c73aaa55c41b5c65502e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 27 Aug 2025 22:28:09 -0400 Subject: [PATCH 235/252] Add error on gate primitive connection width mismatch --- src/V3Width.cpp | 15 +++++++++++---- test_regress/t/t_gate_width_bad.out | 14 ++++++++++++++ test_regress/t/t_gate_width_bad.py | 16 ++++++++++++++++ test_regress/t/t_gate_width_bad.v | 20 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 test_regress/t/t_gate_width_bad.out create mode 100755 test_regress/t/t_gate_width_bad.py create mode 100644 test_regress/t/t_gate_width_bad.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a487ce473..49cf22ba6 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6085,18 +6085,25 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); nodep->dtypeFrom(nodep->rangep()); // Very much like like an pin - const AstNodeDType* const conDTypep = nodep->exprp()->dtypep(); + const AstNodeDType* const pinDTypep = nodep->exprp()->dtypep(); const int numInsts = nodep->rangep()->elementsConst(); const int modwidth = numInsts; - const int conwidth = conDTypep->width(); - if (conwidth == 1 && modwidth > 1) { // Multiple connections - AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric()); + const int pinwidth = pinDTypep->width(); + if (pinwidth == 1 && modwidth > 1) { // Multiple connections + AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, pinDTypep->numeric()); userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p()); AstNode* const newp = new AstReplicate{nodep->fileline(), nodep->exprp()->unlinkFrBack(), static_cast(numInsts)}; nodep->replaceWith(newp); } else { + if (pinwidth != modwidth) { // && is not generic interconnect (when supported) + nodep->exprp()->v3error("Gate primitive connection expects " + << modwidth << " bits " + << ((modwidth != 1) ? "or 1 bit "s : ""s) + << "on the gate port, but the connection generates " + << pinwidth << " bits (IEEE 1800-2023 28.3.6)"); + } // Eliminating so pass down all of vup userIterateAndNext(nodep->exprp(), m_vup); nodep->replaceWith(nodep->exprp()->unlinkFrBack()); diff --git a/test_regress/t/t_gate_width_bad.out b/test_regress/t/t_gate_width_bad.out new file mode 100644 index 000000000..bdc4cfd0b --- /dev/null +++ b/test_regress/t/t_gate_width_bad.out @@ -0,0 +1,14 @@ +%Error: t/t_gate_width_bad.v:14:26: Gate primitive connection expects 1 bits on the gate port, but the connection generates 2 bits (IEEE 1800-2023 28.3.6) + : ... note: In instance 't' + 14 | buf buf2[0:0] (out[1], 2'b01); + | ^~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_gate_width_bad.v:15:28: Gate primitive connection expects 1 bits on the gate port, but the connection generates 2 bits (IEEE 1800-2023 28.3.6) + : ... note: In instance 't' + 15 | buf buf3[0:0] (out[2], in[1:0]); + | ^ +%Error: t/t_gate_width_bad.v:16:28: Gate primitive connection expects 4 bits or 1 bit on the gate port, but the connection generates 2 bits (IEEE 1800-2023 28.3.6) + : ... note: In instance 't' + 16 | buf buf4[3:0] (out[2], in[1:0]); + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_gate_width_bad.py b/test_regress/t/t_gate_width_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_gate_width_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_gate_width_bad.v b/test_regress/t/t_gate_width_bad.v new file mode 100644 index 000000000..2d0d0b424 --- /dev/null +++ b/test_regress/t/t_gate_width_bad.v @@ -0,0 +1,20 @@ +// 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; + + reg [1:0] in; + wire [2:0] out; + + // verilator lint_off WIDTH + buf buf1 (out[0], 1); // <--- BAD wrong connection width + buf buf2[0:0] (out[1], 2'b01); // <--- BAD wrong connection width + buf buf3[0:0] (out[2], in[1:0]); // <--- BAD wrong connection width + buf buf4[3:0] (out[2], in[1:0]); // <--- BAD wrong connection width + + initial $stop; + +endmodule From 1043a6c6bec81a2b441262145c6ee8d4263254b2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 28 Aug 2025 08:48:51 +0100 Subject: [PATCH 236/252] Fix splitting of assignments to SC variables (#6329) (#6336) SC variables can only be assigned whole, so do not split up concatenation assignments in V3FuncOpt. Fixes #6329 --- src/V3FuncOpt.cpp | 2 ++ test_regress/t/t_balance_cats.py | 3 ++- test_regress/t/t_balance_cats_sc.py | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_balance_cats_sc.py diff --git a/src/V3FuncOpt.cpp b/src/V3FuncOpt.cpp index 7b79c7309..67af6296f 100644 --- a/src/V3FuncOpt.cpp +++ b/src/V3FuncOpt.cpp @@ -228,6 +228,8 @@ class FuncOptVisitor final : public VNVisitor { UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Inconsistent assignment"); // Only consider pure assignments. Nodes inserted below are safe. if (!nodep->user1() && (!lhsp->isPure() || !rhsp->isPure())) return false; + // Do not split assignments to SC variables, they cannot be assigned in parts + if (lhsp->exists([](AstVarRef* refp) { return refp->varp()->isSc(); })) return false; // Check for a Sel on the LHS if present, and skip over it uint32_t lsb = 0; if (AstSel* const selp = VN_CAST(lhsp, Sel)) { diff --git a/test_regress/t/t_balance_cats.py b/test_regress/t/t_balance_cats.py index b3cdbade4..b4c1671aa 100755 --- a/test_regress/t/t_balance_cats.py +++ b/test_regress/t/t_balance_cats.py @@ -11,7 +11,8 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=["--stats"]) +test.compile( + verilator_flags2=["--stats", "--build", "--gate-stmts", "10000", "--expand-limit", "128"]) test.file_grep(test.stats, r'Optimizations, FuncOpt concat trees balanced\s+(\d+)', 1) test.file_grep(test.stats, r'Optimizations, FuncOpt concat splits\s+(\d+)', 62) diff --git a/test_regress/t/t_balance_cats_sc.py b/test_regress/t/t_balance_cats_sc.py new file mode 100755 index 000000000..4bf066c4e --- /dev/null +++ b/test_regress/t/t_balance_cats_sc.py @@ -0,0 +1,23 @@ +#!/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.top_filename = "t/t_balance_cats.v" + +test.compile(verilator_flags2=[ + "--stats", "--build", "--gate-stmts", "10000", "--expand-limit", "128", "--sc" +]) + +test.file_grep(test.stats, r'Optimizations, FuncOpt concat trees balanced\s+(\d+)', 1) +test.file_grep(test.stats, r'Optimizations, FuncOpt concat splits\s+(\d+)', 0) + +test.passes() From 8868d459a295b0b72bbc1925b7ae1ccaabca5295 Mon Sep 17 00:00:00 2001 From: Szymon Gizler Date: Thu, 28 Aug 2025 15:03:46 +0200 Subject: [PATCH 237/252] Fix broken support of unassigned virtual interfaces (#6253) (#6338) * Fix broken support of unassigned virtual interfaces Unassigned virtual interface support added by #6245 is broken - PR marks dead module as alive - we can't do that as once a module is dead it needs to remain dead because earlier steps (e.g. port resolution) have already been skipped. This commit handles unassigned virtual interfaces at the beginning of first pass of LinkDot (so it is never marked as dead, and no linking steps are getting skipped). Fixes #6253. * Apply suggestions from code review Co-authored-by: Wilson Snyder * Apply 'make format' * Revert add of redundant iterateChildren() call * Add original test case --------- Co-authored-by: Wilson Snyder Co-authored-by: github action --- src/V3LinkDot.cpp | 63 ++++++++++++++----- .../t/t_interface_virtual_missing_bad.out | 5 ++ .../t/t_interface_virtual_missing_bad.py | 16 +++++ .../t/t_interface_virtual_missing_bad.v | 15 +++++ test_regress/t/t_interface_virtual_unused.v | 2 +- test_regress/t/t_interface_virtual_unused2.py | 18 ++++++ test_regress/t/t_interface_virtual_unused2.v | 27 ++++++++ test_regress/t/t_interface_virtual_unused3.py | 16 +++++ test_regress/t/t_interface_virtual_unused3.v | 20 ++++++ 9 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 test_regress/t/t_interface_virtual_missing_bad.out create mode 100755 test_regress/t/t_interface_virtual_missing_bad.py create mode 100644 test_regress/t/t_interface_virtual_missing_bad.v create mode 100755 test_regress/t/t_interface_virtual_unused2.py create mode 100644 test_regress/t/t_interface_virtual_unused2.v create mode 100755 test_regress/t/t_interface_virtual_unused3.py create mode 100644 test_regress/t/t_interface_virtual_unused3.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index f6e8573fd..4afa67720 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -533,22 +533,9 @@ public: continue; } else if (ifacerefp->ifaceViaCellp()->dead() || !existsNodeSym(ifacerefp->ifaceViaCellp())) { - // virtual interface never assigned any actual interface - ifacerefp->ifacep()->dead(false); - varp->dtypep(ifacerefp->dtypep()); - // Create dummy cell to keep the virtual interface alive - // (later stages assume that non-dead interface has associated cell) - AstCell* const ifacecellp - = new AstCell{ifacerefp->ifacep()->fileline(), - ifacerefp->ifacep()->fileline(), - ifacerefp->ifacep()->name() + "__02E" + varp->name(), - ifacerefp->ifaceName(), - nullptr, - nullptr, - nullptr}; - ifacecellp->modp(ifacerefp->ifacep()); - VSymEnt* const symp = new VSymEnt{&m_syms, ifacecellp}; - ifacerefp->ifacep()->user1p(symp); + UASSERT_OBJ(false, ifacerefp->ifaceViaCellp(), + "Interface referenced by virtual iface is dead. " + "Dead interfaces have required linking steps skipped"); } VSymEnt* const ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp()); VSymEnt* ifOrPortSymp = ifaceSymp; @@ -874,6 +861,14 @@ LinkDotState* LinkDotState::s_errorThisp = nullptr; //====================================================================== class LinkDotFindVisitor final : public VNVisitor { + // NODE STATE + // Cleared on global + // *::user1p() -> See LinkDotState + // *::user2p() -> See LinkDotState + // *::user3() // bool. Node stored in m_virtualIfaces + // *::user4() -> See LinkDotState + const VNUser3InUse m_inuser3; + // STATE LinkDotState* const m_statep; // State to pass between visitors, including symbol table AstNodeModule* m_classOrPackagep = nullptr; // Current package @@ -891,6 +886,8 @@ class LinkDotFindVisitor final : public VNVisitor { int m_modBlockNum = 0; // Begin block number in module, 0=none seen int m_modWithNum = 0; // With block number, 0=none seen int m_modArgNum = 0; // Arg block number for randomize(), 0=none seen + std::vector m_virtIfaces; // interfaces used as virtual, + // needed for handleUnvisitedVirtIfaces() // METHODS void makeImplicitNew(AstClass* nodep) { @@ -986,6 +983,14 @@ class LinkDotFindVisitor final : public VNVisitor { } void visit(AstTypeTable*) override {} // FindVisitor:: void visit(AstConstPool*) override {} // FindVisitor:: + void visit(AstIfaceRefDType* nodep) override { + if (m_statep->forPrimary() && nodep->isVirtual() && nodep->ifacep() + && !nodep->ifacep()->user3()) { + m_virtIfaces.push_back(nodep->ifacep()); + nodep->ifacep()->user3(true); + } + iterateChildren(nodep); + } void visit(AstNodeModule* nodep) override { // FindVisitor:: // Called on top module from Netlist, other modules from the cell creating them, // and packages @@ -1821,12 +1826,36 @@ class LinkDotFindVisitor final : public VNVisitor { void visit(AstNode* nodep) override { iterateChildren(nodep); } // FindVisitor:: + void handleUnvisitedVirtIfaces() { + AstNodeModule* const top = v3Global.rootp()->modulesp(); + m_curSymp = m_statep->getNodeSym(top); + for (AstIface* ifacep : m_virtIfaces) { + if (!ifacep->user4()) { // virtual interface never assigned any actual interface + // Create dummy cell to keep the virtual interface alive + // (later stages assume that non-dead interface has associated cell) + AstCell* const ifacecellp = new AstCell{ifacep->fileline(), + ifacep->fileline(), + "__VdummyInstOf" + ifacep->name(), + ifacep->name(), + nullptr, + nullptr, + nullptr}; + ifacecellp->modp(ifacep); + top->addStmtsp(ifacecellp); + + iterate(ifacecellp); + } + } + } + public: // CONSTRUCTORS LinkDotFindVisitor(AstNetlist* rootp, LinkDotState* statep) : m_statep{statep} { UINFO(4, __FUNCTION__ << ": "); iterate(rootp); + + if (!m_virtIfaces.empty()) handleUnvisitedVirtIfaces(); } ~LinkDotFindVisitor() override = default; }; @@ -2877,7 +2906,7 @@ class LinkDotResolveVisitor final : public VNVisitor { void visit(AstTypeTable*) override {} void visit(AstConstPool*) override {} void visit(AstNodeModule* nodep) override { - if (nodep->dead() || !m_statep->existsNodeSym(nodep)) return; + if (nodep->dead()) return; LINKDOT_VISIT_START(); UINFO(8, indent() << "visit " << nodep); VL_RESTORER(m_genericIfaceModule); diff --git a/test_regress/t/t_interface_virtual_missing_bad.out b/test_regress/t/t_interface_virtual_missing_bad.out new file mode 100644 index 000000000..c8d485e15 --- /dev/null +++ b/test_regress/t/t_interface_virtual_missing_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_interface_virtual_missing_bad.v:9:12: Cannot find file containing interface: 'foo' + 9 | virtual foo vif; + | ^~~ + ... 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_interface_virtual_missing_bad.py b/test_regress/t/t_interface_virtual_missing_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_interface_virtual_missing_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_virtual_missing_bad.v b/test_regress/t/t_interface_virtual_missing_bad.v new file mode 100644 index 000000000..321539f28 --- /dev/null +++ b/test_regress/t/t_interface_virtual_missing_bad.v @@ -0,0 +1,15 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + virtual foo vif; + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_virtual_unused.v b/test_regress/t/t_interface_virtual_unused.v index c0c7d185f..df22a29fd 100644 --- a/test_regress/t/t_interface_virtual_unused.v +++ b/test_regress/t/t_interface_virtual_unused.v @@ -6,7 +6,7 @@ // See also t_interface_virtual.v -interface QBus; +interface QBus(); endinterface module t (/*AUTOARG*/); diff --git a/test_regress/t/t_interface_virtual_unused2.py b/test_regress/t/t_interface_virtual_unused2.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_interface_virtual_unused2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_virtual_unused2.v b/test_regress/t/t_interface_virtual_unused2.v new file mode 100644 index 000000000..81502e650 --- /dev/null +++ b/test_regress/t/t_interface_virtual_unused2.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +interface QBus(input logic k); + logic data; +endinterface + +class cls; + virtual QBus vif1; + + function foo(virtual QBus vif2); + vif2.data = 1; + endfunction +endclass + +module t (/*AUTOARG*/); + + cls bar; + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_virtual_unused3.py b/test_regress/t/t_interface_virtual_unused3.py new file mode 100755 index 000000000..147fe6faf --- /dev/null +++ b/test_regress/t/t_interface_virtual_unused3.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_interface_virtual_unused3.v b/test_regress/t/t_interface_virtual_unused3.v new file mode 100644 index 000000000..fbac6d425 --- /dev/null +++ b/test_regress/t/t_interface_virtual_unused3.v @@ -0,0 +1,20 @@ +// 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 + +interface stream_ifc #( +) ( + input logic clk_i +); +endinterface + +package pkg; + class stream_driver #(); + virtual stream_ifc stream; + endclass +endpackage + +module t; +endmodule From 91d138248d42771315a46f7652f4829b09120a94 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 29 Aug 2025 18:33:14 -0400 Subject: [PATCH 238/252] Tests: Favor all caps for tests' parameters. No test change. --- test_regress/t/t_array_non_blocking_loop.v | 6 +- test_regress/t/t_assert_elab.v | 8 +- test_regress/t/t_bind2.v | 137 +- test_regress/t/t_bitsel_2d_slice.v | 6 +- test_regress/t/t_case_orig.v | 10 +- test_regress/t/t_cover_expr.out | 6 +- test_regress/t/t_cover_expr.v | 6 +- test_regress/t/t_cover_expr_max.out | 6 +- test_regress/t/t_cover_expr_trace.out | 6 +- test_regress/t/t_cover_line_expr.out | 6 +- test_regress/t/t_foreach_const.v | 64 +- test_regress/t/t_func_defaults.v | 4 +- test_regress/t/t_func_while.v | 10 +- test_regress/t/t_gen_if.v | 8 +- test_regress/t/t_interface_array_bad.v | 4 +- .../t/t_interface_generic_modport_param.v | 4 +- test_regress/t/t_interface_localparam.v | 18 +- test_regress/t/t_interface_param1.v | 4 +- test_regress/t/t_interface_param_acc_bits.v | 4 +- test_regress/t/t_lint_width_arraydecl.v | 4 +- test_regress/t/t_math_pow4.v | 20 +- test_regress/t/t_math_swap.v | 32 +- test_regress/t/t_math_yosys.v | 16 +- test_regress/t/t_param_array2.v | 14 +- test_regress/t/t_param_array7.v | 56 +- test_regress/t/t_param_bit_sel.v | 4 +- test_regress/t/t_param_const_part.v | 6 +- test_regress/t/t_param_func.v | 10 +- test_regress/t/t_param_in_func.v | 20 +- test_regress/t/t_param_mem_attr.v | 6 +- test_regress/t/t_param_real2.v | 12 +- test_regress/t/t_param_shift.v | 2 +- test_regress/t/t_param_type6.v | 6 +- test_regress/t/t_param_width_loc_bad.out | 4 +- test_regress/t/t_param_width_loc_bad.v | 4 +- test_regress/t/t_real_param.v | 12 +- test_regress/t/t_select_bound1.v | 4 +- test_regress/t/t_select_bound2.v | 4 +- test_regress/t/t_split_var_0.v | 6 +- test_regress/t/t_split_var_2_trace.out | 2767 +++++++++-------- test_regress/t/t_struct_array.v | 6 +- test_regress/t/t_struct_pat_width.v | 8 +- test_regress/t/t_vams_wreal.v | 4 +- test_regress/t/t_var_init.v | 6 +- test_regress/t/t_vpi_dump.out | 8 +- test_regress/t/t_vpi_dump.v | 6 +- 46 files changed, 1760 insertions(+), 1604 deletions(-) diff --git a/test_regress/t/t_array_non_blocking_loop.v b/test_regress/t/t_array_non_blocking_loop.v index aada1da78..5c1f488df 100644 --- a/test_regress/t/t_array_non_blocking_loop.v +++ b/test_regress/t/t_array_non_blocking_loop.v @@ -7,8 +7,8 @@ interface intf #( - parameter int write_data_width) (); - logic [write_data_width-1:0] writedata; + parameter int WRITE_DATA_WIDTH) (); + logic [WRITE_DATA_WIDTH-1:0] writedata; endinterface module t( /*AUTOARG*/ clk @@ -20,7 +20,7 @@ module t( /*AUTOARG*/ for (num_chunks = 1; num_chunks <= 2; num_chunks++) begin : gen_n localparam int decoded_width = 55 * num_chunks; intf #( - .write_data_width(decoded_width)) + .WRITE_DATA_WIDTH(decoded_width)) the_intf (); always @(posedge clk) begin for (int i = 0; i < decoded_width; i++) diff --git a/test_regress/t/t_assert_elab.v b/test_regress/t/t_assert_elab.v index 15b21a53d..44d6d35de 100644 --- a/test_regress/t/t_assert_elab.v +++ b/test_regress/t/t_assert_elab.v @@ -5,18 +5,18 @@ // SPDX-License-Identifier: CC0-1.0 module t; - localparam str = "string"; + localparam STR = "string"; function logic checkParameter(input logic [8:0] N); $display("x is %d.", N); if (N == 1) return 0; - $fatal(1, "Parameter %d is invalid...%s and %s", N, str, "constant both work"); + $fatal(1, "Parameter %d is invalid...%s and %s", N, STR, "constant both work"); endfunction `ifdef FAILING_ASSERTIONS - localparam x = checkParameter(5); + localparam X = checkParameter(5); `else - localparam x = checkParameter(1); + localparam X = checkParameter(1); `endif initial begin diff --git a/test_regress/t/t_bind2.v b/test_regress/t/t_bind2.v index 273e21b31..c1a7ef5bf 100644 --- a/test_regress/t/t_bind2.v +++ b/test_regress/t/t_bind2.v @@ -7,87 +7,90 @@ // verilator lint_off WIDTH `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); +`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*/ - // Inputs - clk - ); - input clk; - reg [7:0] p1; - reg [7:0] p2; - reg [7:0] p3; +module t ( /*AUTOARG*/ + // Inputs + clk + ); + input clk; + reg [7:0] p1; + reg [7:0] p2; + reg [7:0] p3; - initial begin - p1 = 8'h01; - p2 = 8'h02; - p3 = 8'h03; - end + initial begin + p1 = 8'h01; + p2 = 8'h02; + p3 = 8'h03; + end - parameter int param1 = 8'h11; - parameter int param2 = 8'h12; - parameter int param3 = 8'h13; + parameter int PARAM1 = 8'h11; + parameter int PARAM2 = 8'h12; + parameter int PARAM3 = 8'h13; - targetmod i_targetmod (/*AUTOINST*/ - // Inputs - .clk (clk)); + targetmod i_targetmod ( /*AUTOINST*/ + // Inputs + .clk (clk)); - //Binding i_targetmod to mycheck --instantiates i_mycheck inside i_targetmod - //param1 not over-riden (as mycheck) (=> 0x31) - //param2 explicitly bound to targetmod value (=> 0x22) - //param3 explicitly bound to top value (=> 0x13) - //p1 implictly bound (.*), takes value from targetmod (=> 0x04) - //p2 explictly bound to targetmod (=> 0x05) - //p3 explictly bound to top (=> 0x03) + //Binding i_targetmod to mycheck --instantiates i_mycheck inside i_targetmod + //PARAM1 not over-riden (as mycheck) (=> 0x31) + //PARAM2 explicitly bound to targetmod value (=> 0x22) + //PARAM3 explicitly bound to top value (=> 0x13) + //p1 implictly bound (.*), takes value from targetmod (=> 0x04) + //p2 explictly bound to targetmod (=> 0x05) + //p3 explictly bound to top (=> 0x03) - // Alternative unsupported form is i_targetmod - bind targetmod mycheck - #( - .param2(param2), - .param3(param3) - ) - i_mycheck (.p2(p2), .p3(p3), .*); + // Alternative unsupported form is i_targetmod + bind targetmod mycheck #( + .PARAM2(PARAM2), + .PARAM3(PARAM3) + ) i_mycheck ( + .p2(p2), + .p3(p3), + .*); endmodule -module targetmod (input clk); - reg [7:0] p1; - reg [7:0] p2; - reg [7:0] p3; +module targetmod ( + input clk +); + reg [7:0] p1; + reg [7:0] p2; + reg [7:0] p3; - parameter int param1 = 8'h21; - parameter int param2 = 8'h22; - parameter int param3 = 8'h23; + parameter int PARAM1 = 8'h21; + parameter int PARAM2 = 8'h22; + parameter int PARAM3 = 8'h23; - initial begin - p1 = 8'h04; - p2 = 8'h05; - p3 = 8'h06; - end + initial begin + p1 = 8'h04; + p2 = 8'h05; + p3 = 8'h06; + end endmodule -module mycheck (/*AUTOARG*/ - // Inputs - clk, p1, p2, p3 - ); +module mycheck ( /*AUTOARG*/ + // Inputs + clk, p1, p2, p3 + ); - input clk; - input [7:0] p1; - input [7:0] p2; - input [7:0] p3; + input clk; + input [7:0] p1; + input [7:0] p2; + input [7:0] p3; - parameter int param1 = 8'h31; - parameter int param2 = 8'h32; - parameter int param3 = 8'h33; + parameter int PARAM1 = 8'h31; + parameter int PARAM2 = 8'h32; + parameter int PARAM3 = 8'h33; - always @ (posedge clk) begin - `checkh(param1,8'h31); - `checkh(param2,8'h22); - `checkh(param3,8'h23); - `checkh(p1,8'h04); - `checkh(p2,8'h05); - `checkh(p3,8'h06); - $write("*-* All Finished *-*\n"); - $finish; - end + always @(posedge clk) begin + `checkh(PARAM1, 8'h31); + `checkh(PARAM2, 8'h22); + `checkh(PARAM3, 8'h23); + `checkh(p1, 8'h04); + `checkh(p2, 8'h05); + `checkh(p3, 8'h06); + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_bitsel_2d_slice.v b/test_regress/t/t_bitsel_2d_slice.v index c81e7be24..7aa39c50f 100644 --- a/test_regress/t/t_bitsel_2d_slice.v +++ b/test_regress/t/t_bitsel_2d_slice.v @@ -10,9 +10,9 @@ module t (/*AUTOARG*/ ); input clk; - localparam int width = 8; - typedef logic [width-1:0] [15:0] two_dee_t; - typedef logic[$clog2(width)-1:0] index_t; + localparam int WIDTH = 8; + typedef logic [WIDTH-1:0] [15:0] two_dee_t; + typedef logic[$clog2(WIDTH)-1:0] index_t; two_dee_t the_two_dee; diff --git a/test_regress/t/t_case_orig.v b/test_regress/t/t_case_orig.v index cd0ca49f1..823a97225 100644 --- a/test_regress/t/t_case_orig.v +++ b/test_regress/t/t_case_orig.v @@ -24,7 +24,7 @@ module t (/*AUTOARG*/ wire sigone2 = 1'b1; reg ok; - parameter [1:0] twounkn = 2'b?; // This gets extended to 2'b?? + parameter [1:0] TWOUNKN = 2'b?; // This gets extended to 2'b?? // Large case statements should be well optimizable. reg [2:0] anot; @@ -64,13 +64,13 @@ module t (/*AUTOARG*/ endcase casez (a) default: $stop; - {1'b0, twounkn}: $stop; - {1'b1, twounkn}: ; + {1'b0, TWOUNKN}: $stop; + {1'b1, TWOUNKN}: ; endcase casez (b) default: $stop; - {1'b0, twounkn}: $stop; - {1'b1, twounkn}: ; + {1'b0, TWOUNKN}: $stop; + {1'b1, TWOUNKN}: ; // {1'b0, 2'b??}: $stop; // {1'b1, 2'b??}: ; endcase diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out index 914ec8071..16744d1df 100644 --- a/test_regress/t/t_cover_expr.out +++ b/test_regress/t/t_cover_expr.out @@ -47,10 +47,10 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction - localparam int num_intfs = 4; - intf the_intfs [num_intfs-1:0] (); + localparam int NUM_INTFS = 4; + intf the_intfs [NUM_INTFS-1:0] (); genvar intf_i; - for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + for (intf_i = 0; intf_i < NUM_INTFS; intf_i++) begin always_comb the_intfs[intf_i].t = cyc[intf_i]; end diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v index 526c23cd4..cef3ec0d6 100644 --- a/test_regress/t/t_cover_expr.v +++ b/test_regress/t/t_cover_expr.v @@ -41,10 +41,10 @@ module t (/*AUTOARG*/ return a & b; endfunction - localparam int num_intfs = 4; - intf the_intfs [num_intfs-1:0] (); + localparam int NUM_INTFS = 4; + intf the_intfs [NUM_INTFS-1:0] (); genvar intf_i; - for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + for (intf_i = 0; intf_i < NUM_INTFS; intf_i++) begin always_comb the_intfs[intf_i].t = cyc[intf_i]; end diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out index 47706565e..1dfc2e15a 100644 --- a/test_regress/t/t_cover_expr_max.out +++ b/test_regress/t/t_cover_expr_max.out @@ -47,10 +47,10 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction - localparam int num_intfs = 4; - intf the_intfs [num_intfs-1:0] (); + localparam int NUM_INTFS = 4; + intf the_intfs [NUM_INTFS-1:0] (); genvar intf_i; - for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + for (intf_i = 0; intf_i < NUM_INTFS; intf_i++) begin always_comb the_intfs[intf_i].t = cyc[intf_i]; end diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out index 914ec8071..16744d1df 100644 --- a/test_regress/t/t_cover_expr_trace.out +++ b/test_regress/t/t_cover_expr_trace.out @@ -47,10 +47,10 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction - localparam int num_intfs = 4; - intf the_intfs [num_intfs-1:0] (); + localparam int NUM_INTFS = 4; + intf the_intfs [NUM_INTFS-1:0] (); genvar intf_i; - for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + for (intf_i = 0; intf_i < NUM_INTFS; intf_i++) begin always_comb the_intfs[intf_i].t = cyc[intf_i]; end diff --git a/test_regress/t/t_cover_line_expr.out b/test_regress/t/t_cover_line_expr.out index 4f07df409..5a6e1dfc4 100644 --- a/test_regress/t/t_cover_line_expr.out +++ b/test_regress/t/t_cover_line_expr.out @@ -54,10 +54,10 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction - localparam int num_intfs = 4; - intf the_intfs [num_intfs-1:0] (); + localparam int NUM_INTFS = 4; + intf the_intfs [NUM_INTFS-1:0] (); genvar intf_i; -%000004 for (intf_i = 0; intf_i < num_intfs; intf_i++) begin +%000004 for (intf_i = 0; intf_i < NUM_INTFS; intf_i++) begin -000004 point: comment=block hier=top.t %000004 always_comb the_intfs[intf_i].t = cyc[intf_i]; -000004 point: comment=block hier=top.t diff --git a/test_regress/t/t_foreach_const.v b/test_regress/t/t_foreach_const.v index 08e95917c..9a10b7824 100644 --- a/test_regress/t/t_foreach_const.v +++ b/test_regress/t/t_foreach_const.v @@ -46,7 +46,7 @@ module t (/*AUTOARG*/); if (index_a == 1) return 7; end endfunction - localparam par1 = test1(); + localparam PAR1 = test1(); function [63:0] test2; test2 = 0; @@ -54,7 +54,7 @@ module t (/*AUTOARG*/); test2 = crc(test2, index_a, 0, 0, 0); end endfunction - localparam par2 = test2(); + localparam PAR2 = test2(); function [63:0] test3; test3 = 0; @@ -62,7 +62,7 @@ module t (/*AUTOARG*/); test3 = crc(test3, index_a, index_b, 0, 0); end endfunction - localparam par3 = test3(); + localparam PAR3 = test3(); function [63:0] test4; test4 = 0; @@ -70,7 +70,7 @@ module t (/*AUTOARG*/); test4 = crc(test4, index_a, index_b, index_c, 0); end endfunction - localparam par4 = test4(); + localparam PAR4 = test4(); function [63:0] test5; test5 = 0; @@ -78,7 +78,7 @@ module t (/*AUTOARG*/); test5 = crc(test5, index_a, index_b, index_c, index_d); end endfunction - localparam par5 = test5(); + localparam PAR5 = test5(); function [63:0] test6; // comma syntax @@ -88,7 +88,7 @@ module t (/*AUTOARG*/); test6 = crc(test6, 0, index_b, 0, 0); end endfunction - localparam par6 = test6(); + localparam PAR6 = test6(); function [63:0] test7; test7 = 0; @@ -96,7 +96,7 @@ module t (/*AUTOARG*/); test7 = crc(test7, index_a, 0, 0, 0); end endfunction - localparam par7 = test7(); + localparam PAR7 = test7(); function [63:0] test8; test8 = 0; @@ -105,7 +105,7 @@ module t (/*AUTOARG*/); test8 = test8 + {4'b0,index_a[7:0], 4'h0,index_b[7:0]}; end endfunction - localparam par8 = test8(); + localparam PAR8 = test8(); function [63:0] test9; test9 = 0; @@ -113,7 +113,7 @@ module t (/*AUTOARG*/); test9 = crc(test9, index_a, index_b, index_c, 0); end endfunction - localparam par9 = test9(); + localparam PAR9 = test9(); function [63:0] test10; test10 = 0; @@ -121,7 +121,7 @@ module t (/*AUTOARG*/); test10 = crc(test10, index_a, index_b, index_c, index_d); end endfunction - localparam par10 = test10(); + localparam PAR10 = test10(); function [63:0] test11; automatic mid_t strarray[3]; @@ -135,7 +135,7 @@ module t (/*AUTOARG*/); foreach (strarray[s].mid.subarray[ss]) test11 += strarray[s].mid.subarray[ss]; endfunction - localparam par11 = test11(); + localparam PAR11 = test11(); function [63:0] test12; test12 = 0; @@ -144,7 +144,7 @@ module t (/*AUTOARG*/); break; end endfunction - localparam par12 = test12(); + localparam PAR12 = test12(); function [63:0] test13; test13 = 0; @@ -154,7 +154,7 @@ module t (/*AUTOARG*/); test13 += 100; end endfunction - localparam par13 = test13(); + localparam PAR13 = test13(); function [63:0] test14; test14 = 0; @@ -163,7 +163,7 @@ module t (/*AUTOARG*/); break; end endfunction - localparam par14 = test14(); + localparam PAR14 = test14(); function [63:0] test15; test15 = 0; @@ -175,7 +175,7 @@ module t (/*AUTOARG*/); foreach (twod[i, j]); // Null body check endfunction - localparam par15 = test15(); + localparam PAR15 = test15(); function automatic [63:0] test16; string str1 = ""; @@ -184,27 +184,27 @@ module t (/*AUTOARG*/); test16++; end endfunction - localparam par16 = test16(); + localparam PAR16 = test16(); initial begin - `checkh(par1, 64'h0); - `checkh(par2, 64'h000000c000000000); - `checkh(par3, 64'h000003601e000000); - `checkh(par4, 64'h00003123fc101000); - `checkh(par5, 64'h0030128ab2a8e557); - `checkh(par6, 64'h0000000006000000); - `checkh(par7, 64'h0000009000000000); - `checkh(par8, 64'h000002704b057073); - `checkh(par9, 64'h00002136f9000000); - `checkh(par10, 64'h0020179aa7aa0aaa); - `checkh(par11, 'h19); + `checkh(PAR1, 64'h0); + `checkh(PAR2, 64'h000000c000000000); + `checkh(PAR3, 64'h000003601e000000); + `checkh(PAR4, 64'h00003123fc101000); + `checkh(PAR5, 64'h0030128ab2a8e557); + `checkh(PAR6, 64'h0000000006000000); + `checkh(PAR7, 64'h0000009000000000); + `checkh(PAR8, 64'h000002704b057073); + `checkh(PAR9, 64'h00002136f9000000); + `checkh(PAR10, 64'h0020179aa7aa0aaa); + `checkh(PAR11, 'h19); - `checkh(par12, 1); // 9 - `checkh(par13, 3); // 9, 8, 7 + `checkh(PAR12, 1); // 9 + `checkh(PAR13, 3); // 9, 8, 7 // See https://www.accellera.org/images/eda/sv-bc/10303.html - `checkh(par14, 1); // 3,9 - `checkh(par15, 6); - `checkh(par16, 0); + `checkh(PAR14, 1); // 3,9 + `checkh(PAR15, 6); + `checkh(PAR16, 0); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_func_defaults.v b/test_regress/t/t_func_defaults.v index 6728ce654..797e6c44d 100644 --- a/test_regress/t/t_func_defaults.v +++ b/test_regress/t/t_func_defaults.v @@ -5,7 +5,7 @@ // without warranty, 2015 by Todd Strader. // SPDX-License-Identifier: CC0-1.0 -parameter logic Bar = 1'b1; +parameter logic BAR = 1'b1; function automatic logic calc_y; return 1'b1; @@ -13,7 +13,7 @@ endfunction function automatic logic [1:0] foo ( - input logic x = Bar, + input logic x = BAR, input logic y = calc_y() ); return x + y; diff --git a/test_regress/t/t_func_while.v b/test_regress/t/t_func_while.v index b478bb2fe..0f848e3c2 100644 --- a/test_regress/t/t_func_while.v +++ b/test_regress/t/t_func_while.v @@ -4,10 +4,10 @@ // without warranty, 2018 by Julien Margetts. // SPDX-License-Identifier: CC0-1.0 -module t #(parameter sz = 4096) +module t #(parameter SZ = 4096) ( input wire clk, - output reg [tdw(sz)-1:0] data + output reg [tdw(SZ)-1:0] data ); // bug1330 @@ -19,14 +19,14 @@ module t #(parameter sz = 4096) tmp = tmp>>1; endfunction - function integer tdw(input integer sz); - tdw = clog2(sz); + function integer tdw(input integer SZ); + tdw = clog2(SZ); endfunction integer b; always @(posedge clk) - for (b=0; b> P; - localparam [95: 0] lfsr_p = 1 >> P; + localparam [95: 0] LFSR_P = 1 >> P; initial begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_param_type6.v b/test_regress/t/t_param_type6.v index c423bf589..32c173a15 100644 --- a/test_regress/t/t_param_type6.v +++ b/test_regress/t/t_param_type6.v @@ -23,7 +23,7 @@ module t (/*AUTOARG*/ intf #(.the_type (logic [7:0])) intf_eight(); no_param_intf the_no_param_intf(); - sub #(.type_bits (8)) sub_eight ( + sub #(.TYPE_BITS (8)) sub_eight ( .intf_pin (intf_eight), .no_param_intf_pin (the_no_param_intf) ); @@ -36,7 +36,7 @@ module t (/*AUTOARG*/ endmodule module sub #( - parameter int type_bits + parameter int TYPE_BITS )( intf intf_pin, no_param_intf no_param_intf_pin @@ -45,7 +45,7 @@ module sub #( localparam type intf_type = type(intf_pin.foo); localparam type no_param_intf_type = type(no_param_intf_pin.bar); initial begin - if ($bits(intf_type) != type_bits) $stop(); + if ($bits(intf_type) != TYPE_BITS) $stop(); if ($bits(no_param_intf_type) != 14) $stop(); end diff --git a/test_regress/t/t_param_width_loc_bad.out b/test_regress/t/t_param_width_loc_bad.out index 5d3a3b9de..df2ace02d 100644 --- a/test_regress/t/t_param_width_loc_bad.out +++ b/test_regress/t/t_param_width_loc_bad.out @@ -1,6 +1,6 @@ -%Warning-WIDTHTRUNC: t/t_param_width_loc_bad.v:20:21: Operator VAR 'param' expects 1 bits on the Initial value, but Initial value's CONST '32'h0' generates 32 bits. +%Warning-WIDTHTRUNC: t/t_param_width_loc_bad.v:20:21: Operator VAR 'PARAM' expects 1 bits on the Initial value, but Initial value's CONST '32'h0' generates 32 bits. : ... note: In instance 't.test_i' - 20 | parameter logic param = 1'b0 + 20 | parameter logic PARAM = 1'b0 | ^~~~~ ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. diff --git a/test_regress/t/t_param_width_loc_bad.v b/test_regress/t/t_param_width_loc_bad.v index bb950d98e..1e0b5ad4d 100644 --- a/test_regress/t/t_param_width_loc_bad.v +++ b/test_regress/t/t_param_width_loc_bad.v @@ -7,7 +7,7 @@ module t (/*AUTOARG*/); // bug1624 - test #(.param(32'd0)) test_i(); + test #(.PARAM(32'd0)) test_i(); initial begin $write("*-* All Finished *-*\n"); @@ -17,6 +17,6 @@ endmodule module test #( - parameter logic param = 1'b0 + parameter logic PARAM = 1'b0 ) (); endmodule diff --git a/test_regress/t/t_real_param.v b/test_regress/t/t_real_param.v index 16737272a..fe95bebad 100644 --- a/test_regress/t/t_real_param.v +++ b/test_regress/t/t_real_param.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 module foo - #( parameter real bar = 2.0) + #( parameter real BAR = 2.0) (); endmodule @@ -14,16 +14,16 @@ module t(); generate for (m = 10; m <= 20; m+=10) begin : gen_m for (r = 0; r <= 1; r++) begin : gen_r - localparam real lparam = m + (r + 0.5); + localparam real LPARAM = m + (r + 0.5); initial begin - if (lparam != foo_inst.bar) begin - $display("%m: lparam != foo_inst.bar (%f, %f)", - lparam, foo_inst.bar); + if (LPARAM != foo_inst.BAR) begin + $display("%m: LPARAM != foo_inst.BAR (%f, %f)", + LPARAM, foo_inst.BAR); $stop(); end end - foo #(.bar (lparam)) foo_inst (); + foo #(.BAR (LPARAM)) foo_inst (); end end endgenerate diff --git a/test_regress/t/t_select_bound1.v b/test_regress/t/t_select_bound1.v index 811f2bea6..cd5764bd9 100644 --- a/test_regress/t/t_select_bound1.v +++ b/test_regress/t/t_select_bound1.v @@ -77,11 +77,11 @@ module Test (/*AUTOARG*/ input [2:0] in; output reg [3:0] out; output reg [3:0] mask; - localparam [15:5] p = 11'h1ac; + localparam [15:5] P = 11'h1ac; always @(posedge clk) begin // verilator lint_off WIDTH - out <= p[15 + in -: 5]; + out <= P[15 + in -: 5]; // verilator lint_on WIDTH end always @(posedge clk) begin diff --git a/test_regress/t/t_select_bound2.v b/test_regress/t/t_select_bound2.v index f1a522381..85771588a 100644 --- a/test_regress/t/t_select_bound2.v +++ b/test_regress/t/t_select_bound2.v @@ -77,11 +77,11 @@ module Test (/*AUTOARG*/ input [6:0] in; // Note much wider than any index output reg [3:0] out; output reg [3:0] mask; - localparam [15:5] p = 11'h1ac; + localparam [15:5] P = 11'h1ac; always @(posedge clk) begin // verilator lint_off WIDTH - out <= p[15 + in -: 5]; + out <= P[15 + in -: 5]; // verilator lint_on WIDTH mask[3] <= ((15 + in - 5) < 12); mask[2] <= ((15 + in - 5) < 13); diff --git a/test_regress/t/t_split_var_0.v b/test_regress/t/t_split_var_0.v index 65fd4cde6..6d951d1c1 100644 --- a/test_regress/t/t_split_var_0.v +++ b/test_regress/t/t_split_var_0.v @@ -151,13 +151,13 @@ module barshift_1d_unpacked_struct1 #(parameter DEPTH = 2, localparam WIDTH = 2* typedef struct packed { int data; } data_type; data_type tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/; - localparam [32-WIDTH-1:0] pad = 0; + localparam [32-WIDTH-1:0] PAD = 0; generate for(genvar i = 0; i < DEPTH; ++i) begin always_comb if (shift[i]) begin /*verilator lint_off ALWCOMBORDER*/ - tmp[i+1+OFFSET] = {pad, tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + tmp[i+1+OFFSET] = {PAD, tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; /*verilator lint_on ALWCOMBORDER*/ end else begin @@ -165,7 +165,7 @@ module barshift_1d_unpacked_struct1 #(parameter DEPTH = 2, localparam WIDTH = 2* end end endgenerate - assign tmp[0+OFFSET] = {pad, in}; + assign tmp[0+OFFSET] = {PAD, in}; logic _dummy; always_comb {_dummy, out[WIDTH-1:1], out[0]} = tmp[DEPTH+OFFSET][WIDTH:0]; endmodule diff --git a/test_regress/t/t_split_var_2_trace.out b/test_regress/t/t_split_var_2_trace.out index 7c9b4fd34..f70c24b51 100644 --- a/test_regress/t/t_split_var_2_trace.out +++ b/test_regress/t/t_split_var_2_trace.out @@ -1,89 +1,89 @@ $version Generated by VerilatedVcd $end $timescale 1ps $end $scope module top $end - $var wire 1 x! clk $end + $var wire 1 [" clk $end $scope module t $end - $var wire 1 x! clk $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 32 N" NUMSUB [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 8 y! out[0] [7:0] $end - $var wire 8 z! out[1] [7:0] $end - $var wire 8 {! out[2] [7:0] $end - $var wire 8 |! out[3] [7:0] $end - $var wire 8 }! out[4] [7:0] $end - $var wire 8 ~! out[5] [7:0] $end - $var wire 8 !" out[6] [7:0] $end - $var wire 8 "" out[7] [7:0] $end - $var wire 8 #" out[8] [7:0] $end - $var wire 8 $" through_tmp [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 31 . hash_input_d [31:1] $end - $var wire 31 / hash_input_a [1:31] $end - $var wire 9 %" hash_output_dd [8:0] $end - $var wire 9 &" hash_output_da [8:0] $end - $var wire 9 %" hash_output_ad [8:0] $end - $var wire 9 &" hash_output_aa [8:0] $end - $var wire 64 P" expc [63:0] $end + $var wire 1 [" clk $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 32 ^" NUMSUB [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 8 - out[0] [7:0] $end + $var wire 8 . out[1] [7:0] $end + $var wire 8 / out[2] [7:0] $end + $var wire 8 0 out[3] [7:0] $end + $var wire 8 1 out[4] [7:0] $end + $var wire 8 2 out[5] [7:0] $end + $var wire 8 3 out[6] [7:0] $end + $var wire 8 4 out[7] [7:0] $end + $var wire 8 5 out[8] [7:0] $end + $var wire 8 6 through_tmp [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 31 i hash_input_d [31:1] $end + $var wire 31 j hash_input_a [1:31] $end + $var wire 9 k hash_output_dd [8:0] $end + $var wire 9 l hash_output_da [8:0] $end + $var wire 9 m hash_output_ad [8:0] $end + $var wire 9 n hash_output_aa [8:0] $end + $var wire 64 `" expc [63:0] $end $scope module always_block $end - $var wire 1 0 failed $end - $var wire 9 1 hash_expected [8:0] $end + $var wire 1 o failed $end + $var wire 9 p hash_expected [8:0] $end $scope module unnamedblk1 $end - $var wire 32 4 i [31:0] $end + $var wire 32 q i [31:0] $end $upscope $end $upscope $end $scope module delay0 $end - $var wire 1 x! clk $end - $var wire 1 R" unpack_sig0(10) $end - $var wire 1 S" unpack_sig0(11) $end - $var wire 1 T" unpack_sig0(12) $end - $var wire 1 5 unpack_sig0(13) $end - $var wire 1 6 unpack_sig0(14) $end - $var wire 1 7 unpack_sig0(15) $end - $var wire 1 8 unpack_sig0(16) $end - $var wire 1 9 unpack_sig1(13) $end - $var wire 1 : unpack_sig1(14) $end - $var wire 1 ; unpack_sig1(15) $end - $var wire 1 < unpack_sig1(16) $end - $var wire 1 U" unpack_sig2(10) $end - $var wire 1 V" unpack_sig2(11) $end - $var wire 1 W" unpack_sig2(12) $end - $var wire 1 = unpack_sig2(13) $end - $var wire 1 > unpack_sig2(14) $end - $var wire 1 ? unpack_sig2(15) $end - $var wire 1 @ unpack_sig2(16) $end - $var wire 1 A unpack_sig3(13) $end - $var wire 1 B unpack_sig3(14) $end - $var wire 1 C unpack_sig3(15) $end - $var wire 1 D unpack_sig3(16) $end - $var wire 32 E c [31:0] $end + $var wire 1 [" clk $end + $var wire 1 b" unpack_sig0(10) $end + $var wire 1 c" unpack_sig0(11) $end + $var wire 1 d" unpack_sig0(12) $end + $var wire 1 r unpack_sig0(13) $end + $var wire 1 s unpack_sig0(14) $end + $var wire 1 t unpack_sig0(15) $end + $var wire 1 u unpack_sig0(16) $end + $var wire 1 v unpack_sig1(13) $end + $var wire 1 w unpack_sig1(14) $end + $var wire 1 x unpack_sig1(15) $end + $var wire 1 y unpack_sig1(16) $end + $var wire 1 e" unpack_sig2(10) $end + $var wire 1 f" unpack_sig2(11) $end + $var wire 1 g" unpack_sig2(12) $end + $var wire 1 z unpack_sig2(13) $end + $var wire 1 { unpack_sig2(14) $end + $var wire 1 | unpack_sig2(15) $end + $var wire 1 } unpack_sig2(16) $end + $var wire 1 ~ unpack_sig3(13) $end + $var wire 1 !! unpack_sig3(14) $end + $var wire 1 "! unpack_sig3(15) $end + $var wire 1 #! unpack_sig3(16) $end + $var wire 32 $! c [31:0] $end $upscope $end $scope module i_hash_aa $end - $var wire 31 / i [1:31] $end - $var wire 9 &" o [8:0] $end + $var wire 31 j i [1:31] $end + $var wire 9 n o [8:0] $end $upscope $end $scope module i_hash_ad $end - $var wire 31 . i [1:31] $end - $var wire 9 %" o [8:0] $end + $var wire 31 i i [1:31] $end + $var wire 9 m o [8:0] $end $upscope $end $scope module i_hash_da $end - $var wire 31 / i [31:1] $end - $var wire 9 &" o [8:0] $end + $var wire 31 j i [31:1] $end + $var wire 9 l o [8:0] $end $upscope $end $scope module i_hash_dd $end - $var wire 31 . i [31:1] $end - $var wire 9 %" o [8:0] $end + $var wire 31 i i [31:1] $end + $var wire 9 k o [8:0] $end $upscope $end $scope module i_t_array_rev $end - $var wire 1 x! clk $end - $var wire 32 X" cyc [31:0] $end + $var wire 1 [" clk $end + $var wire 32 h" cyc [31:0] $end $var wire 1 # arrd(0) $end $var wire 1 $ arrd(1) $end $var wire 1 % y0 $end $var wire 1 & y1 $end - $var wire 1 '" localbkw(0) $end - $var wire 1 (" localbkw(1) $end + $var wire 1 %! localbkw(0) $end + $var wire 1 &! localbkw(1) $end $scope module arr_rev_u $end $var wire 1 ' arrbkw[0] $end $var wire 1 ( arrbkw[1] $end @@ -98,312 +98,312 @@ $timescale 1ps $end $var wire 32 , var3 [30:-1] $end $upscope $end $scope module shifter0 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 )" out [7:0] $end - $var wire 32 Y" OFFSET [31:0] $end - $var wire 8 *" tmp(-1) [7:0] $end - $var wire 8 2 tmp(-2) [7:0] $end - $var wire 8 O" tmp(-3) [7:0] $end - $var wire 8 )" tmp(0) [7:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 '! out [7:0] $end + $var wire 32 i" OFFSET [31:0] $end + $var wire 8 (! tmp(-1) [7:0] $end + $var wire 8 )! tmp(-2) [7:0] $end + $var wire 8 _" tmp(-3) [7:0] $end + $var wire 8 '! tmp(0) [7:0] $end $upscope $end $scope module shifter1 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 )" out [7:0] $end - $var wire 32 Y" OFFSET [31:0] $end - $var wire 8 *" tmp(-1) [7:0] $end - $var wire 8 2 tmp(-2) [7:0] $end - $var wire 8 O" tmp(-3) [7:0] $end - $var wire 8 )" tmp(0) [7:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 *! out [7:0] $end + $var wire 32 i" OFFSET [31:0] $end + $var wire 8 +! tmp(-1) [7:0] $end + $var wire 8 )! tmp(-2) [7:0] $end + $var wire 8 _" tmp(-3) [7:0] $end + $var wire 8 *! tmp(0) [7:0] $end $upscope $end $scope module shifter2 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 )" out [7:0] $end - $var wire 32 Z" OFFSET [31:0] $end - $var wire 8 O" tmp(1) [7:0] $end - $var wire 8 2 tmp(2) [7:0] $end - $var wire 8 *" tmp(3) [7:0] $end - $var wire 8 )" tmp(4) [7:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 ,! out [7:0] $end + $var wire 32 j" OFFSET [31:0] $end + $var wire 8 _" tmp(1) [7:0] $end + $var wire 8 -! tmp(2) [7:0] $end + $var wire 8 .! tmp(3) [7:0] $end + $var wire 8 ,! tmp(4) [7:0] $end $upscope $end $scope module shifter3 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 +" out [7:0] $end - $var wire 32 Z" OFFSET [31:0] $end - $var wire 32 L" N [31:0] $end - $var wire 8 O" tmp0(1)(1) [7:0] $end - $var wire 8 O" tmp0(1)(2) [7:0] $end - $var wire 8 O" tmp0(1)(3) [7:0] $end - $var wire 8 2 tmp0(2)(1) [7:0] $end - $var wire 8 2 tmp0(2)(2) [7:0] $end - $var wire 8 2 tmp0(2)(3) [7:0] $end - $var wire 8 *" tmp0(3)(1) [7:0] $end - $var wire 8 *" tmp0(3)(2) [7:0] $end - $var wire 8 *" tmp0(3)(3) [7:0] $end - $var wire 8 )" tmp0(4)(1) [7:0] $end - $var wire 8 )" tmp0(4)(2) [7:0] $end - $var wire 8 )" tmp0(4)(3) [7:0] $end - $var wire 8 g tmp1(1)(1) [7:0] $end - $var wire 8 h tmp1(1)(2) [7:0] $end - $var wire 8 i tmp1(1)(3) [7:0] $end - $var wire 8 d tmp1(2)(1) [7:0] $end - $var wire 8 e tmp1(2)(2) [7:0] $end - $var wire 8 f tmp1(2)(3) [7:0] $end - $var wire 8 a tmp1(3)(1) [7:0] $end - $var wire 8 b tmp1(3)(2) [7:0] $end - $var wire 8 c tmp1(3)(3) [7:0] $end - $var wire 8 ^ tmp1(4)(1) [7:0] $end - $var wire 8 _ tmp1(4)(2) [7:0] $end - $var wire 8 ` tmp1(4)(3) [7:0] $end - $var wire 8 j tmp2[1][1] [7:0] $end - $var wire 8 k tmp2[1][2] [7:0] $end - $var wire 8 l tmp2[1][3] [7:0] $end - $var wire 8 m tmp2[2][1] [7:0] $end - $var wire 8 n tmp2[2][2] [7:0] $end - $var wire 8 o tmp2[2][3] [7:0] $end - $var wire 8 p tmp2[3][1] [7:0] $end - $var wire 8 q tmp2[3][2] [7:0] $end - $var wire 8 r tmp2[3][3] [7:0] $end - $var wire 8 s tmp2[4][1] [7:0] $end - $var wire 8 t tmp2[4][2] [7:0] $end - $var wire 8 u tmp2[4][3] [7:0] $end - $var wire 8 !! tmp3(1)(1) [7:0] $end - $var wire 8 "! tmp3(1)(2) [7:0] $end - $var wire 8 #! tmp3(1)(3) [7:0] $end - $var wire 8 | tmp3(2)(1) [7:0] $end - $var wire 8 } tmp3(2)(2) [7:0] $end - $var wire 8 ~ tmp3(2)(3) [7:0] $end - $var wire 8 y tmp3(3)(1) [7:0] $end - $var wire 8 z tmp3(3)(2) [7:0] $end - $var wire 8 { tmp3(3)(3) [7:0] $end - $var wire 8 v tmp3(4)(1) [7:0] $end - $var wire 8 w tmp3(4)(2) [7:0] $end - $var wire 8 x tmp3(4)(3) [7:0] $end - $var wire 8 $! tmp4(1)(1) [7:0] $end - $var wire 8 %! tmp4(1)(2) [7:0] $end - $var wire 8 &! tmp4(1)(3) [7:0] $end - $var wire 8 '! tmp4(2)(1) [7:0] $end - $var wire 8 (! tmp4(2)(2) [7:0] $end - $var wire 8 )! tmp4(2)(3) [7:0] $end - $var wire 8 *! tmp4(3)(1) [7:0] $end - $var wire 8 +! tmp4(3)(2) [7:0] $end - $var wire 8 ,! tmp4(3)(3) [7:0] $end - $var wire 8 -! tmp4(4)(1) [7:0] $end - $var wire 8 .! tmp4(4)(2) [7:0] $end - $var wire 8 /! tmp4(4)(3) [7:0] $end - $var wire 8 0! tmp5[1][1] [7:0] $end - $var wire 8 1! tmp5[1][2] [7:0] $end - $var wire 8 2! tmp5[1][3] [7:0] $end - $var wire 8 3! tmp5[2][1] [7:0] $end - $var wire 8 4! tmp5[2][2] [7:0] $end - $var wire 8 5! tmp5[2][3] [7:0] $end - $var wire 8 6! tmp5[3][1] [7:0] $end - $var wire 8 7! tmp5[3][2] [7:0] $end - $var wire 8 8! tmp5[3][3] [7:0] $end - $var wire 8 9! tmp5[4][1] [7:0] $end - $var wire 8 :! tmp5[4][2] [7:0] $end - $var wire 8 ;! tmp5[4][3] [7:0] $end - $var wire 8 ! tmp6(1)(3) [7:0] $end - $var wire 8 ?! tmp6(2)(1) [7:0] $end - $var wire 8 @! tmp6(2)(2) [7:0] $end - $var wire 8 A! tmp6(2)(3) [7:0] $end - $var wire 8 B! tmp6(3)(1) [7:0] $end - $var wire 8 C! tmp6(3)(2) [7:0] $end - $var wire 8 D! tmp6(3)(3) [7:0] $end - $var wire 8 E! tmp6(4)(1) [7:0] $end - $var wire 8 F! tmp6(4)(2) [7:0] $end - $var wire 8 G! tmp6(4)(3) [7:0] $end - $var wire 8 Q! tmp7(2)(1) [7:0] $end - $var wire 8 R! tmp7(2)(2) [7:0] $end - $var wire 8 S! tmp7(2)(3) [7:0] $end - $var wire 8 N! tmp7(3)(1) [7:0] $end - $var wire 8 O! tmp7(3)(2) [7:0] $end - $var wire 8 P! tmp7(3)(3) [7:0] $end - $var wire 8 K! tmp7(4)(1) [7:0] $end - $var wire 8 L! tmp7(4)(2) [7:0] $end - $var wire 8 M! tmp7(4)(3) [7:0] $end - $var wire 8 H! tmp7(5)(1) [7:0] $end - $var wire 8 I! tmp7(5)(2) [7:0] $end - $var wire 8 J! tmp7(5)(3) [7:0] $end - $var wire 8 [" tmp8(0)(1) [7:0] $end - $var wire 8 \" tmp8(0)(2) [7:0] $end - $var wire 8 ]" tmp8(0)(3) [7:0] $end - $var wire 8 ^" tmp8(1)(1) [7:0] $end - $var wire 8 _" tmp8(1)(2) [7:0] $end - $var wire 8 `" tmp8(1)(3) [7:0] $end - $var wire 8 ]! tmp8(2)(1) [7:0] $end - $var wire 8 ^! tmp8(2)(2) [7:0] $end - $var wire 8 _! tmp8(2)(3) [7:0] $end - $var wire 8 Z! tmp8(3)(1) [7:0] $end - $var wire 8 [! tmp8(3)(2) [7:0] $end - $var wire 8 \! tmp8(3)(3) [7:0] $end - $var wire 8 W! tmp8(4)(1) [7:0] $end - $var wire 8 X! tmp8(4)(2) [7:0] $end - $var wire 8 Y! tmp8(4)(3) [7:0] $end - $var wire 8 T! tmp8(5)(1) [7:0] $end - $var wire 8 U! tmp8(5)(2) [7:0] $end - $var wire 8 V! tmp8(5)(3) [7:0] $end - $var wire 8 a" tmp8(6)(1) [7:0] $end - $var wire 8 b" tmp8(6)(2) [7:0] $end - $var wire 8 c" tmp8(6)(3) [7:0] $end - $var wire 8 d" tmp8(7)(1) [7:0] $end - $var wire 8 e" tmp8(7)(2) [7:0] $end - $var wire 8 f" tmp8(7)(3) [7:0] $end - $var wire 8 i! tmp9(4)(1) [7:0] $end - $var wire 8 j! tmp9(4)(2) [7:0] $end - $var wire 8 k! tmp9(4)(3) [7:0] $end - $var wire 8 f! tmp9(5)(1) [7:0] $end - $var wire 8 g! tmp9(5)(2) [7:0] $end - $var wire 8 h! tmp9(5)(3) [7:0] $end - $var wire 8 c! tmp9(6)(1) [7:0] $end - $var wire 8 d! tmp9(6)(2) [7:0] $end - $var wire 8 e! tmp9(6)(3) [7:0] $end - $var wire 8 `! tmp9(7)(1) [7:0] $end - $var wire 8 a! tmp9(7)(2) [7:0] $end - $var wire 8 b! tmp9(7)(3) [7:0] $end - $var wire 8 u! tmp10(1)(1) [7:0] $end - $var wire 8 v! tmp10(1)(2) [7:0] $end - $var wire 8 w! tmp10(1)(3) [7:0] $end - $var wire 8 r! tmp10(2)(1) [7:0] $end - $var wire 8 s! tmp10(2)(2) [7:0] $end - $var wire 8 t! tmp10(2)(3) [7:0] $end - $var wire 8 o! tmp10(3)(1) [7:0] $end - $var wire 8 p! tmp10(3)(2) [7:0] $end - $var wire 8 q! tmp10(3)(3) [7:0] $end - $var wire 8 l! tmp10(4)(1) [7:0] $end - $var wire 8 m! tmp10(4)(2) [7:0] $end - $var wire 8 n! tmp10(4)(3) [7:0] $end - $var wire 8 ," tmp12(-1)(1)(1) [7:0] $end - $var wire 8 -" tmp12(-1)(1)(2) [7:0] $end - $var wire 8 ." tmp12(-1)(1)(3) [7:0] $end - $var wire 8 /" tmp12(-1)(2)(1) [7:0] $end - $var wire 8 0" tmp12(-1)(2)(2) [7:0] $end - $var wire 8 1" tmp12(-1)(2)(3) [7:0] $end - $var wire 8 2" tmp12(-1)(3)(1) [7:0] $end - $var wire 8 3" tmp12(-1)(3)(2) [7:0] $end - $var wire 8 4" tmp12(-1)(3)(3) [7:0] $end - $var wire 8 +" tmp12(-1)(4)(1) [7:0] $end - $var wire 8 5" tmp12(-1)(4)(2) [7:0] $end - $var wire 8 6" tmp12(-1)(4)(3) [7:0] $end - $var wire 8 7" tmp12(0)(1)(1) [7:0] $end - $var wire 8 8" tmp12(0)(1)(2) [7:0] $end - $var wire 8 9" tmp12(0)(1)(3) [7:0] $end - $var wire 8 :" tmp12(0)(2)(1) [7:0] $end - $var wire 8 ;" tmp12(0)(2)(2) [7:0] $end - $var wire 8 <" tmp12(0)(2)(3) [7:0] $end - $var wire 8 =" tmp12(0)(3)(1) [7:0] $end - $var wire 8 >" tmp12(0)(3)(2) [7:0] $end - $var wire 8 ?" tmp12(0)(3)(3) [7:0] $end - $var wire 8 @" tmp12(0)(4)(1) [7:0] $end - $var wire 8 A" tmp12(0)(4)(2) [7:0] $end - $var wire 8 B" tmp12(0)(4)(3) [7:0] $end - $var wire 8 g" tmp13(1)(1) [7:0] $end - $var wire 8 h" tmp13(1)(2) [7:0] $end - $var wire 8 i" tmp13(1)(3) [7:0] $end - $var wire 8 j" tmp13(2)(1) [7:0] $end - $var wire 8 k" tmp13(2)(2) [7:0] $end - $var wire 8 l" tmp13(2)(3) [7:0] $end - $var wire 8 m" tmp13(3)(1) [7:0] $end - $var wire 8 n" tmp13(3)(2) [7:0] $end - $var wire 8 o" tmp13(3)(3) [7:0] $end - $var wire 8 p" tmp13(4)(1) [7:0] $end - $var wire 8 q" tmp13(4)(2) [7:0] $end - $var wire 8 r" tmp13(4)(3) [7:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 7 out [7:0] $end + $var wire 32 j" OFFSET [31:0] $end + $var wire 32 \" N [31:0] $end + $var wire 8 _" tmp0(1)(1) [7:0] $end + $var wire 8 _" tmp0(1)(2) [7:0] $end + $var wire 8 _" tmp0(1)(3) [7:0] $end + $var wire 8 )! tmp0(2)(1) [7:0] $end + $var wire 8 )! tmp0(2)(2) [7:0] $end + $var wire 8 )! tmp0(2)(3) [7:0] $end + $var wire 8 /! tmp0(3)(1) [7:0] $end + $var wire 8 0! tmp0(3)(2) [7:0] $end + $var wire 8 1! tmp0(3)(3) [7:0] $end + $var wire 8 2! tmp0(4)(1) [7:0] $end + $var wire 8 3! tmp0(4)(2) [7:0] $end + $var wire 8 4! tmp0(4)(3) [7:0] $end + $var wire 8 5! tmp1(1)(1) [7:0] $end + $var wire 8 6! tmp1(1)(2) [7:0] $end + $var wire 8 7! tmp1(1)(3) [7:0] $end + $var wire 8 8! tmp1(2)(1) [7:0] $end + $var wire 8 9! tmp1(2)(2) [7:0] $end + $var wire 8 :! tmp1(2)(3) [7:0] $end + $var wire 8 ;! tmp1(3)(1) [7:0] $end + $var wire 8 ! tmp1(4)(1) [7:0] $end + $var wire 8 ?! tmp1(4)(2) [7:0] $end + $var wire 8 @! tmp1(4)(3) [7:0] $end + $var wire 8 A! tmp2[1][1] [7:0] $end + $var wire 8 B! tmp2[1][2] [7:0] $end + $var wire 8 C! tmp2[1][3] [7:0] $end + $var wire 8 D! tmp2[2][1] [7:0] $end + $var wire 8 E! tmp2[2][2] [7:0] $end + $var wire 8 F! tmp2[2][3] [7:0] $end + $var wire 8 G! tmp2[3][1] [7:0] $end + $var wire 8 H! tmp2[3][2] [7:0] $end + $var wire 8 I! tmp2[3][3] [7:0] $end + $var wire 8 J! tmp2[4][1] [7:0] $end + $var wire 8 K! tmp2[4][2] [7:0] $end + $var wire 8 L! tmp2[4][3] [7:0] $end + $var wire 8 M! tmp3(1)(1) [7:0] $end + $var wire 8 N! tmp3(1)(2) [7:0] $end + $var wire 8 O! tmp3(1)(3) [7:0] $end + $var wire 8 P! tmp3(2)(1) [7:0] $end + $var wire 8 Q! tmp3(2)(2) [7:0] $end + $var wire 8 R! tmp3(2)(3) [7:0] $end + $var wire 8 S! tmp3(3)(1) [7:0] $end + $var wire 8 T! tmp3(3)(2) [7:0] $end + $var wire 8 U! tmp3(3)(3) [7:0] $end + $var wire 8 V! tmp3(4)(1) [7:0] $end + $var wire 8 W! tmp3(4)(2) [7:0] $end + $var wire 8 X! tmp3(4)(3) [7:0] $end + $var wire 8 Y! tmp4(1)(1) [7:0] $end + $var wire 8 Z! tmp4(1)(2) [7:0] $end + $var wire 8 [! tmp4(1)(3) [7:0] $end + $var wire 8 \! tmp4(2)(1) [7:0] $end + $var wire 8 ]! tmp4(2)(2) [7:0] $end + $var wire 8 ^! tmp4(2)(3) [7:0] $end + $var wire 8 _! tmp4(3)(1) [7:0] $end + $var wire 8 `! tmp4(3)(2) [7:0] $end + $var wire 8 a! tmp4(3)(3) [7:0] $end + $var wire 8 b! tmp4(4)(1) [7:0] $end + $var wire 8 c! tmp4(4)(2) [7:0] $end + $var wire 8 d! tmp4(4)(3) [7:0] $end + $var wire 8 e! tmp5[1][1] [7:0] $end + $var wire 8 f! tmp5[1][2] [7:0] $end + $var wire 8 g! tmp5[1][3] [7:0] $end + $var wire 8 h! tmp5[2][1] [7:0] $end + $var wire 8 i! tmp5[2][2] [7:0] $end + $var wire 8 j! tmp5[2][3] [7:0] $end + $var wire 8 k! tmp5[3][1] [7:0] $end + $var wire 8 l! tmp5[3][2] [7:0] $end + $var wire 8 m! tmp5[3][3] [7:0] $end + $var wire 8 n! tmp5[4][1] [7:0] $end + $var wire 8 o! tmp5[4][2] [7:0] $end + $var wire 8 p! tmp5[4][3] [7:0] $end + $var wire 8 q! tmp6(1)(1) [7:0] $end + $var wire 8 r! tmp6(1)(2) [7:0] $end + $var wire 8 s! tmp6(1)(3) [7:0] $end + $var wire 8 t! tmp6(2)(1) [7:0] $end + $var wire 8 u! tmp6(2)(2) [7:0] $end + $var wire 8 v! tmp6(2)(3) [7:0] $end + $var wire 8 w! tmp6(3)(1) [7:0] $end + $var wire 8 x! tmp6(3)(2) [7:0] $end + $var wire 8 y! tmp6(3)(3) [7:0] $end + $var wire 8 z! tmp6(4)(1) [7:0] $end + $var wire 8 {! tmp6(4)(2) [7:0] $end + $var wire 8 |! tmp6(4)(3) [7:0] $end + $var wire 8 }! tmp7(2)(1) [7:0] $end + $var wire 8 ~! tmp7(2)(2) [7:0] $end + $var wire 8 !" tmp7(2)(3) [7:0] $end + $var wire 8 "" tmp7(3)(1) [7:0] $end + $var wire 8 #" tmp7(3)(2) [7:0] $end + $var wire 8 $" tmp7(3)(3) [7:0] $end + $var wire 8 %" tmp7(4)(1) [7:0] $end + $var wire 8 &" tmp7(4)(2) [7:0] $end + $var wire 8 '" tmp7(4)(3) [7:0] $end + $var wire 8 (" tmp7(5)(1) [7:0] $end + $var wire 8 )" tmp7(5)(2) [7:0] $end + $var wire 8 *" tmp7(5)(3) [7:0] $end + $var wire 8 k" tmp8(0)(1) [7:0] $end + $var wire 8 l" tmp8(0)(2) [7:0] $end + $var wire 8 m" tmp8(0)(3) [7:0] $end + $var wire 8 n" tmp8(1)(1) [7:0] $end + $var wire 8 o" tmp8(1)(2) [7:0] $end + $var wire 8 p" tmp8(1)(3) [7:0] $end + $var wire 8 +" tmp8(2)(1) [7:0] $end + $var wire 8 ," tmp8(2)(2) [7:0] $end + $var wire 8 -" tmp8(2)(3) [7:0] $end + $var wire 8 ." tmp8(3)(1) [7:0] $end + $var wire 8 /" tmp8(3)(2) [7:0] $end + $var wire 8 0" tmp8(3)(3) [7:0] $end + $var wire 8 1" tmp8(4)(1) [7:0] $end + $var wire 8 2" tmp8(4)(2) [7:0] $end + $var wire 8 3" tmp8(4)(3) [7:0] $end + $var wire 8 4" tmp8(5)(1) [7:0] $end + $var wire 8 5" tmp8(5)(2) [7:0] $end + $var wire 8 6" tmp8(5)(3) [7:0] $end + $var wire 8 q" tmp8(6)(1) [7:0] $end + $var wire 8 r" tmp8(6)(2) [7:0] $end + $var wire 8 s" tmp8(6)(3) [7:0] $end + $var wire 8 t" tmp8(7)(1) [7:0] $end + $var wire 8 u" tmp8(7)(2) [7:0] $end + $var wire 8 v" tmp8(7)(3) [7:0] $end + $var wire 8 7" tmp9(4)(1) [7:0] $end + $var wire 8 8" tmp9(4)(2) [7:0] $end + $var wire 8 9" tmp9(4)(3) [7:0] $end + $var wire 8 :" tmp9(5)(1) [7:0] $end + $var wire 8 ;" tmp9(5)(2) [7:0] $end + $var wire 8 <" tmp9(5)(3) [7:0] $end + $var wire 8 =" tmp9(6)(1) [7:0] $end + $var wire 8 >" tmp9(6)(2) [7:0] $end + $var wire 8 ?" tmp9(6)(3) [7:0] $end + $var wire 8 @" tmp9(7)(1) [7:0] $end + $var wire 8 A" tmp9(7)(2) [7:0] $end + $var wire 8 B" tmp9(7)(3) [7:0] $end + $var wire 8 C" tmp10(1)(1) [7:0] $end + $var wire 8 D" tmp10(1)(2) [7:0] $end + $var wire 8 E" tmp10(1)(3) [7:0] $end + $var wire 8 F" tmp10(2)(1) [7:0] $end + $var wire 8 G" tmp10(2)(2) [7:0] $end + $var wire 8 H" tmp10(2)(3) [7:0] $end + $var wire 8 I" tmp10(3)(1) [7:0] $end + $var wire 8 J" tmp10(3)(2) [7:0] $end + $var wire 8 K" tmp10(3)(3) [7:0] $end + $var wire 8 L" tmp10(4)(1) [7:0] $end + $var wire 8 M" tmp10(4)(2) [7:0] $end + $var wire 8 N" tmp10(4)(3) [7:0] $end + $var wire 8 8 tmp12(-1)(1)(1) [7:0] $end + $var wire 8 9 tmp12(-1)(1)(2) [7:0] $end + $var wire 8 : tmp12(-1)(1)(3) [7:0] $end + $var wire 8 ; tmp12(-1)(2)(1) [7:0] $end + $var wire 8 < tmp12(-1)(2)(2) [7:0] $end + $var wire 8 = tmp12(-1)(2)(3) [7:0] $end + $var wire 8 > tmp12(-1)(3)(1) [7:0] $end + $var wire 8 ? tmp12(-1)(3)(2) [7:0] $end + $var wire 8 @ tmp12(-1)(3)(3) [7:0] $end + $var wire 8 7 tmp12(-1)(4)(1) [7:0] $end + $var wire 8 A tmp12(-1)(4)(2) [7:0] $end + $var wire 8 B tmp12(-1)(4)(3) [7:0] $end + $var wire 8 C tmp12(0)(1)(1) [7:0] $end + $var wire 8 D tmp12(0)(1)(2) [7:0] $end + $var wire 8 E tmp12(0)(1)(3) [7:0] $end + $var wire 8 F tmp12(0)(2)(1) [7:0] $end + $var wire 8 G tmp12(0)(2)(2) [7:0] $end + $var wire 8 H tmp12(0)(2)(3) [7:0] $end + $var wire 8 I tmp12(0)(3)(1) [7:0] $end + $var wire 8 J tmp12(0)(3)(2) [7:0] $end + $var wire 8 K tmp12(0)(3)(3) [7:0] $end + $var wire 8 L tmp12(0)(4)(1) [7:0] $end + $var wire 8 M tmp12(0)(4)(2) [7:0] $end + $var wire 8 N tmp12(0)(4)(3) [7:0] $end + $var wire 8 w" tmp13(1)(1) [7:0] $end + $var wire 8 x" tmp13(1)(2) [7:0] $end + $var wire 8 y" tmp13(1)(3) [7:0] $end + $var wire 8 z" tmp13(2)(1) [7:0] $end + $var wire 8 {" tmp13(2)(2) [7:0] $end + $var wire 8 |" tmp13(2)(3) [7:0] $end + $var wire 8 }" tmp13(3)(1) [7:0] $end + $var wire 8 ~" tmp13(3)(2) [7:0] $end + $var wire 8 !# tmp13(3)(3) [7:0] $end + $var wire 8 "# tmp13(4)(1) [7:0] $end + $var wire 8 ## tmp13(4)(2) [7:0] $end + $var wire 8 $# tmp13(4)(3) [7:0] $end $upscope $end $scope module shifter4 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 C" out [7:0] $end - $var wire 32 s" OFFSET [31:0] $end - $var wire 32 t" tmp(2) [31:0] $end - $var wire 32 3 tmp(3) [31:0] $end - $var wire 32 D" tmp(4) [31:0] $end - $var wire 32 E" tmp(5) [31:0] $end - $var wire 24 u" pad [23:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 O" out [7:0] $end + $var wire 32 %# OFFSET [31:0] $end + $var wire 32 &# tmp(2) [31:0] $end + $var wire 32 P" tmp(3) [31:0] $end + $var wire 32 Q" tmp(4) [31:0] $end + $var wire 32 R" tmp(5) [31:0] $end + $var wire 24 '# PAD [23:0] $end $upscope $end $scope module shifter5 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 F" out [7:0] $end - $var wire 32 v" OFFSET [31:0] $end - $var wire 32 G" tmp [31:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 S" out [7:0] $end + $var wire 32 (# OFFSET [31:0] $end + $var wire 32 T" tmp [31:0] $end $upscope $end $scope module shifter6 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 H" out [7:0] $end - $var wire 32 v" OFFSET [31:0] $end - $var wire 32 I" tmp [31:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 U" out [7:0] $end + $var wire 32 (# OFFSET [31:0] $end + $var wire 32 V" tmp [31:0] $end $upscope $end $scope module shifter7 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 )" out [7:0] $end - $var wire 32 K" tmp [31:0] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 W" out [7:0] $end + $var wire 32 X" tmp [31:0] $end $upscope $end $scope module shifter8 $end - $var wire 32 L" DEPTH [31:0] $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 O" in [7:0] $end - $var wire 3 - shift [2:0] $end - $var wire 8 )" out [7:0] $end - $var wire 32 K" tmp [0:31] $end + $var wire 32 \" DEPTH [31:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 _" in [7:0] $end + $var wire 3 h shift [2:0] $end + $var wire 8 Y" out [7:0] $end + $var wire 32 Z" tmp [0:31] $end $upscope $end $scope module though0 $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 )" in [7:0] $end - $var wire 8 $" out [7:0] $end - $var wire 1 N unpack_tmp(0) $end - $var wire 1 O unpack_tmp(1) $end - $var wire 1 P unpack_tmp(2) $end - $var wire 1 Q unpack_tmp(3) $end - $var wire 1 R unpack_tmp(4) $end - $var wire 1 S unpack_tmp(5) $end - $var wire 1 T unpack_tmp(6) $end - $var wire 1 U unpack_tmp(7) $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 O in [7:0] $end + $var wire 8 6 out [7:0] $end + $var wire 1 P unpack_tmp(0) $end + $var wire 1 Q unpack_tmp(1) $end + $var wire 1 R unpack_tmp(2) $end + $var wire 1 S unpack_tmp(3) $end + $var wire 1 T unpack_tmp(4) $end + $var wire 1 U unpack_tmp(5) $end + $var wire 1 V unpack_tmp(6) $end + $var wire 1 W unpack_tmp(7) $end $scope module i_pack2unpack $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 8 )" in [7:0] $end - $var wire 1 F out[0] $end - $var wire 1 G out[1] $end - $var wire 1 H out[2] $end - $var wire 1 I out[3] $end - $var wire 1 J out[4] $end - $var wire 1 K out[5] $end - $var wire 1 L out[6] $end - $var wire 1 M out[7] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 8 O in [7:0] $end + $var wire 1 X out[0] $end + $var wire 1 Y out[1] $end + $var wire 1 Z out[2] $end + $var wire 1 [ out[3] $end + $var wire 1 \ out[4] $end + $var wire 1 ] out[5] $end + $var wire 1 ^ out[6] $end + $var wire 1 _ out[7] $end $upscope $end $scope module i_unpack2pack $end - $var wire 32 M" WIDTH [31:0] $end - $var wire 1 V in[0] $end - $var wire 1 W in[1] $end - $var wire 1 X in[2] $end - $var wire 1 Y in[3] $end - $var wire 1 Z in[4] $end - $var wire 1 [ in[5] $end - $var wire 1 \ in[6] $end - $var wire 1 ] in[7] $end - $var wire 8 $" out [7:0] $end + $var wire 32 ]" WIDTH [31:0] $end + $var wire 1 ` in[0] $end + $var wire 1 a in[1] $end + $var wire 1 b in[2] $end + $var wire 1 c in[3] $end + $var wire 1 d in[4] $end + $var wire 1 e in[5] $end + $var wire 1 f in[6] $end + $var wire 1 g in[7] $end + $var wire 8 6 out [7:0] $end $upscope $end $upscope $end $upscope $end @@ -422,94 +422,94 @@ b00000001001000110100010101100111 ) b00100000000000000000000000000000 * b00000001001000110100000000100111 + b00000000000000000000000000000011 , -b000 - -b0110010000100001010101111001101 . -b0110010000100001010101111001101 / -00 -b000000000 1 +b10001110 - +b10001110 . +b10001110 / +b10001110 0 +b10001110 1 b10001110 2 -b00000000000000000000000010001110 3 -b00000000000000000000000000000000 4 -05 -06 -07 -08 -09 -0: -0; -0< -0= -0> -0? -0@ -0A -0B -0C -0D -b00000000000000000000000000000000 E -0F -1G -1H -1I -0J -0K -0L -1M -1N -0O -0P +b10001110 3 +b10001110 4 +b10001110 5 +b10001110 6 +b10001110 7 +b10001110 8 +b10001110 9 +b10001110 : +b10001110 ; +b10001110 < +b10001110 = +b10001110 > +b10001110 ? +b10001110 @ +b10001110 A +b10001110 B +b10001110 C +b10001110 D +b10001110 E +b10001110 F +b10001110 G +b10001110 H +b10001110 I +b10001110 J +b10001110 K +b10001110 L +b10001110 M +b10001110 N +b10001110 O +1P 0Q -1R -1S +0R +0S 1T -0U -0V -1W -1X +1U +1V +0W +0X 1Y -0Z -0[ +1Z +1[ 0\ -1] -b10001110 ^ -b10001110 _ -b10001110 ` -b10001110 a -b10001110 b -b10001110 c -b10001110 d -b10001110 e -b10001110 f -b10001110 g -b10001110 h -b10001110 i -b10001110 j -b10001110 k -b10001110 l -b10001110 m -b10001110 n -b10001110 o -b10001110 p -b10001110 q -b10001110 r -b10001110 s -b10001110 t -b10001110 u -b10001110 v -b10001110 w -b10001110 x -b10001110 y -b10001110 z -b10001110 { -b10001110 | -b10001110 } -b10001110 ~ -b10001110 !! -b10001110 "! -b10001110 #! -b10001110 $! -b10001110 %! -b10001110 &! +0] +0^ +1_ +0` +1a +1b +1c +0d +0e +0f +1g +b000 h +b0110010000100001010101111001101 i +b0110010000100001010101111001101 j +b100011100 k +b100011100 l +b100011100 m +b100011100 n +0o +b000000000 p +b00000000000000000000000000000000 q +0r +0s +0t +0u +0v +0w +0x +0y +0z +0{ +0| +0} +0~ +0!! +0"! +0#! +b00000000000000000000000000000000 $! +0%! +0&! b10001110 '! b10001110 (! b10001110 )! @@ -591,7 +591,7 @@ b10001110 t! b10001110 u! b10001110 v! b10001110 w! -0x! +b10001110 x! b10001110 y! b10001110 z! b10001110 {! @@ -602,10 +602,10 @@ b10001110 !" b10001110 "" b10001110 #" b10001110 $" -b100011100 %" -b100011100 &" -0'" -0(" +b10001110 %" +b10001110 &" +b10001110 '" +b10001110 (" b10001110 )" b10001110 *" b10001110 +" @@ -633,43 +633,44 @@ b10001110 @" b10001110 A" b10001110 B" b10001110 C" -b00000000000000000000000010001110 D" -b00000000000000000000000010001110 E" +b10001110 D" +b10001110 E" b10001110 F" -b10001110100011101000111010001110 G" +b10001110 G" b10001110 H" -b10001110100011101000111010001110 I" -b10001110100011101000111010001110 K" -b00000000000000000000000000000011 L" -b00000000000000000000000000001000 M" -b00000000000000000000000000001001 N" +b10001110 I" +b10001110 J" +b10001110 K" +b10001110 L" +b10001110 M" +b10001110 N" b10001110 O" -b1000111001000111101000111101000111101000011101000011101000011101 P" -0R" -0S" -0T" -0U" -0V" -0W" -b00000000000000000000000000000000 X" -b11111111111111111111111111111101 Y" -b00000000000000000000000000000001 Z" -b00000000 [" -b00000000 \" -b00000000 ]" -b00000000 ^" -b00000000 _" -b00000000 `" -b00000000 a" -b00000000 b" -b00000000 c" -b00000000 d" -b00000000 e" -b00000000 f" -b00000000 g" -b00000000 h" -b00000000 i" -b00000000 j" +b00000000000000000000000010001110 P" +b00000000000000000000000010001110 Q" +b00000000000000000000000010001110 R" +b10001110 S" +b10001110100011101000111010001110 T" +b10001110 U" +b10001110100011101000111010001110 V" +b10001110 W" +b10001110100011101000111010001110 X" +b10001110 Y" +b10001110100011101000111010001110 Z" +0[" +b00000000000000000000000000000011 \" +b00000000000000000000000000001000 ]" +b00000000000000000000000000001001 ^" +b10001110 _" +b1000111001000111101000111101000111101000011101000011101000011101 `" +0b" +0c" +0d" +0e" +0f" +0g" +b00000000000000000000000000000000 h" +b11111111111111111111111111111101 i" +b00000000000000000000000000000001 j" b00000000 k" b00000000 l" b00000000 m" @@ -678,62 +679,83 @@ b00000000 o" b00000000 p" b00000000 q" b00000000 r" -b00000000000000000000000000000010 s" -b00000000000000000000000010001110 t" -b000000000000000000000000 u" -b11111111111111111111111111111110 v" +b00000000 s" +b00000000 t" +b00000000 u" +b00000000 v" +b00000000 w" +b00000000 x" +b00000000 y" +b00000000 z" +b00000000 {" +b00000000 |" +b00000000 }" +b00000000 ~" +b00000000 !# +b00000000 "# +b00000000 ## +b00000000 $# +b00000000000000000000000000000010 %# +b00000000000000000000000010001110 &# +b000000000000000000000000 '# +b11111111111111111111111111111110 (# #10 -b001 - -b1011001000010000101010111100110 . -b1011001000010000101010111100110 / -b100011100 1 +b01000111 - +b01000111 . +b01000111 / +b01000111 0 +b01000111 1 b01000111 2 -b00000000000000000000000001000111 3 -b00000000000000000000000000001001 4 -15 -19 -1= -1A -b00000000000000000000000000000001 E -1F -0I -1L -0M -0N -1O -0R -1U -1V -0Y -1\ -0] -b01000111 ^ -b01000111 _ -b01000111 ` -b01000111 a -b01000111 b -b01000111 c -b01000111 d -b01000111 e -b01000111 f -b01000111 m -b01000111 n -b01000111 o -b01000111 p -b01000111 q -b01000111 r -b01000111 s -b01000111 t -b01000111 u -b01000111 v -b01000111 w -b01000111 x -b01000111 y -b01000111 z -b01000111 { -b01000111 | -b01000111 } -b01000111 ~ +b01000111 3 +b01000111 4 +b01000111 5 +b01000111 6 +b01000111 7 +b01000111 ; +b01000111 < +b01000111 = +b01000111 > +b01000111 ? +b01000111 @ +b01000111 A +b01000111 B +b01000111 F +b01000111 G +b01000111 H +b01000111 I +b01000111 J +b01000111 K +b01000111 L +b01000111 M +b01000111 N +b01000111 O +0P +1Q +0T +1W +1X +0[ +1^ +0_ +1` +0c +1f +0g +b001 h +b1011001000010000101010111100110 i +b1011001000010000101010111100110 j +b010001110 k +b010001110 l +b010001110 m +b010001110 n +b100011100 p +b00000000000000000000000000001001 q +1r +1v +1z +1~ +b00000000000000000000000000000001 $! +1&! b01000111 '! b01000111 (! b01000111 )! @@ -743,20 +765,20 @@ b01000111 ,! b01000111 -! b01000111 .! b01000111 /! +b01000111 0! +b01000111 1! +b01000111 2! b01000111 3! b01000111 4! -b01000111 5! -b01000111 6! -b01000111 7! b01000111 8! b01000111 9! b01000111 :! b01000111 ;! +b01000111 ! b01000111 ?! b01000111 @! -b01000111 A! -b01000111 B! -b01000111 C! b01000111 D! b01000111 E! b01000111 F! @@ -766,54 +788,52 @@ b01000111 I! b01000111 J! b01000111 K! b01000111 L! -b01000111 M! -b01000111 N! -b01000111 O! b01000111 P! +b01000111 Q! +b01000111 R! +b01000111 S! b01000111 T! b01000111 U! b01000111 V! b01000111 W! b01000111 X! -b01000111 Y! -b01000111 Z! -b01000111 [! b01000111 \! +b01000111 ]! +b01000111 ^! +b01000111 _! b01000111 `! b01000111 a! b01000111 b! b01000111 c! b01000111 d! -b01000111 e! -b01000111 f! -b01000111 g! b01000111 h! +b01000111 i! +b01000111 j! +b01000111 k! b01000111 l! b01000111 m! b01000111 n! b01000111 o! b01000111 p! -b01000111 q! -b01000111 r! -b01000111 s! b01000111 t! -1x! +b01000111 u! +b01000111 v! +b01000111 w! +b01000111 x! b01000111 y! b01000111 z! b01000111 {! b01000111 |! -b01000111 }! -b01000111 ~! -b01000111 !" b01000111 "" b01000111 #" b01000111 $" -b010001110 %" -b010001110 &" -1(" +b01000111 %" +b01000111 &" +b01000111 '" +b01000111 (" b01000111 )" b01000111 *" -b01000111 +" +b01000111 ." b01000111 /" b01000111 0" b01000111 1" @@ -831,149 +851,166 @@ b01000111 ?" b01000111 @" b01000111 A" b01000111 B" -b01000111 C" -b00000000000000000000000001000111 D" -b00000000000000000000000001000111 E" b01000111 F" -b10001110010001110100011101000111 G" +b01000111 G" b01000111 H" -b10001110010001110100011101000111 I" -b10001110010001110100011101000111 K" +b01000111 I" +b01000111 J" +b01000111 K" +b01000111 L" +b01000111 M" +b01000111 N" +b01000111 O" +b00000000000000000000000001000111 P" +b00000000000000000000000001000111 Q" +b00000000000000000000000001000111 R" +b01000111 S" +b10001110010001110100011101000111 T" +b01000111 U" +b10001110010001110100011101000111 V" +b01000111 W" +b10001110010001110100011101000111 X" +b01000111 Y" +b10001110010001110100011101000111 Z" +1[" #15 -0x! +0[" #20 -b010 - -b0101100100001000010101011110011 . -b0101100100001000010101011110011 / -b010001110 1 -b10001110 2 -b00000000000000000000000010001110 3 -16 -1: -1> -1B -b00000000000000000000000000000010 E -0H -1K -0L -1M -1N -0O +b10100011 - +b10100011 . +b10100011 / +b10100011 0 +b10100011 1 +b10100011 2 +b10100011 3 +b10100011 4 +b10100011 5 +b10100011 6 +b10100011 7 +b10001110 ; +b10001110 < +b10001110 = +b10100011 > +b10100011 ? +b10100011 @ +b10100011 A +b10100011 B +b10001110 F +b10001110 G +b10001110 H +b10100011 I +b10100011 J +b10100011 K +b10100011 L +b10100011 M +b10100011 N +b10100011 O 1P -0S -0X -1[ -0\ +0Q +1R +0U +0Z 1] -b10100011 ^ -b10100011 _ -b10100011 ` -b10100011 a -b10100011 b -b10100011 c -b10001110 d -b10001110 e -b10001110 f -b10001110 m -b10001110 n -b10001110 o -b10100011 p -b10100011 q -b10100011 r -b10100011 s -b10100011 t -b10100011 u -b10100011 v -b10100011 w -b10100011 x -b10100011 y -b10100011 z -b10100011 { -b10001110 | -b10001110 } -b10001110 ~ -b10001110 '! -b10001110 (! +0^ +1_ +0b +1e +0f +1g +b010 h +b0101100100001000010101011110011 i +b0101100100001000010101011110011 j +b101000111 k +b101000111 l +b101000111 m +b101000111 n +b010001110 p +1s +1w +1{ +1!! +b00000000000000000000000000000010 $! +b10100011 '! +b10100011 (! b10001110 )! b10100011 *! b10100011 +! b10100011 ,! -b10100011 -! +b10001110 -! b10100011 .! b10100011 /! -b10001110 3! -b10001110 4! -b10001110 5! -b10100011 6! -b10100011 7! -b10100011 8! -b10100011 9! -b10100011 :! +b10100011 0! +b10100011 1! +b10100011 2! +b10100011 3! +b10100011 4! +b10001110 8! +b10001110 9! +b10001110 :! b10100011 ;! -b10001110 ?! -b10001110 @! -b10001110 A! -b10100011 B! -b10100011 C! -b10100011 D! -b10100011 E! -b10100011 F! +b10100011 ! +b10100011 ?! +b10100011 @! +b10001110 D! +b10001110 E! +b10001110 F! b10100011 G! b10100011 H! b10100011 I! b10100011 J! b10100011 K! b10100011 L! -b10100011 M! -b10001110 N! -b10001110 O! b10001110 P! +b10001110 Q! +b10001110 R! +b10100011 S! b10100011 T! b10100011 U! b10100011 V! b10100011 W! b10100011 X! -b10100011 Y! -b10001110 Z! -b10001110 [! b10001110 \! +b10001110 ]! +b10001110 ^! +b10100011 _! b10100011 `! b10100011 a! b10100011 b! b10100011 c! b10100011 d! -b10100011 e! -b10001110 f! -b10001110 g! b10001110 h! +b10001110 i! +b10001110 j! +b10100011 k! b10100011 l! b10100011 m! b10100011 n! b10100011 o! b10100011 p! -b10100011 q! -b10001110 r! -b10001110 s! b10001110 t! -1x! +b10001110 u! +b10001110 v! +b10100011 w! +b10100011 x! b10100011 y! b10100011 z! b10100011 {! b10100011 |! -b10100011 }! -b10100011 ~! -b10100011 !" -b10100011 "" -b10100011 #" -b10100011 $" -b101000111 %" -b101000111 &" +b10001110 "" +b10001110 #" +b10001110 $" +b10100011 %" +b10100011 &" +b10100011 '" +b10100011 (" b10100011 )" b10100011 *" -b10100011 +" +b10001110 ." b10001110 /" b10001110 0" -b10001110 1" +b10100011 1" b10100011 2" b10100011 3" b10100011 4" @@ -988,149 +1025,166 @@ b10100011 ?" b10100011 @" b10100011 A" b10100011 B" -b10100011 C" -b00000000000000000000000010100011 D" -b00000000000000000000000010100011 E" -b10100011 F" -b10001110100011101010001110100011 G" -b10100011 H" -b10001110100011101010001110100011 I" -b10001110100011101010001110100011 K" +b10001110 F" +b10001110 G" +b10001110 H" +b10100011 I" +b10100011 J" +b10100011 K" +b10100011 L" +b10100011 M" +b10100011 N" +b10100011 O" +b00000000000000000000000010001110 P" +b00000000000000000000000010100011 Q" +b00000000000000000000000010100011 R" +b10100011 S" +b10001110100011101010001110100011 T" +b10100011 U" +b10001110100011101010001110100011 V" +b10100011 W" +b10001110100011101010001110100011 X" +b10100011 Y" +b10001110100011101010001110100011 Z" +1[" #25 -0x! +0[" #30 -b011 - -b1010110010000100001010101111001 . -b1010110010000100001010101111001 / -b101000111 1 -b01000111 2 -b00000000000000000000000001000111 3 -17 -1; -1? -1C -b00000000000000000000000000000011 E -0G -1J -0K -1L -1O -0P +b11010001 - +b11010001 . +b11010001 / +b11010001 0 +b11010001 1 +b11010001 2 +b11010001 3 +b11010001 4 +b11010001 5 +b11010001 6 +b11010001 7 +b01000111 ; +b01000111 < +b01000111 = +b11010001 > +b11010001 ? +b11010001 @ +b11010001 A +b11010001 B +b01000111 F +b01000111 G +b01000111 H +b11010001 I +b11010001 J +b11010001 K +b11010001 L +b11010001 M +b11010001 N +b11010001 O 1Q -0T -0W -1Z -0[ +0R +1S +0V +0Y 1\ -b11010001 ^ -b11010001 _ -b11010001 ` -b11010001 a -b11010001 b -b11010001 c -b01000111 d -b01000111 e -b01000111 f -b01000111 m -b01000111 n -b01000111 o -b11010001 p -b11010001 q -b11010001 r -b11010001 s -b11010001 t -b11010001 u -b11010001 v -b11010001 w -b11010001 x -b11010001 y -b11010001 z -b11010001 { -b01000111 | -b01000111 } -b01000111 ~ -b01000111 '! -b01000111 (! +0] +1^ +0a +1d +0e +1f +b011 h +b1010110010000100001010101111001 i +b1010110010000100001010101111001 j +b110100011 k +b110100011 l +b110100011 m +b110100011 n +b101000111 p +1t +1x +1| +1"! +b00000000000000000000000000000011 $! +b11010001 '! +b11010001 (! b01000111 )! b11010001 *! b11010001 +! b11010001 ,! -b11010001 -! +b01000111 -! b11010001 .! b11010001 /! -b01000111 3! -b01000111 4! -b01000111 5! -b11010001 6! -b11010001 7! -b11010001 8! -b11010001 9! -b11010001 :! +b11010001 0! +b11010001 1! +b11010001 2! +b11010001 3! +b11010001 4! +b01000111 8! +b01000111 9! +b01000111 :! b11010001 ;! -b01000111 ?! -b01000111 @! -b01000111 A! -b11010001 B! -b11010001 C! -b11010001 D! -b11010001 E! -b11010001 F! +b11010001 ! +b11010001 ?! +b11010001 @! +b01000111 D! +b01000111 E! +b01000111 F! b11010001 G! b11010001 H! b11010001 I! b11010001 J! b11010001 K! b11010001 L! -b11010001 M! -b01000111 N! -b01000111 O! b01000111 P! +b01000111 Q! +b01000111 R! +b11010001 S! b11010001 T! b11010001 U! b11010001 V! b11010001 W! b11010001 X! -b11010001 Y! -b01000111 Z! -b01000111 [! b01000111 \! +b01000111 ]! +b01000111 ^! +b11010001 _! b11010001 `! b11010001 a! b11010001 b! b11010001 c! b11010001 d! -b11010001 e! -b01000111 f! -b01000111 g! b01000111 h! +b01000111 i! +b01000111 j! +b11010001 k! b11010001 l! b11010001 m! b11010001 n! b11010001 o! b11010001 p! -b11010001 q! -b01000111 r! -b01000111 s! b01000111 t! -1x! +b01000111 u! +b01000111 v! +b11010001 w! +b11010001 x! b11010001 y! b11010001 z! b11010001 {! b11010001 |! -b11010001 }! -b11010001 ~! -b11010001 !" -b11010001 "" -b11010001 #" -b11010001 $" -b110100011 %" -b110100011 &" +b01000111 "" +b01000111 #" +b01000111 $" +b11010001 %" +b11010001 &" +b11010001 '" +b11010001 (" b11010001 )" b11010001 *" -b11010001 +" +b01000111 ." b01000111 /" b01000111 0" -b01000111 1" +b11010001 1" b11010001 2" b11010001 3" b11010001 4" @@ -1145,152 +1199,169 @@ b11010001 ?" b11010001 @" b11010001 A" b11010001 B" -b11010001 C" -b00000000000000000000000011010001 D" -b00000000000000000000000011010001 E" -b11010001 F" -b10001110010001111101000111010001 G" -b11010001 H" -b10001110010001111101000111010001 I" -b10001110010001111101000111010001 K" +b01000111 F" +b01000111 G" +b01000111 H" +b11010001 I" +b11010001 J" +b11010001 K" +b11010001 L" +b11010001 M" +b11010001 N" +b11010001 O" +b00000000000000000000000001000111 P" +b00000000000000000000000011010001 Q" +b00000000000000000000000011010001 R" +b11010001 S" +b10001110010001111101000111010001 T" +b11010001 U" +b10001110010001111101000111010001 V" +b11010001 W" +b10001110010001111101000111010001 X" +b11010001 Y" +b10001110010001111101000111010001 Z" +1[" #35 -0x! +0[" #40 -b100 - -b1101011001000010000101010111100 . -b1101011001000010000101010111100 / -b110100011 1 -b10001110 2 -b00000000000000000000000010001110 3 -18 -1< -1@ -1D -b00000000000000000000000000000100 E -0F -1I -0J -1K -1P -0Q +b11101000 - +b11101000 . +b11101000 / +b11101000 0 +b11101000 1 +b11101000 2 +b11101000 3 +b11101000 4 +b11101000 5 +b11101000 6 +b11101000 7 +b10001110 ; +b10001110 < +b10001110 = +b10001110 > +b10001110 ? +b10001110 @ +b11101000 A +b11101000 B +b10001110 F +b10001110 G +b10001110 H +b10001110 I +b10001110 J +b10001110 K +b11101000 L +b11101000 M +b11101000 N +b11101000 O 1R -0U -0V -1Y -0Z +0S +1T +0W +0X 1[ -b11101000 ^ -b11101000 _ -b11101000 ` -b10001110 a -b10001110 b -b10001110 c -b10001110 d -b10001110 e -b10001110 f -b10001110 m -b10001110 n -b10001110 o -b10001110 p -b10001110 q -b10001110 r -b11101000 s -b11101000 t -b11101000 u -b11101000 v -b11101000 w -b11101000 x -b10001110 y -b10001110 z -b10001110 { -b10001110 | -b10001110 } -b10001110 ~ -b10001110 '! +0\ +1] +0` +1c +0d +1e +b100 h +b1101011001000010000101010111100 i +b1101011001000010000101010111100 j +b011010001 k +b011010001 l +b011010001 m +b011010001 n +b110100011 p +1u +1y +1} +1#! +b00000000000000000000000000000100 $! +b11101000 '! b10001110 (! b10001110 )! -b10001110 *! +b11101000 *! b10001110 +! -b10001110 ,! -b11101000 -! -b11101000 .! -b11101000 /! -b10001110 3! -b10001110 4! -b10001110 5! -b10001110 6! -b10001110 7! +b11101000 ,! +b10001110 -! +b10001110 .! +b10001110 /! +b10001110 0! +b10001110 1! +b11101000 2! +b11101000 3! +b11101000 4! b10001110 8! -b11101000 9! -b11101000 :! -b11101000 ;! -b10001110 ?! -b10001110 @! -b10001110 A! -b10001110 B! -b10001110 C! +b10001110 9! +b10001110 :! +b10001110 ;! +b10001110 ! +b11101000 ?! +b11101000 @! b10001110 D! -b11101000 E! -b11101000 F! -b11101000 G! -b11101000 H! -b11101000 I! +b10001110 E! +b10001110 F! +b10001110 G! +b10001110 H! +b10001110 I! b11101000 J! -b10001110 K! -b10001110 L! -b10001110 M! -b10001110 N! -b10001110 O! +b11101000 K! +b11101000 L! b10001110 P! -b11101000 T! -b11101000 U! +b10001110 Q! +b10001110 R! +b10001110 S! +b10001110 T! +b10001110 U! b11101000 V! -b10001110 W! -b10001110 X! -b10001110 Y! -b10001110 Z! -b10001110 [! +b11101000 W! +b11101000 X! b10001110 \! -b11101000 `! -b11101000 a! +b10001110 ]! +b10001110 ^! +b10001110 _! +b10001110 `! +b10001110 a! b11101000 b! -b10001110 c! -b10001110 d! -b10001110 e! -b10001110 f! -b10001110 g! +b11101000 c! +b11101000 d! b10001110 h! -b11101000 l! -b11101000 m! +b10001110 i! +b10001110 j! +b10001110 k! +b10001110 l! +b10001110 m! b11101000 n! -b10001110 o! -b10001110 p! -b10001110 q! -b10001110 r! -b10001110 s! +b11101000 o! +b11101000 p! b10001110 t! -1x! -b11101000 y! +b10001110 u! +b10001110 v! +b10001110 w! +b10001110 x! +b10001110 y! b11101000 z! b11101000 {! b11101000 |! -b11101000 }! -b11101000 ~! -b11101000 !" -b11101000 "" -b11101000 #" -b11101000 $" -b011010001 %" -b011010001 &" +b10001110 "" +b10001110 #" +b10001110 $" +b10001110 %" +b10001110 &" +b10001110 '" +b11101000 (" b11101000 )" -b10001110 *" -b11101000 +" +b11101000 *" +b10001110 ." b10001110 /" b10001110 0" b10001110 1" b10001110 2" b10001110 3" -b10001110 4" +b11101000 4" b11101000 5" b11101000 6" b10001110 :" @@ -1302,148 +1373,165 @@ b10001110 ?" b11101000 @" b11101000 A" b11101000 B" -b11101000 C" -b00000000000000000000000010001110 D" -b00000000000000000000000011101000 E" -b11101000 F" -b10001110100011101000111011101000 G" -b11101000 H" -b10001110100011101000111011101000 I" -b10001110100011101000111011101000 K" +b10001110 F" +b10001110 G" +b10001110 H" +b10001110 I" +b10001110 J" +b10001110 K" +b11101000 L" +b11101000 M" +b11101000 N" +b11101000 O" +b00000000000000000000000010001110 P" +b00000000000000000000000010001110 Q" +b00000000000000000000000011101000 R" +b11101000 S" +b10001110100011101000111011101000 T" +b11101000 U" +b10001110100011101000111011101000 V" +b11101000 W" +b10001110100011101000111011101000 X" +b11101000 Y" +b10001110100011101000111011101000 Z" +1[" #45 -0x! +0[" #50 -b101 - -b0110101100100001000010101011110 . -b0110101100100001000010101011110 / -b011010001 1 -b01000111 2 -b00000000000000000000000001000111 3 -b00000000000000000000000000000101 E -1H -0I -1J -0M -0N -1Q -0R +b01110100 - +b01110100 . +b01110100 / +b01110100 0 +b01110100 1 +b01110100 2 +b01110100 3 +b01110100 4 +b01110100 5 +b01110100 6 +b01110100 7 +b01000111 ; +b01000111 < +b01000111 = +b01000111 > +b01000111 ? +b01000111 @ +b01110100 A +b01110100 B +b01000111 F +b01000111 G +b01000111 H +b01000111 I +b01000111 J +b01000111 K +b01110100 L +b01110100 M +b01110100 N +b01110100 O +0P 1S -1X -0Y +0T +1U 1Z -0] -b01110100 ^ -b01110100 _ -b01110100 ` -b01000111 a -b01000111 b -b01000111 c -b01000111 d -b01000111 e -b01000111 f -b01000111 m -b01000111 n -b01000111 o -b01000111 p -b01000111 q -b01000111 r -b01110100 s -b01110100 t -b01110100 u -b01110100 v -b01110100 w -b01110100 x -b01000111 y -b01000111 z -b01000111 { -b01000111 | -b01000111 } -b01000111 ~ -b01000111 '! +0[ +1\ +0_ +1b +0c +1d +0g +b101 h +b0110101100100001000010101011110 i +b0110101100100001000010101011110 j +b001101000 k +b001101000 l +b001101000 m +b001101000 n +b011010001 p +b00000000000000000000000000000101 $! +b01110100 '! b01000111 (! b01000111 )! -b01000111 *! +b01110100 *! b01000111 +! -b01000111 ,! -b01110100 -! -b01110100 .! -b01110100 /! -b01000111 3! -b01000111 4! -b01000111 5! -b01000111 6! -b01000111 7! +b01110100 ,! +b01000111 -! +b01000111 .! +b01000111 /! +b01000111 0! +b01000111 1! +b01110100 2! +b01110100 3! +b01110100 4! b01000111 8! -b01110100 9! -b01110100 :! -b01110100 ;! -b01000111 ?! -b01000111 @! -b01000111 A! -b01000111 B! -b01000111 C! +b01000111 9! +b01000111 :! +b01000111 ;! +b01000111 ! +b01110100 ?! +b01110100 @! b01000111 D! -b01110100 E! -b01110100 F! -b01110100 G! -b01110100 H! -b01110100 I! +b01000111 E! +b01000111 F! +b01000111 G! +b01000111 H! +b01000111 I! b01110100 J! -b01000111 K! -b01000111 L! -b01000111 M! -b01000111 N! -b01000111 O! +b01110100 K! +b01110100 L! b01000111 P! -b01110100 T! -b01110100 U! +b01000111 Q! +b01000111 R! +b01000111 S! +b01000111 T! +b01000111 U! b01110100 V! -b01000111 W! -b01000111 X! -b01000111 Y! -b01000111 Z! -b01000111 [! +b01110100 W! +b01110100 X! b01000111 \! -b01110100 `! -b01110100 a! +b01000111 ]! +b01000111 ^! +b01000111 _! +b01000111 `! +b01000111 a! b01110100 b! -b01000111 c! -b01000111 d! -b01000111 e! -b01000111 f! -b01000111 g! +b01110100 c! +b01110100 d! b01000111 h! -b01110100 l! -b01110100 m! +b01000111 i! +b01000111 j! +b01000111 k! +b01000111 l! +b01000111 m! b01110100 n! -b01000111 o! -b01000111 p! -b01000111 q! -b01000111 r! -b01000111 s! +b01110100 o! +b01110100 p! b01000111 t! -1x! -b01110100 y! +b01000111 u! +b01000111 v! +b01000111 w! +b01000111 x! +b01000111 y! b01110100 z! b01110100 {! b01110100 |! -b01110100 }! -b01110100 ~! -b01110100 !" -b01110100 "" -b01110100 #" -b01110100 $" -b001101000 %" -b001101000 &" +b01000111 "" +b01000111 #" +b01000111 $" +b01000111 %" +b01000111 &" +b01000111 '" +b01110100 (" b01110100 )" -b01000111 *" -b01110100 +" +b01110100 *" +b01000111 ." b01000111 /" b01000111 0" b01000111 1" b01000111 2" b01000111 3" -b01000111 4" +b01110100 4" b01110100 5" b01110100 6" b01000111 :" @@ -1455,148 +1543,165 @@ b01000111 ?" b01110100 @" b01110100 A" b01110100 B" -b01110100 C" -b00000000000000000000000001000111 D" -b00000000000000000000000001110100 E" -b01110100 F" -b10001110010001110100011101110100 G" -b01110100 H" -b10001110010001110100011101110100 I" -b10001110010001110100011101110100 K" +b01000111 F" +b01000111 G" +b01000111 H" +b01000111 I" +b01000111 J" +b01000111 K" +b01110100 L" +b01110100 M" +b01110100 N" +b01110100 O" +b00000000000000000000000001000111 P" +b00000000000000000000000001000111 Q" +b00000000000000000000000001110100 R" +b01110100 S" +b10001110010001110100011101110100 T" +b01110100 U" +b10001110010001110100011101110100 V" +b01110100 W" +b10001110010001110100011101110100 X" +b01110100 Y" +b10001110010001110100011101110100 Z" +1[" #55 -0x! +0[" #60 -b110 - -b0011010110010000100001010101111 . -b0011010110010000100001010101111 / -b001101000 1 -b10001110 2 -b00000000000000000000000010001110 3 -b00000000000000000000000000000110 E -1G -0H -1I -0L -0O -1R -0S +b00111010 - +b00111010 . +b00111010 / +b00111010 0 +b00111010 1 +b00111010 2 +b00111010 3 +b00111010 4 +b00111010 5 +b00111010 6 +b00111010 7 +b10001110 ; +b10001110 < +b10001110 = +b10100011 > +b10100011 ? +b10100011 @ +b00111010 A +b00111010 B +b10001110 F +b10001110 G +b10001110 H +b10100011 I +b10100011 J +b10100011 K +b00111010 L +b00111010 M +b00111010 N +b00111010 O +0Q 1T -1W -0X +0U +1V 1Y -0\ -b00111010 ^ -b00111010 _ -b00111010 ` -b10100011 a -b10100011 b -b10100011 c -b10001110 d -b10001110 e -b10001110 f -b10001110 m -b10001110 n -b10001110 o -b10100011 p -b10100011 q -b10100011 r -b00111010 s -b00111010 t -b00111010 u -b00111010 v -b00111010 w -b00111010 x -b10100011 y -b10100011 z -b10100011 { -b10001110 | -b10001110 } -b10001110 ~ -b10001110 '! -b10001110 (! +0Z +1[ +0^ +1a +0b +1c +0f +b110 h +b0011010110010000100001010101111 i +b0011010110010000100001010101111 j +b100110100 k +b100110100 l +b100110100 m +b100110100 n +b001101000 p +b00000000000000000000000000000110 $! +b00111010 '! +b10100011 (! b10001110 )! -b10100011 *! +b00111010 *! b10100011 +! -b10100011 ,! -b00111010 -! -b00111010 .! -b00111010 /! -b10001110 3! -b10001110 4! -b10001110 5! -b10100011 6! -b10100011 7! -b10100011 8! -b00111010 9! -b00111010 :! -b00111010 ;! -b10001110 ?! -b10001110 @! -b10001110 A! -b10100011 B! -b10100011 C! -b10100011 D! -b00111010 E! -b00111010 F! -b00111010 G! -b00111010 H! -b00111010 I! +b00111010 ,! +b10001110 -! +b10100011 .! +b10100011 /! +b10100011 0! +b10100011 1! +b00111010 2! +b00111010 3! +b00111010 4! +b10001110 8! +b10001110 9! +b10001110 :! +b10100011 ;! +b10100011 ! +b00111010 ?! +b00111010 @! +b10001110 D! +b10001110 E! +b10001110 F! +b10100011 G! +b10100011 H! +b10100011 I! b00111010 J! -b10100011 K! -b10100011 L! -b10100011 M! -b10001110 N! -b10001110 O! +b00111010 K! +b00111010 L! b10001110 P! -b00111010 T! -b00111010 U! +b10001110 Q! +b10001110 R! +b10100011 S! +b10100011 T! +b10100011 U! b00111010 V! -b10100011 W! -b10100011 X! -b10100011 Y! -b10001110 Z! -b10001110 [! +b00111010 W! +b00111010 X! b10001110 \! -b00111010 `! -b00111010 a! +b10001110 ]! +b10001110 ^! +b10100011 _! +b10100011 `! +b10100011 a! b00111010 b! -b10100011 c! -b10100011 d! -b10100011 e! -b10001110 f! -b10001110 g! +b00111010 c! +b00111010 d! b10001110 h! -b00111010 l! -b00111010 m! +b10001110 i! +b10001110 j! +b10100011 k! +b10100011 l! +b10100011 m! b00111010 n! -b10100011 o! -b10100011 p! -b10100011 q! -b10001110 r! -b10001110 s! +b00111010 o! +b00111010 p! b10001110 t! -1x! -b00111010 y! +b10001110 u! +b10001110 v! +b10100011 w! +b10100011 x! +b10100011 y! b00111010 z! b00111010 {! b00111010 |! -b00111010 }! -b00111010 ~! -b00111010 !" -b00111010 "" -b00111010 #" -b00111010 $" -b100110100 %" -b100110100 &" +b10001110 "" +b10001110 #" +b10001110 $" +b10100011 %" +b10100011 &" +b10100011 '" +b00111010 (" b00111010 )" -b10100011 *" -b00111010 +" +b00111010 *" +b10001110 ." b10001110 /" b10001110 0" -b10001110 1" +b10100011 1" b10100011 2" b10100011 3" -b10100011 4" +b00111010 4" b00111010 5" b00111010 6" b10001110 :" @@ -1608,148 +1713,165 @@ b10100011 ?" b00111010 @" b00111010 A" b00111010 B" -b00111010 C" -b00000000000000000000000010100011 D" -b00000000000000000000000000111010 E" -b00111010 F" -b10001110100011101010001100111010 G" -b00111010 H" -b10001110100011101010001100111010 I" -b10001110100011101010001100111010 K" +b10001110 F" +b10001110 G" +b10001110 H" +b10100011 I" +b10100011 J" +b10100011 K" +b00111010 L" +b00111010 M" +b00111010 N" +b00111010 O" +b00000000000000000000000010001110 P" +b00000000000000000000000010100011 Q" +b00000000000000000000000000111010 R" +b00111010 S" +b10001110100011101010001100111010 T" +b00111010 U" +b10001110100011101010001100111010 V" +b00111010 W" +b10001110100011101010001100111010 X" +b00111010 Y" +b10001110100011101010001100111010 Z" +1[" #65 -0x! +0[" #70 -b111 - -b1001101011001000010000101010111 . -b1001101011001000010000101010111 / -b100110100 1 -b01000111 2 -b00000000000000000000000001000111 3 -b00000000000000000000000000000111 E -1F -0G -1H -0K -0P -1S -0T +b00011101 - +b00011101 . +b00011101 / +b00011101 0 +b00011101 1 +b00011101 2 +b00011101 3 +b00011101 4 +b00011101 5 +b00011101 6 +b00011101 7 +b01000111 ; +b01000111 < +b01000111 = +b11010001 > +b11010001 ? +b11010001 @ +b00011101 A +b00011101 B +b01000111 F +b01000111 G +b01000111 H +b11010001 I +b11010001 J +b11010001 K +b00011101 L +b00011101 M +b00011101 N +b00011101 O +0R 1U -1V -0W +0V +1W 1X -0[ -b00011101 ^ -b00011101 _ -b00011101 ` -b11010001 a -b11010001 b -b11010001 c -b01000111 d -b01000111 e -b01000111 f -b01000111 m -b01000111 n -b01000111 o -b11010001 p -b11010001 q -b11010001 r -b00011101 s -b00011101 t -b00011101 u -b00011101 v -b00011101 w -b00011101 x -b11010001 y -b11010001 z -b11010001 { -b01000111 | -b01000111 } -b01000111 ~ -b01000111 '! -b01000111 (! +0Y +1Z +0] +1` +0a +1b +0e +b111 h +b1001101011001000010000101010111 i +b1001101011001000010000101010111 j +b010011010 k +b010011010 l +b010011010 m +b010011010 n +b100110100 p +b00000000000000000000000000000111 $! +b00011101 '! +b11010001 (! b01000111 )! -b11010001 *! +b00011101 *! b11010001 +! -b11010001 ,! -b00011101 -! -b00011101 .! -b00011101 /! -b01000111 3! -b01000111 4! -b01000111 5! -b11010001 6! -b11010001 7! -b11010001 8! -b00011101 9! -b00011101 :! -b00011101 ;! -b01000111 ?! -b01000111 @! -b01000111 A! -b11010001 B! -b11010001 C! -b11010001 D! -b00011101 E! -b00011101 F! -b00011101 G! -b00011101 H! -b00011101 I! +b00011101 ,! +b01000111 -! +b11010001 .! +b11010001 /! +b11010001 0! +b11010001 1! +b00011101 2! +b00011101 3! +b00011101 4! +b01000111 8! +b01000111 9! +b01000111 :! +b11010001 ;! +b11010001 ! +b00011101 ?! +b00011101 @! +b01000111 D! +b01000111 E! +b01000111 F! +b11010001 G! +b11010001 H! +b11010001 I! b00011101 J! -b11010001 K! -b11010001 L! -b11010001 M! -b01000111 N! -b01000111 O! +b00011101 K! +b00011101 L! b01000111 P! -b00011101 T! -b00011101 U! +b01000111 Q! +b01000111 R! +b11010001 S! +b11010001 T! +b11010001 U! b00011101 V! -b11010001 W! -b11010001 X! -b11010001 Y! -b01000111 Z! -b01000111 [! +b00011101 W! +b00011101 X! b01000111 \! -b00011101 `! -b00011101 a! +b01000111 ]! +b01000111 ^! +b11010001 _! +b11010001 `! +b11010001 a! b00011101 b! -b11010001 c! -b11010001 d! -b11010001 e! -b01000111 f! -b01000111 g! +b00011101 c! +b00011101 d! b01000111 h! -b00011101 l! -b00011101 m! +b01000111 i! +b01000111 j! +b11010001 k! +b11010001 l! +b11010001 m! b00011101 n! -b11010001 o! -b11010001 p! -b11010001 q! -b01000111 r! -b01000111 s! +b00011101 o! +b00011101 p! b01000111 t! -1x! -b00011101 y! +b01000111 u! +b01000111 v! +b11010001 w! +b11010001 x! +b11010001 y! b00011101 z! b00011101 {! b00011101 |! -b00011101 }! -b00011101 ~! -b00011101 !" -b00011101 "" -b00011101 #" -b00011101 $" -b010011010 %" -b010011010 &" +b01000111 "" +b01000111 #" +b01000111 $" +b11010001 %" +b11010001 &" +b11010001 '" +b00011101 (" b00011101 )" -b11010001 *" -b00011101 +" +b00011101 *" +b01000111 ." b01000111 /" b01000111 0" -b01000111 1" +b11010001 1" b11010001 2" b11010001 3" -b11010001 4" +b00011101 4" b00011101 5" b00011101 6" b01000111 :" @@ -1761,63 +1883,81 @@ b11010001 ?" b00011101 @" b00011101 A" b00011101 B" -b00011101 C" -b00000000000000000000000011010001 D" -b00000000000000000000000000011101 E" -b00011101 F" -b10001110010001111101000100011101 G" -b00011101 H" -b10001110010001111101000100011101 I" -b10001110010001111101000100011101 K" +b01000111 F" +b01000111 G" +b01000111 H" +b11010001 I" +b11010001 J" +b11010001 K" +b00011101 L" +b00011101 M" +b00011101 N" +b00011101 O" +b00000000000000000000000001000111 P" +b00000000000000000000000011010001 Q" +b00000000000000000000000000011101 R" +b00011101 S" +b10001110010001111101000100011101 T" +b00011101 U" +b10001110010001111101000100011101 V" +b00011101 W" +b10001110010001111101000100011101 X" +b00011101 Y" +b10001110010001111101000100011101 Z" +1[" #75 -0x! +0[" #80 -b000 - -b1100110101100100001000010101011 . -b1100110101100100001000010101011 / -b010011010 1 +b10001110 - +b10001110 . +b10001110 / +b10001110 0 +b10001110 1 b10001110 2 -b00000000000000000000000010001110 3 -b00000000000000000000000000001000 E -0F -1G -0J -1M -1N -0Q -1T -0U -0V -1W -0Z -1] -b10001110 ^ -b10001110 _ -b10001110 ` -b10001110 a -b10001110 b -b10001110 c -b10001110 d -b10001110 e -b10001110 f -b10001110 m -b10001110 n -b10001110 o -b10001110 p -b10001110 q -b10001110 r -b10001110 s -b10001110 t -b10001110 u -b10001110 v -b10001110 w -b10001110 x -b10001110 y -b10001110 z -b10001110 { -b10001110 | -b10001110 } -b10001110 ~ +b10001110 3 +b10001110 4 +b10001110 5 +b10001110 6 +b10001110 7 +b10001110 ; +b10001110 < +b10001110 = +b10001110 > +b10001110 ? +b10001110 @ +b10001110 A +b10001110 B +b10001110 F +b10001110 G +b10001110 H +b10001110 I +b10001110 J +b10001110 K +b10001110 L +b10001110 M +b10001110 N +b10001110 O +1P +0S +1V +0W +0X +1Y +0\ +1_ +0` +1a +0d +1g +b000 h +b1100110101100100001000010101011 i +b1100110101100100001000010101011 j +b001001101 k +b001001101 l +b001001101 m +b001001101 n +b010011010 p +b00000000000000000000000000001000 $! b10001110 '! b10001110 (! b10001110 )! @@ -1827,20 +1967,20 @@ b10001110 ,! b10001110 -! b10001110 .! b10001110 /! +b10001110 0! +b10001110 1! +b10001110 2! b10001110 3! b10001110 4! -b10001110 5! -b10001110 6! -b10001110 7! b10001110 8! b10001110 9! b10001110 :! b10001110 ;! +b10001110 ! b10001110 ?! b10001110 @! -b10001110 A! -b10001110 B! -b10001110 C! b10001110 D! b10001110 E! b10001110 F! @@ -1850,53 +1990,52 @@ b10001110 I! b10001110 J! b10001110 K! b10001110 L! -b10001110 M! -b10001110 N! -b10001110 O! b10001110 P! +b10001110 Q! +b10001110 R! +b10001110 S! b10001110 T! b10001110 U! b10001110 V! b10001110 W! b10001110 X! -b10001110 Y! -b10001110 Z! -b10001110 [! b10001110 \! +b10001110 ]! +b10001110 ^! +b10001110 _! b10001110 `! b10001110 a! b10001110 b! b10001110 c! b10001110 d! -b10001110 e! -b10001110 f! -b10001110 g! b10001110 h! +b10001110 i! +b10001110 j! +b10001110 k! b10001110 l! b10001110 m! b10001110 n! b10001110 o! b10001110 p! -b10001110 q! -b10001110 r! -b10001110 s! b10001110 t! -1x! +b10001110 u! +b10001110 v! +b10001110 w! +b10001110 x! b10001110 y! b10001110 z! b10001110 {! b10001110 |! -b10001110 }! -b10001110 ~! -b10001110 !" b10001110 "" b10001110 #" b10001110 $" -b001001101 %" -b001001101 &" +b10001110 %" +b10001110 &" +b10001110 '" +b10001110 (" b10001110 )" b10001110 *" -b10001110 +" +b10001110 ." b10001110 /" b10001110 0" b10001110 1" @@ -1914,11 +2053,25 @@ b10001110 ?" b10001110 @" b10001110 A" b10001110 B" -b10001110 C" -b00000000000000000000000010001110 D" -b00000000000000000000000010001110 E" b10001110 F" -b10001110100011101000111010001110 G" +b10001110 G" b10001110 H" -b10001110100011101000111010001110 I" -b10001110100011101000111010001110 K" +b10001110 I" +b10001110 J" +b10001110 K" +b10001110 L" +b10001110 M" +b10001110 N" +b10001110 O" +b00000000000000000000000010001110 P" +b00000000000000000000000010001110 Q" +b00000000000000000000000010001110 R" +b10001110 S" +b10001110100011101000111010001110 T" +b10001110 U" +b10001110100011101000111010001110 V" +b10001110 W" +b10001110100011101000111010001110 X" +b10001110 Y" +b10001110100011101000111010001110 Z" +1[" diff --git a/test_regress/t/t_struct_array.v b/test_regress/t/t_struct_array.v index ae6c6c18c..9391b7417 100644 --- a/test_regress/t/t_struct_array.v +++ b/test_regress/t/t_struct_array.v @@ -26,10 +26,10 @@ module t(clk); endmodule module sub(a_out); - parameter n = 4; - output TEST_TYPES::a_struct_t [n-1:0] a_out; + parameter N = 4; + output TEST_TYPES::a_struct_t [N-1:0] a_out; always_comb begin - for (int i=0;i Date: Fri, 29 Aug 2025 21:13:43 -0400 Subject: [PATCH 239/252] Commentary: Changes update --- Changes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changes b/Changes index 89dcee298..7d5569a1f 100644 --- a/Changes +++ b/Changes @@ -41,6 +41,7 @@ Verilator 5.039 devel * Support generic interfaces (#6272). [Igor Zaworski, Antmicro Ltd.] * Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] * Support future sampled value functions. +* Support simple disable within task (#6334). [Ryszard Rozak, Antmicro Ltd.] * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Change `$display("%p")` to remove space after `}`. @@ -53,6 +54,7 @@ Verilator 5.039 devel * Optimize variable removal in scoped DFG (#6260). [Geza Lore] * Optimize acyclic DFG components into the original acyclic sub-graph. (#6261). [Geza Lore] * Optimize multiplexers in DFG synthesis (#6331). [Geza Lore] +* Optimize interfaces in DFG (#6332). [Geza Lore] * Fix loop initialization visibility outside loop (#4237). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). @@ -80,6 +82,7 @@ Verilator 5.039 devel * Fix error when force assignment is used with ref function args (#6244). [Ryszard Rozak, Antmicro Ltd.] * Fix write of 0 in '%c' (#6248) (#6249). [Rodrigo Batista de Moraes] * Fix coverage of variables of complex types (#6250). [Ryszard Rozak, Antmicro Ltd.] +* Fix broken support of unassigned virtual interfaces (#6253) (#6338). [Szymon Gizler, Antmicro Ltd.] * Fix partial DFG conversion of concat assignments (#6255). [Geza Lore] * Fix dynamic cast purity (#6267). [Igor Zaworski, Antmicro Ltd.] * Fix same variable on the RHS forced to two different LHSs. (#6269). [Artur Bieniek, Antmicro Ltd.] @@ -100,6 +103,7 @@ Verilator 5.039 devel * Fix gathering sensitivities from virtual interface members (#6325). [Aleksander Kiryk] * Fix FreeBSD missing headers (#6326). [Aleksander Kiryk] * Fix to select UDPs when they are the only candidate for a top module. +* Fix splitting of assignments to SC variables (#6329) (#6336). [Geza Lore] * Fix to localize for super constructors with function calls as arguments (#6330). [Igor Zaworski, Antmicro Ltd.] From 91ae4c35b7fad809e643d679cfa47374ec943b7e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 29 Aug 2025 21:19:37 -0400 Subject: [PATCH 240/252] Add error on force/release non-constant selects. --- Changes | 1 + src/V3Width.cpp | 48 ++++++++++++++++++++++++--- test_regress/t/t_force_select_bad.out | 30 +++++++++++++++++ test_regress/t/t_force_select_bad.py | 16 +++++++++ test_regress/t/t_force_select_bad.v | 30 +++++++++++++++++ 5 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 test_regress/t/t_force_select_bad.out create mode 100755 test_regress/t/t_force_select_bad.py create mode 100644 test_regress/t/t_force_select_bad.v diff --git a/Changes b/Changes index 7d5569a1f..02029ac36 100644 --- a/Changes +++ b/Changes @@ -23,6 +23,7 @@ Verilator 5.039 devel * Add ALWNEVER warning, for `always @*` that never execute (#6291). * Add separate coverage counters for toggles 0->1 and 1->0 (#6086). [Ryszard Rozak, Antmicro Ltd.] * Add error on class 'function static'. +* Add error on force/release non-constant selects. * Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk. * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support unassigned virtual interfaces (#5265) (#6245). [Szymon Gizler, Antmicro Ltd.] diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 49cf22ba6..5bef730b3 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5469,6 +5469,7 @@ class WidthVisitor final : public VNVisitor { iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); // UINFOTREE(1, nodep, "", "AssignOut"); } + if (VN_IS(nodep, AssignForce)) checkForceReleaseLhs(nodep, nodep->lhsp()); if (AstNode* const controlp = nodep->timingControlp()) { if (VN_IS(m_ftaskp, Func)) { controlp->v3error("Timing controls are not legal in functions. Suggest use a task " @@ -5537,6 +5538,7 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?"); UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?"); + checkForceReleaseLhs(nodep, nodep->lhsp()); } void formatNoStringArg(AstNode* argp, char ch) { @@ -8544,7 +8546,7 @@ class WidthVisitor final : public VNVisitor { const AstArg* const argp = tconnect.second; const AstNode* const pinp = argp->exprp(); if (!pinp) continue; // Argument error we'll find later - if (hasOpenArrayIterateDType(portp->dtypep())) portp->dtypep(pinp->dtypep()); + if (hasOpenArrayDTypeRecurse(portp->dtypep())) portp->dtypep(pinp->dtypep()); } } @@ -8552,7 +8554,7 @@ class WidthVisitor final : public VNVisitor { bool hasOpen = false; for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* const portp = VN_CAST(stmtp, Var)) { - if (portp->isDpiOpenArray() || hasOpenArrayIterateDType(portp->dtypep())) { + if (portp->isDpiOpenArray() || hasOpenArrayDTypeRecurse(portp->dtypep())) { portp->isDpiOpenArray(true); hasOpen = true; } @@ -8560,10 +8562,10 @@ class WidthVisitor final : public VNVisitor { } return hasOpen; } - bool hasOpenArrayIterateDType(AstNodeDType* nodep) { + bool hasOpenArrayDTypeRecurse(AstNodeDType* nodep) { // Return true iff this datatype or child has an openarray if (VN_IS(nodep, UnsizedArrayDType)) return true; - if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp()); + if (nodep->subDTypep()) return hasOpenArrayDTypeRecurse(nodep->subDTypep()->skipRefp()); return false; } AstNodeDType* dtypeNotIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) { @@ -8599,6 +8601,44 @@ class WidthVisitor final : public VNVisitor { } return nodep; } + void checkForceReleaseLhs(AstNode* nodep, AstNode* lhsp) { + // V3Force can't check as vector may have expanded, or propagated constant into index + if (AstNode* const selNodep = selectNonConstantRecurse(lhsp)) + nodep->v3error((VN_IS(nodep, Release) ? "Release"s : "Force"s) + + " left-hand-side must not have variable bit/part select " + "(IEEE 1800-2023 10.6.2)\n" + << nodep->warnContextPrimary() << '\n' + << selNodep->warnOther() << "... Location of non-constant index\n" + << selNodep->warnContextSecondary()); + } + AstNode* selectNonConstantRecurse(AstNode* nodep, bool inSel = false) { + // If node has a non-constant select, return that select + AstNode* resultp = nullptr; + if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) { + resultp = selectNonConstantRecurse(anodep->fromp(), inSel); + if (resultp) return resultp; + resultp = selectNonConstantRecurse(anodep->bitp(), true); + } else if (AstSel* const anodep = VN_CAST(nodep, Sel)) { + resultp = selectNonConstantRecurse(anodep->fromp(), inSel); + if (resultp) return resultp; + resultp = selectNonConstantRecurse(anodep->lsbp(), true); + } else if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) { + if (inSel && !anodep->varp()->isParam() && !anodep->varp()->isGenVar()) return anodep; + } else { + if (AstNode* const refp = nodep->op1p()) + resultp = selectNonConstantRecurse(refp, inSel); + if (resultp) return resultp; + if (AstNode* const refp = nodep->op2p()) + resultp = selectNonConstantRecurse(refp, inSel); + if (resultp) return resultp; + if (AstNode* const refp = nodep->op3p()) + resultp = selectNonConstantRecurse(refp, inSel); + if (resultp) return resultp; + if (AstNode* const refp = nodep->op4p()) + resultp = selectNonConstantRecurse(refp, inSel); + } + return resultp; + } //---------------------------------------------------------------------- // METHODS - special type detection diff --git a/test_regress/t/t_force_select_bad.out b/test_regress/t/t_force_select_bad.out new file mode 100644 index 000000000..722296d71 --- /dev/null +++ b/test_regress/t/t_force_select_bad.out @@ -0,0 +1,30 @@ +%Error: t/t_force_select_bad.v:24:5: Force left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2) + : ... note: In instance 't' + 24 | force array1[bad_index] = 1'b1; + | ^~~~~ + t/t_force_select_bad.v:24:18: ... Location of non-constant index + 24 | force array1[bad_index] = 1'b1; + | ^~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_force_select_bad.v:25:5: Release left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2) + : ... note: In instance 't' + 25 | release array1[bad_index]; + | ^~~~~~~ + t/t_force_select_bad.v:25:20: ... Location of non-constant index + 25 | release array1[bad_index]; + | ^~~~~~~~~ +%Error: t/t_force_select_bad.v:26:5: Force left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2) + : ... note: In instance 't' + 26 | force vec[bad_index+:1] = 1'b1; + | ^~~~~ + t/t_force_select_bad.v:26:15: ... Location of non-constant index + 26 | force vec[bad_index+:1] = 1'b1; + | ^~~~~~~~~ +%Error: t/t_force_select_bad.v:27:5: Release left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2) + : ... note: In instance 't' + 27 | release vec[bad_index+:1]; + | ^~~~~~~ + t/t_force_select_bad.v:27:17: ... Location of non-constant index + 27 | release vec[bad_index+:1]; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_force_select_bad.py b/test_regress/t/t_force_select_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_force_select_bad.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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_force_select_bad.v b/test_regress/t/t_force_select_bad.v new file mode 100644 index 000000000..5b5bdb725 --- /dev/null +++ b/test_regress/t/t_force_select_bad.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + wire array1[2:1]; + wire [2:-1] vec; + + integer bad_index = 1; + + parameter P_ONE = 1; + + initial begin + force array1[P_ONE] = 1'b1; // ok + release array1[P_ONE]; // ok + force vec[P_ONE+:1] = 1'b1; // ok + release vec[P_ONE+:1]; // ok + + // IEEE 1800-2023 10.6.2 [A force] shall not be a bit-select or a + // part-select of a [non-constant] variable or of a net with a user-defined + // nettype. + force array1[bad_index] = 1'b1; // <---- BAD not constant index + release array1[bad_index]; // <---- BAD not constant index + force vec[bad_index+:1] = 1'b1; // <---- BAD not constant index + release vec[bad_index+:1]; // <---- BAD not constant index + end + +endmodule From 40dda323fe2f1e84874e046acaed27fcb1f2e857 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Sat, 30 Aug 2025 03:20:09 +0200 Subject: [PATCH 241/252] Fix upcasting class type parameters (#6344) Signed-off-by: Krzysztof Bieganski --- src/V3EmitCFunc.h | 3 ++- test_regress/t/t_class_param_upcast.py | 16 +++++++++++++ test_regress/t/t_class_param_upcast.v | 33 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_class_param_upcast.py create mode 100644 test_regress/t/t_class_param_upcast.v diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 5506ebb83..0a93cb1ad 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1337,7 +1337,8 @@ public: } void visit(AstCCast* nodep) override { // Extending a value of the same word width is just a NOP. - if (const AstClassRefDType* const classDtypep = VN_CAST(nodep->dtypep(), ClassRefDType)) { + if (const AstClassRefDType* const classDtypep + = VN_CAST(nodep->dtypep()->skipRefp(), ClassRefDType)) { putns(nodep, "(" + classDtypep->cType("", false, false) + ")("); } else if (nodep->size() <= VL_BYTESIZE) { putns(nodep, "(CData)("); diff --git a/test_regress/t/t_class_param_upcast.py b/test_regress/t/t_class_param_upcast.py new file mode 100755 index 000000000..147fe6faf --- /dev/null +++ b/test_regress/t/t_class_param_upcast.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_class_param_upcast.v b/test_regress/t/t_class_param_upcast.v new file mode 100644 index 000000000..d82f1320a --- /dev/null +++ b/test_regress/t/t_class_param_upcast.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 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class factory #(type T); + static function T create; + T obj = new; + return obj; + endfunction +endclass + +class foo; +endclass + +class bar extends foo; + static function bar create; + bar b = new; + return b; + endfunction +endclass + +module t; + initial begin + foo f; + if (bit'($random)) + f = bar::create; + else + f = factory#(foo)::create(); + $finish; + end +endmodule; From fbfd1f12ddd25c7c3c7801e8b1fc1f415a5d8882 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 29 Aug 2025 21:36:54 -0400 Subject: [PATCH 242/252] Tests: Fix sarif reruns --- test_regress/t/t_sarif.py | 2 +- test_regress/t/t_sarif.sarif.out | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_regress/t/t_sarif.py b/test_regress/t/t_sarif.py index 7780ff8e0..b62cbf5c1 100755 --- a/test_regress/t/t_sarif.py +++ b/test_regress/t/t_sarif.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint(verilator_flags2=['-Wno-fatal --diagnostics-sarif'], +test.lint(verilator_flags2=['-Wno-fatal --diagnostics-sarif --no-skip-identical'], expect_filename=test.golden_filename) sarif_filename = test.obj_dir + "/" + test.vm_prefix + ".sarif" diff --git a/test_regress/t/t_sarif.sarif.out b/test_regress/t/t_sarif.sarif.out index 58f0f611e..f99874753 100644 --- a/test_regress/t/t_sarif.sarif.out +++ b/test_regress/t/t_sarif.sarif.out @@ -26,7 +26,7 @@ }, "invocations": [ { - "commandLine": "--prefix Vt_sarif -cc -Mdir obj_vlt/t_sarif --fdedup --debug-check --comp-limit-members 10 --x-assign unique -Wno-fatal --diagnostics-sarif --clk clk -f input.vc +define+TEST_OBJ_DIR=obj_vlt/t_sarif +define+TEST_DUMPFILE=obj_vlt/t_sarif/simx.vcd t/t_sarif.v +librescan +notimingchecks +libext+.v -y t +incdir+t", + "commandLine": "--prefix Vt_sarif -cc -Mdir obj_vlt/t_sarif --fdedup --debug-check --comp-limit-members 10 --x-assign unique -Wno-fatal --diagnostics-sarif --no-skip-identical --clk clk -f input.vc +define+TEST_OBJ_DIR=obj_vlt/t_sarif +define+TEST_DUMPFILE=obj_vlt/t_sarif/simx.vcd t/t_sarif.v +librescan +notimingchecks +libext+.v -y t +incdir+t", "executionSuccessful": true } ], From 712ff95a4876ebfd73c8da5800a0a72d9de85add Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 07:42:41 -0400 Subject: [PATCH 243/252] Internals: Remove unneeded return, cleanup param test. No functional change --- src/V3Const.cpp | 6 +- src/V3Const.h | 2 +- test_regress/t/t_const_slicesel_bad.v | 8 +- test_regress/t/t_param_pattern.v | 10 +- test_regress/t/t_struct_param.v | 80 ++++++++-------- test_regress/t/t_struct_param_overflow.v | 114 +++++++++++------------ 6 files changed, 111 insertions(+), 109 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 77a81b6d0..e5bf9ecf5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -4047,8 +4047,7 @@ void V3Const::constifyParamsNoWarnEdit(AstNode* nodep) { //! generated. Since such occurrences, must be constant, in order to be //! something a generate block can depend on, we can wait until later to do the //! width check. -//! @return Pointer to the edited node. -AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { +void V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // UINFOTREE(1, nodep, "", "forceConPRE:"); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized @@ -4064,11 +4063,10 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // init value because we need widthing above to handle the var's type. if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); } else { - nodep = visitor.mainAcceptEdit(nodep); + VL_DO_DANGLING(visitor.mainAcceptEdit(nodep), nodep); } // Because we do edits, nodep links may get trashed and core dump this. // UINFOTREE(1, nodep, "", "forceConDONE"); - return nodep; } void V3Const::constifyAllLint(AstNetlist* nodep) { diff --git a/src/V3Const.h b/src/V3Const.h index c8f1c5e27..eeee84b9d 100644 --- a/src/V3Const.h +++ b/src/V3Const.h @@ -28,7 +28,7 @@ class V3Const final { public: static void constifyParamsEdit(AstNode* nodep) VL_MT_DISABLED; static void constifyParamsNoWarnEdit(AstNode* nodep) VL_MT_DISABLED; - static AstNode* constifyGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED; + static void constifyGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED; // Only do constant pushing, without removing dead logic static void constifyAllLive(AstNetlist* nodep) VL_MT_DISABLED; // Everything that's possible diff --git a/test_regress/t/t_const_slicesel_bad.v b/test_regress/t/t_const_slicesel_bad.v index 9d588a3ad..e5a9edbc5 100644 --- a/test_regress/t/t_const_slicesel_bad.v +++ b/test_regress/t/t_const_slicesel_bad.v @@ -4,11 +4,11 @@ // any use, without warranty, 2021 by Michael Lefebvre. // SPDX-License-Identifier: CC0-1.0 -module t(/*AUTOARG*/); +module t; -localparam int unsigned A3 [2:0] = '{4,5,6}; + localparam int unsigned A3[2:0] = '{4, 5, 6}; -// slicesel out of range should fail -localparam int unsigned B32_T [1:0] = A3[3:1]; + // slicesel out of range should fail + localparam int unsigned B32_T[1:0] = A3[3:1]; endmodule diff --git a/test_regress/t/t_param_pattern.v b/test_regress/t/t_param_pattern.v index c5af3bad9..43df17942 100644 --- a/test_regress/t/t_param_pattern.v +++ b/test_regress/t/t_param_pattern.v @@ -14,9 +14,9 @@ package config_pkg; int USE_QUAD0; int USE_QUAD1; int USE_QUAD2; - } config_struct; + } config_struct_t; -endpackage : config_pkg +endpackage module t; import config_pkg::*; @@ -28,11 +28,11 @@ module t; USE_QUAD1: 5, USE_QUAD2: 6 })) a_submodule_I (); -endmodule : t +endmodule module struct_submodule import config_pkg::*; - #(parameter config_struct MY_CONFIG = '0); + #(parameter config_struct_t MY_CONFIG = '0); initial begin `checkd(MY_CONFIG.UPPER0, 10); @@ -43,4 +43,4 @@ module struct_submodule $write("*-* All Finished *-*\n"); $finish; end -endmodule : struct_submodule +endmodule diff --git a/test_regress/t/t_struct_param.v b/test_regress/t/t_struct_param.v index 081a8c85f..02d6878ab 100644 --- a/test_regress/t/t_struct_param.v +++ b/test_regress/t/t_struct_param.v @@ -4,53 +4,57 @@ // without warranty, 2017 by Matt Myers. // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off `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); +`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); +// verilog_format: on package config_pkg; - typedef struct packed { - int UPPER0; - struct packed { - int USE_QUAD0; - int USE_QUAD1; - int USE_QUAD2; - } mac; - int UPPER2; - } config_struct; + typedef struct packed { + int UPPER0; + struct packed { + int USE_QUAD0; + int USE_QUAD1; + int USE_QUAD2; + } mac; + int UPPER2; + } config_struct_t; - function automatic config_struct static_config(int selector); - config_struct return_config; - return_config = '0; - return_config.UPPER0 = 10; - return_config.UPPER2 = 20; - return_config.mac.USE_QUAD0 = 4; - return_config.mac.USE_QUAD2 = 6; - case (selector) - 1: return_config.mac.USE_QUAD1 = 5; - endcase - return return_config; - endfunction -endpackage : config_pkg + function automatic config_struct_t static_config(int selector); + config_struct_t return_config; + return_config = '0; + return_config.UPPER0 = 10; + return_config.UPPER2 = 20; + return_config.mac.USE_QUAD0 = 4; + return_config.mac.USE_QUAD2 = 6; + case (selector) + 1: return_config.mac.USE_QUAD1 = 5; + endcase + return return_config; + endfunction +endpackage module t; - import config_pkg::*; + import config_pkg::*; - localparam config_struct MY_CONFIG = static_config(1); + localparam config_struct_t MY_CONFIG = static_config(1); - struct_submodule #(.MY_CONFIG(MY_CONFIG)) a_submodule_I (); -endmodule : t + struct_submodule #(.MY_CONFIG(MY_CONFIG)) a_submodule_I (); +endmodule module struct_submodule import config_pkg::*; - #(parameter config_struct MY_CONFIG = '0); +#( + parameter config_struct_t MY_CONFIG = '0 +); - initial begin - `checkd(MY_CONFIG.UPPER0, 10); - `checkd(MY_CONFIG.mac.USE_QUAD0, 4); - `checkd(MY_CONFIG.mac.USE_QUAD1, 5); - `checkd(MY_CONFIG.mac.USE_QUAD2, 6); - `checkd(MY_CONFIG.UPPER2, 20); - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule : struct_submodule + initial begin + `checkd(MY_CONFIG.UPPER0, 10); + `checkd(MY_CONFIG.mac.USE_QUAD0, 4); + `checkd(MY_CONFIG.mac.USE_QUAD1, 5); + `checkd(MY_CONFIG.mac.USE_QUAD2, 6); + `checkd(MY_CONFIG.UPPER2, 20); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_struct_param_overflow.v b/test_regress/t/t_struct_param_overflow.v index 97f3220ec..29c3b6492 100644 --- a/test_regress/t/t_struct_param_overflow.v +++ b/test_regress/t/t_struct_param_overflow.v @@ -6,76 +6,76 @@ package config_pkg; - localparam int unsigned N = 10; + localparam int unsigned N = 10; - typedef struct packed { - logic [N-1:0][31:0] lo; - logic [N-1:0][31:0] hi; - logic [100-1:0][31:0] x; - int unsigned n; - } config_struct; + typedef struct packed { + logic [N-1:0][31:0] lo; + logic [N-1:0][31:0] hi; + logic [100-1:0][31:0] x; + int unsigned n; + } config_struct_t; - function automatic logic subcheck(logic[31:0] lo, logic[31:0] hi, logic[31:0] val); - return lo <= val && val < hi; - endfunction + function automatic logic subcheck(logic [31:0] lo, logic [31:0] hi, logic [31:0] val); + return lo <= val && val < hi; + endfunction - function automatic logic check(config_struct cfg, logic[31:0] val); - logic[N-1:0] good = '0; - logic[N-1:0] bad = '0; - for (int i = 0; i < cfg.n; i++) begin - good[i] = subcheck(cfg.lo[i], cfg.hi[i], val); - end - for (int i = cfg.n; i < N; i++) begin - bad[i] = !(cfg.lo[i] == '0 && cfg.hi[i] == '0); - end - return good != '0 && bad == '0; - endfunction + function automatic logic check(config_struct_t cfg, logic [31:0] val); + logic [N-1:0] good = '0; + logic [N-1:0] bad = '0; + for (int i = 0; i < cfg.n; i++) begin + good[i] = subcheck(cfg.lo[i], cfg.hi[i], val); + end + for (int i = cfg.n; i < N; i++) begin + bad[i] = !(cfg.lo[i] == '0 && cfg.hi[i] == '0); + end + return good != '0 && bad == '0; + endfunction endpackage : config_pkg -module t(/*AUTOARG*/ - // Inputs - clk -); +module t ( /*AUTOARG*/ + // Inputs + clk + ); - input clk; - import config_pkg::*; + input clk; + import config_pkg::*; - parameter config_struct MY_CONFIG = '{ - lo: {((N-3)*32)'('0), 32'h00, 32'h10, 32'h20}, - hi: {((N-3)*32)'('0), 32'h10, 32'h20, 32'h30}, - x : 3200'h0deadbeef, - n : 3 - }; + parameter config_struct_t MY_CONFIG = '{ + lo: {((N - 3) * 32)'('0), 32'h00, 32'h10, 32'h20}, + hi: {((N - 3) * 32)'('0), 32'h10, 32'h20, 32'h30}, + x : 3200'h0deadbeef, + n : 3 + }; - struct_submodule #(.MY_CONFIG(MY_CONFIG)) a_submodule_I (.clk); -endmodule : t + struct_submodule #(.MY_CONFIG(MY_CONFIG)) a_submodule_I (.clk); +endmodule module struct_submodule - import config_pkg::*; - #( - parameter config_struct MY_CONFIG = '0 - ) ( - input clk - ); + import config_pkg::*; +#( + parameter config_struct_t MY_CONFIG = '0 +) ( + input clk +); - logic [31:0] val; - logic c; - int count = 0; + logic [31:0] val; + logic c; + int count = 0; - assign val = 3; - assign c = check(MY_CONFIG, count); + assign val = 3; + assign c = check(MY_CONFIG, count); - always @(posedge clk) begin - count <= count + 1; - if (c != '1) begin - $error("c not 1"); - $stop; - end - if (count >= 10) begin - $write("*-* All Finished *-*\n"); - $finish; - end + always @(posedge clk) begin + count <= count + 1; + if (c != '1) begin + $error("c not 1"); + $stop; end + if (count >= 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end -endmodule : struct_submodule +endmodule From b6ecffeb60b2482af74620e3b682ba561c53f659 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 07:45:35 -0400 Subject: [PATCH 244/252] Support recursive constant functions. --- Changes | 1 + src/V3Simulate.h | 114 ++++++++--- test_regress/t/t_func_recurse_param.out | 12 -- test_regress/t/t_func_recurse_param.py | 5 +- test_regress/t/t_func_recurse_param.v | 45 +++-- test_regress/t/t_func_recurse_param_bad.out | 211 +++++++++++++++++++- test_regress/t/t_func_recurse_param_bad.v | 22 +- 7 files changed, 342 insertions(+), 68 deletions(-) delete mode 100644 test_regress/t/t_func_recurse_param.out diff --git a/Changes b/Changes index 02029ac36..551cba559 100644 --- a/Changes +++ b/Changes @@ -43,6 +43,7 @@ Verilator 5.039 devel * Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.] * Support future sampled value functions. * Support simple disable within task (#6334). [Ryszard Rozak, Antmicro Ltd.] +* Support recursive constant functions. * Change control file `public_flat_*` and other signal attributes to support __ in names (#6140). * Change runtime to exit() instead of abort(), unless under +verilated+debug. * Change `$display("%p")` to remove space after `}`. diff --git a/src/V3Simulate.h b/src/V3Simulate.h index e3b1d0545..789905885 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -74,6 +74,10 @@ class SimulateVisitor VL_NOT_FINAL : public VNVisitorConst { // is missing, we will not apply the optimization, rather then bomb. private: + // CONSTANTS + static constexpr int CONST_FUNC_RECURSION_MAX = 1000; + static constexpr int CALL_STACK_MAX = 100; + // NODE STATE // Cleared on each always/assignw const VNUser1InUse m_inuser1; @@ -136,12 +140,13 @@ private: bool m_isCoverage; // Has coverage int m_instrCount; ///< Number of nodes int m_dataCount; ///< Bytes of data + int m_recurseCount = 0; // Now deep in current recursion AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from // Simulating: // Allocators for constants of various data types std::unordered_map m_constps; size_t m_constGeneration = 0; - std::vector m_callStack; ///< Call stack for verbose error messages + std::vector m_callStack; // Call stack for verbose error messages // Cleanup // V3Numbers that represents strings are a bit special and the API for @@ -217,6 +222,7 @@ public: } // LCOV_EXCL_STOP m_whyNotOptimizable = why; std::ostringstream stack; + int n = 0; for (const auto& callstack : vlstd::reverse_view(m_callStack)) { const AstFuncRef* const funcp = callstack->m_funcp; stack << "\n " << funcp->fileline() << "... Called from '" @@ -232,6 +238,10 @@ public: << prettyNumber(&valp->num(), dtypep); } } + if (++n > CALL_STACK_MAX) { + stack << "\n ... stack truncated"; + break; + } } m_whyNotOptimizable += stack.str(); } @@ -444,6 +454,18 @@ private: return ""; } + void initVar(AstVar* nodep) { + if (const AstBasicDType* const basicp = nodep->dtypeSkipRefp()->basicp()) { + AstConst cnst{nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0}; + if (basicp->isZeroInit()) { + cnst.num().setAllBits0(); + } else { + cnst.num().setAllBitsX(); + } + newValue(nodep, &cnst); + } + } + // VISITORS void visit(AstAlways* nodep) override { if (jumpingOver()) return; @@ -906,6 +928,8 @@ private: iterateAndNextConstNull(nodep->rhsp()); // Value to assign handleAssignRecurse(nodep, nodep->lhsp(), nodep->rhsp()); + // UINFO(9, "set " << fetchConst(nodep->rhsp())->num().ascii() << " for assign " + // << nodep->lhsp()->name()); } void visit(AstArraySel* nodep) override { checkNodeInfo(nodep); @@ -1143,17 +1167,49 @@ private: VL_DANGLING(funcp); // Make sure we've sized the function funcp = nodep->taskp(); UASSERT_OBJ(funcp, nodep, "Not linked"); + if (funcp->recursive()) { - // Because we attach values to nodes rather then making a stack, this is a mess - // When we do support this, we need a stack depth limit of 1K or something, - // and the t_func_recurse_param_bad.v test should check that limit's error message - clearOptimizable(funcp, "Unsupported: Recursive constant functions"); - return; + if (m_recurseCount >= CONST_FUNC_RECURSION_MAX) { + clearOptimizable(funcp, "Constant function recursed more than "s + + std::to_string(CONST_FUNC_RECURSION_MAX) + " times"); + return; + } + ++m_recurseCount; + } + + // Values from previous call, so can save to stack + // The "stack" is this visit function's local stack, as this visit is itself recursing + std::map oldValues; + + if (funcp->recursive() && !m_checkOnly) { + // Save local automatics + funcp->foreach([this, &oldValues](AstVar* varp) { + if (varp->lifetime().isAutomatic()) { // This also does function's I/O + if (AstNodeExpr* const valuep = fetchValueNull(varp)) { + AstNodeExpr* const nvaluep = newTrackedClone(valuep); + oldValues.emplace(varp, nvaluep); + } + } + }); + // Save every expression value, as might be in middle of expression + // that calls recursively back to this same function. + // This is much heavier-weight then likely is needed, in theory + // we could look at the visit stack to determine what nodes + // need save-restore, but that is difficult to get right, and + // recursion is rare. + funcp->foreach([this, &oldValues](AstNodeExpr* exprp) { + if (VN_IS(exprp, Const)) return; // Speed up as won't change + if (AstNodeExpr* const valuep = fetchValueNull(exprp)) { + AstNodeExpr* const nvaluep = newTrackedClone(valuep); + oldValues.emplace(exprp, nvaluep); + } + }); } // Apply function call values to function V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); // Must do this in two steps, eval all params, then apply them // Otherwise chained functions may have the wrong results + std::vector> portValues; for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) { AstVar* const portp = it->first; AstNode* const pinp = it->second->exprp(); @@ -1166,37 +1222,45 @@ private: } // Evaluate pin value iterateConst(pinp); + // Clone in case are recursing + portValues.push_back(std::make_pair(portp, newTrackedClone(fetchValue(pinp)))); } } - for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) { - AstVar* const portp = it->first; - AstNode* const pinp = it->second->exprp(); - if (pinp) { // Else too few arguments in function call - ignore it - // Apply value to the function - if (!m_checkOnly && optimizable()) newValue(portp, fetchValue(pinp)); + // Apply value to the function + if (!m_checkOnly && optimizable()) + for (auto& it : portValues) { + if (!m_checkOnly && optimizable()) newValue(it.first, it.second); } - } SimStackNode stackNode{nodep, &tconnects}; // cppcheck-suppress danglingLifetime m_callStack.push_back(&stackNode); - // Clear output variable - if (const auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) { - AstConst cnst{funcp->fvarp()->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), - 0}; - if (basicp->isZeroInit()) { - cnst.num().setAllBits0(); - } else { - cnst.num().setAllBitsX(); - } - newValue(funcp->fvarp(), &cnst); + if (!m_checkOnly) { + // Clear output variable + initVar(VN_CAST(funcp->fvarp(), Var)); + // Clear other automatic variables + funcp->foreach([this, &oldValues](AstVar* varp) { + if (varp->lifetime().isAutomatic() && !varp->isIO()) initVar(varp); + }); } + // Evaluate the function iterateConst(funcp); m_callStack.pop_back(); + AstNodeExpr* returnp = nullptr; if (!m_checkOnly && optimizable()) { - // Grab return value from output variable (if it's a function) + // Grab return value from output variable UASSERT_OBJ(funcp->fvarp(), nodep, "Function reference points at non-function"); - newValue(nodep, fetchValue(funcp->fvarp())); + returnp = newTrackedClone(fetchValue(funcp->fvarp())); + UINFO(5, "func " << nodep->name() << " return = " << returnp); + } + // Restore local automatics (none unless recursed) + for (const auto& it : oldValues) { + if (it.second) newValue(it.first, it.second); + } + if (returnp) newValue(nodep, returnp); + if (funcp->recursive()) { + UASSERT_OBJ(m_recurseCount > 0, nodep, "recurse underflow"); + --m_recurseCount; } } diff --git a/test_regress/t/t_func_recurse_param.out b/test_regress/t/t_func_recurse_param.out deleted file mode 100644 index a7cc20861..000000000 --- a/test_regress/t/t_func_recurse_param.out +++ /dev/null @@ -1,12 +0,0 @@ -%Error: t/t_func_recurse_param.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self' - : ... note: In instance 't' - t/t_func_recurse_param.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions - 15 | localparam int ZERO = recurse_self(0); - | ^~~~~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_func_recurse_param.v:16:28: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self' - : ... note: In instance 't' - t/t_func_recurse_param.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions - 16 | localparam int ELEVEN = recurse_self(3); - | ^~~~~~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_func_recurse_param.py b/test_regress/t/t_func_recurse_param.py index 966dc53da..d4f986441 100755 --- a/test_regress/t/t_func_recurse_param.py +++ b/test_regress/t/t_func_recurse_param.py @@ -11,9 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) +test.compile() -if not test.vlt_all: - test.execute() +test.execute() test.passes() diff --git a/test_regress/t/t_func_recurse_param.v b/test_regress/t/t_func_recurse_param.v index 0082b0f20..cf82c3993 100644 --- a/test_regress/t/t_func_recurse_param.v +++ b/test_regress/t/t_func_recurse_param.v @@ -4,22 +4,41 @@ // any use, without warranty, 2003 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + module t; - function automatic int recurse_self; - input int i; - if (i == 0) recurse_self = 0; - else recurse_self = i + recurse_self(i - 1) * 2; - endfunction + function automatic int recurse_self; + input int i; + int r1; + int r2; + // Simulator support for statics in constant functions get varying results, not testing + static int local_static = 10; + automatic int local_automatic; // check each function call resets to zero + if (i == 0) begin + local_static = 0; + recurse_self = 0; + end + else begin + local_static = local_static + 1; + local_automatic = local_automatic + 10; + recurse_self = i + recurse_self(i - 1) * 2 + recurse_self(i - 1) * 3 + local_automatic; + end + endfunction - localparam int ZERO = recurse_self(0); - localparam int ELEVEN = recurse_self(3); + localparam int F0 = recurse_self(0); + localparam int F3 = recurse_self(3); + localparam int F4 = recurse_self(4); - initial begin - if (ZERO != 0) $stop; - if (ELEVEN != 11) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end + initial begin + `checkd(F0, 0); + `checkd(F3, 348); + `checkd(F4, 1754); + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_func_recurse_param_bad.out b/test_regress/t/t_func_recurse_param_bad.out index b32ab1b11..77e217bd6 100644 --- a/test_regress/t/t_func_recurse_param_bad.out +++ b/test_regress/t/t_func_recurse_param_bad.out @@ -1,7 +1,210 @@ -%Error: t/t_func_recurse_param_bad.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self' +%Error: t/t_func_recurse_param_bad.v:15:25: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self' : ... note: In instance 't' - t/t_func_recurse_param_bad.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions - 15 | localparam int HUGE = recurse_self(10000); - | ^~~~~~~~~~~~ + t/t_func_recurse_param_bad.v:9:26: ... Location of non-constant FUNC 'recurse_self': Constant function recursed more than 1000 times + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters: + i = 32'h2329 + ... stack truncated + 15 | localparam int HUGE = recurse_self(10000); + | ^~~~~~~~~~~~ ... 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_func_recurse_param_bad.v b/test_regress/t/t_func_recurse_param_bad.v index eb7ff29b2..f3e88f9ba 100644 --- a/test_regress/t/t_func_recurse_param_bad.v +++ b/test_regress/t/t_func_recurse_param_bad.v @@ -6,18 +6,18 @@ module t; - function automatic int recurse_self; - input int i; - if (i == 0) recurse_self = 0; - else recurse_self = i + recurse_self(i - 1) * 2; - endfunction + function automatic int recurse_self; + input int i; + if (i == 0) recurse_self = 0; + else recurse_self = i + recurse_self(i - 1) * 2; + endfunction - localparam int HUGE = recurse_self(10000); // too much recursion + localparam int HUGE = recurse_self(10000); // too much recursion - initial begin - $display(HUGE); - $write("*-* All Finished *-*\n"); - $finish; - end + initial begin + $display(HUGE); + $write("*-* All Finished *-*\n"); + $finish; + end endmodule From 14ec6258d999a8df38d45f1283e73e1338f37eab Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 08:22:22 -0400 Subject: [PATCH 245/252] Internals: Fix compiler warning last commit --- src/V3Simulate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 789905885..d376ba648 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -1238,7 +1238,7 @@ private: // Clear output variable initVar(VN_CAST(funcp->fvarp(), Var)); // Clear other automatic variables - funcp->foreach([this, &oldValues](AstVar* varp) { + funcp->foreach([this](AstVar* varp) { if (varp->lifetime().isAutomatic() && !varp->isIO()) initVar(varp); }); } From c33f2b42aafaea2c8c825b07e979941a9f0900a7 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 30 Aug 2025 15:34:39 +0100 Subject: [PATCH 246/252] Fix wide select expansion and substitution (#6341) (#6345) --- src/V3AstNodeOther.h | 4 + src/V3Expand.cpp | 244 +++++++++++++++++++++++++++++++------------ src/V3Subst.cpp | 36 ++++--- 3 files changed, 204 insertions(+), 80 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d190df286..895bc6063 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1863,6 +1863,7 @@ class AstVar final : public AstNode { bool m_isHideProtected : 1; // Verilog protected bool m_noReset : 1; // Do not do automated reset/randomization bool m_noSubst : 1; // Do not substitute out references + bool m_substConstOnly : 1; // Only substitute if constant bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam bool m_trace : 1; // Trace this variable bool m_isLatched : 1; // Not assigned in all control paths of combo always @@ -1912,6 +1913,7 @@ class AstVar final : public AstNode { m_isHideProtected = false; m_noReset = false; m_noSubst = false; + m_substConstOnly = false; m_overridenParam = false; m_trace = false; m_isLatched = false; @@ -2068,6 +2070,8 @@ public: bool noReset() const { return m_noReset; } void noSubst(bool flag) { m_noSubst = flag; } bool noSubst() const { return m_noSubst; } + void substConstOnly(bool flag) { m_substConstOnly = flag; } + bool substConstOnly() const { return m_substConstOnly; } void overriddenParam(bool flag) { m_overridenParam = flag; } bool overriddenParam() const { return m_overridenParam; } void trace(bool flag) { m_trace = flag; } diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 24bedd325..12b55af44 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -77,10 +77,14 @@ class ExpandVisitor final : public VNVisitor { AstNode* m_stmtp = nullptr; // Current statement // STATE - across all visitors + AstCFunc* m_funcp = nullptr; // Current function VDouble0 m_statWides; // Statistic tracking VDouble0 m_statWideWords; // Statistic tracking VDouble0 m_statWideLimited; // Statistic tracking + // STATE - for current function + size_t m_nTmps = 0; // Sequence numbers for temopraries + // METHODS // Use state that ExpandOkVisitor calculated bool isImpure(AstNode* nodep) { @@ -147,6 +151,19 @@ class ExpandVisitor final : public VNVisitor { addWordAssign(placep, word, placep->lhsp(), rhsp); } + AstVar* addLocalTmp(AstNode* placep, const char* namep, AstNodeExpr* valuep) { + const std::string name = "__V"s + namep + "_"s + std::to_string(m_nTmps); + FileLine* const flp = placep->fileline(); + AstVar* const tmpp = new AstVar{flp, VVarType::STMTTEMP, name, valuep->dtypep()}; + tmpp->funcLocal(true); + tmpp->isInternal(true); + tmpp->noReset(true); + tmpp->substConstOnly(true); + m_funcp->addInitsp(tmpp); + insertBefore(placep, new AstAssign{flp, new AstVarRef{flp, tmpp, VAccess::WRITE}, valuep}); + return tmpp; + } + static void fixCloneLvalue(AstNode* nodep) { // In AstSel transforms, we call clone() on VarRefs that were lvalues, // but are now being used on the RHS of the assignment @@ -213,31 +230,54 @@ class ExpandVisitor final : public VNVisitor { return newp; } - static AstNodeExpr* newWordSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* lsbp, - uint32_t wordOffset = 0) { - // Return equation to get the VL_BITWORD of a constant or non-constant - UASSERT_OBJ(fromp->isWide(), fromp, "Only need AstWordSel on wide from's"); - if (wordOffset >= static_cast(fromp->widthWords())) { - // e.g. "logic [95:0] var[0]; logic [0] sel; out = var[sel];" - // Squash before C++ to avoid getting a C++ compiler warning - // (even though code would be unreachable as presumably a - // AstCond is protecting above this node. - return new AstConst{fl, AstConst::SizedEData{}, 0}; - } else { - AstNodeExpr* wordp; - FileLine* const lfl = lsbp->fileline(); - if (VN_IS(lsbp, Const)) { - wordp = new AstConst{lfl, wordOffset + VL_BITWORD_E(VN_AS(lsbp, Const)->toUInt())}; - } else { - wordp = new AstShiftR{lfl, lsbp->cloneTreePure(true), - new AstConst{lfl, VL_EDATASIZE_LOG2}, VL_EDATASIZE}; - if (wordOffset - != 0) { // This is indexing a arraysel, so a 32 bit constant is fine - wordp = new AstAdd{lfl, new AstConst{lfl, wordOffset}, wordp}; - } - } - return new AstWordSel{fl, fromp, wordp}; + // Return expression indexing the word that contains 'lsbp' + the given word offset + static AstNodeExpr* newWordIndex(AstNodeExpr* lsbp, uint32_t wordOffset = 0) { + // This is indexing a WordSel, so a 32 bit constants are fine + FileLine* const flp = lsbp->fileline(); + if (AstConst* constp = VN_CAST(lsbp, Const)) { + return new AstConst{flp, wordOffset + VL_BITWORD_E(constp->toUInt())}; } + + if (lsbp->backp()) lsbp = lsbp->cloneTreePure(false); + AstNodeExpr* const wwl2p = new AstConst{flp, VL_EDATASIZE_LOG2}; // Word width log 2 + AstNodeExpr* indexp = new AstShiftR{flp, lsbp, wwl2p, VL_EDATASIZE}; + if (wordOffset) indexp = new AstAdd{flp, new AstConst{flp, wordOffset}, indexp}; + return indexp; + } + + // Return word of fromp that contains word indexp + the given word offset. + static AstNodeExpr* newWordSelWord(FileLine* flp, AstNodeExpr* fromp, AstNodeExpr* indexp, + uint32_t wordOffset = 0) { + UASSERT_OBJ(fromp->isWide(), fromp, "Only need AstWordSel on wide from's"); + + if (AstConst* const constp = VN_CAST(indexp, Const)) { + indexp = nullptr; + wordOffset += constp->toUInt(); + } + + // e.g. "logic [95:0] var[0]; logic [0] sel; out = var[sel];" + // Squash before C++ to avoid getting a C++ compiler warning + // (even though code would be unreachable as presumably a + // AstCond is protecting above this node. + if (wordOffset >= static_cast(fromp->widthWords())) { + return new AstConst{flp, AstConst::SizedEData{}, 0}; + } + + if (fromp->backp()) fromp = fromp->cloneTreePure(false); + + // If indexp was constant, just use it + if (!indexp) return new AstWordSel{flp, fromp, new AstConst{flp, wordOffset}}; + + // Otherwise compute at runtime + if (indexp->backp()) indexp = indexp->cloneTreePure(false); + if (wordOffset) indexp = new AstAdd{flp, new AstConst{flp, wordOffset}, indexp}; + return new AstWordSel{flp, fromp, indexp}; + } + + // Return word of fromp that contains bit lsbp + the given word offset. + static AstNodeExpr* newWordSelBit(FileLine* flp, AstNodeExpr* fromp, AstNodeExpr* lsbp, + uint32_t wordOffset = 0) { + return newWordSelWord(flp, fromp, newWordIndex(lsbp, wordOffset)); } static AstNodeExpr* newSelBitBit(AstNodeExpr* lsbp) { @@ -346,6 +386,10 @@ class ExpandVisitor final : public VNVisitor { // VISITORS void visit(AstCFunc* nodep) override { + VL_RESTORER(m_funcp); + VL_RESTORER(m_nTmps); + m_funcp = nodep; + m_nTmps = 0; iterateChildren(nodep); // Constant fold here, as Ast size can likely be reduced @@ -414,8 +458,7 @@ class ExpandVisitor final : public VNVisitor { FileLine* const nfl = nodep->fileline(); FileLine* const lfl = nodep->lsbp()->fileline(); FileLine* const ffl = nodep->fromp()->fileline(); - AstNodeExpr* lowwordp - = newWordSel(ffl, nodep->fromp()->cloneTreePure(true), nodep->lsbp()); + AstNodeExpr* lowwordp = newWordSelBit(ffl, nodep->fromp(), nodep->lsbp()); if (nodep->isQuad() && !lowwordp->isQuad()) { lowwordp = new AstCCast{nfl, lowwordp, nodep}; } @@ -428,10 +471,8 @@ class ExpandVisitor final : public VNVisitor { = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; AstNodeExpr* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, nodep->lsbp()->cloneTreePure(true)}; - AstNodeExpr* midwordp = // SEL(from,[midwordnum]) - newWordSel(ffl, nodep->fromp()->cloneTreePure(true), midMsbp, 0); - // newWordSel clones the index, so delete it - VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); + AstNodeExpr* midwordp = newWordSelBit(ffl, nodep->fromp(), midMsbp, 0); + if (!midMsbp->backp()) VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); if (nodep->isQuad() && !midwordp->isQuad()) { midwordp = new AstCCast{nfl, midwordp, nodep}; } @@ -455,10 +496,8 @@ class ExpandVisitor final : public VNVisitor { const uint32_t hiMsbOffset = nodep->widthConst() - 1; AstNodeExpr* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, nodep->lsbp()->cloneTreePure(true)}; - AstNodeExpr* hiwordp = // SEL(from,[hiwordnum]) - newWordSel(ffl, nodep->fromp()->cloneTreePure(true), hiMsbp); - // newWordSel clones the index, so delete it - VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); + AstNodeExpr* hiwordp = newWordSelBit(ffl, nodep->fromp(), hiMsbp); + if (!hiMsbp->backp()) VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); if (nodep->isQuad() && !hiwordp->isQuad()) { hiwordp = new AstCCast{nfl, hiwordp, nodep}; } @@ -497,40 +536,109 @@ class ExpandVisitor final : public VNVisitor { bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) { UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch"); if (!doExpandWide(nodep)) return false; - if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) { - const int lsb = rhsp->lsbConst(); - UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep); - for (int w = 0; w < nodep->widthWords(); ++w) { - addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_E(lsb))); + + // Simplify the index, in case it becomes a constant + V3Const::constifyEditCpp(rhsp->lsbp()); + + // If it's a constant select and aligned, we can just copy the words + if (AstConst* const lsbConstp = VN_CAST(rhsp->lsbp(), Const)) { + const uint32_t lsb = lsbConstp->toUInt(); + if (VL_BITBIT_E(lsb) == 0) { + UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep); + const uint32_t word = VL_BITWORD_E(lsb); + for (int w = 0; w < nodep->widthWords(); ++w) { + addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + word)); + } + return true; } - return true; - } else { - UINFO(8, " Wordize ASSIGN(EXTRACT,misalign) " << nodep); - FileLine* const nfl = nodep->fileline(); - FileLine* const rfl = rhsp->fileline(); - FileLine* const ffl = rhsp->fromp()->fileline(); - FileLine* const lfl = rhsp->lsbp()->fileline(); - for (int w = 0; w < nodep->widthWords(); ++w) { - // Grab lowest bits - AstNodeExpr* const lowwordp - = newWordSel(rfl, rhsp->fromp()->cloneTreePure(true), rhsp->lsbp(), w); - AstNodeExpr* const lowp - = new AstShiftR{rfl, lowwordp, newSelBitBit(rhsp->lsbp()), VL_EDATASIZE}; - // Upper bits - const V3Number zero{nodep, VL_EDATASIZE, 0}; - AstNodeExpr* const midwordp = // SEL(from,[1+wordnum]) - newWordSel(ffl, rhsp->fromp()->cloneTreePure(true), rhsp->lsbp(), w + 1); - AstNodeExpr* const midshiftp - = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, newSelBitBit(rhsp->lsbp())}; - AstNodeExpr* const midmayp = new AstShiftL{rfl, midwordp, midshiftp, VL_EDATASIZE}; - AstNodeExpr* const midp = new AstCond{ - rfl, new AstEq{rfl, new AstConst{rfl, 0}, newSelBitBit(rhsp->lsbp())}, - new AstConst{rfl, zero}, midmayp}; - AstNodeExpr* const newp = new AstOr{nfl, midp, lowp}; - addWordAssign(nodep, w, newp); - } - return true; } + + UINFO(8, " Wordize ASSIGN(EXTRACT,misalign) " << nodep); + FileLine* const flp = rhsp->fileline(); + + // Use fresh set of temporaries + ++m_nTmps; + + // Compute word index of LSB, store to temporary if not constant + AstNodeExpr* wordIdxp = newWordIndex(rhsp->lsbp()->cloneTreePure(false)); + wordIdxp = V3Const::constifyEditCpp(wordIdxp); + if (!VN_IS(wordIdxp, Const)) { + AstVar* const tmpp = addLocalTmp(nodep, "ExpandSel_WordIdx", wordIdxp); + wordIdxp = new AstVarRef{flp, tmpp, VAccess::READ}; + } + + // Compute shift amounts and mask, store to temporaries if not constants + AstNodeExpr* loShftp = nullptr; + AstNodeExpr* hiShftp = nullptr; + AstNodeExpr* hiMaskp = nullptr; + if (AstConst* const lsbConstp = VN_CAST(rhsp->lsbp(), Const)) { + const uint32_t bitOffset = VL_BITBIT_E(lsbConstp->toUInt()); + // Must be unaligned, otherwise we would have handled it above + UASSERT_OBJ(bitOffset, nodep, "Missed aligned wide select"); + loShftp = new AstConst{flp, bitOffset}; + hiShftp = new AstConst{flp, VL_EDATASIZE - bitOffset}; + hiMaskp = nullptr; + } else { + // Compute Low word shift amount: bottom bits of lsb index + AstNodeExpr* const lsbp = rhsp->lsbp()->cloneTreePure(false); + loShftp = new AstAnd{flp, new AstConst{flp, VL_SIZEBITS_E}, lsbp}; + AstVar* const loTmpp = addLocalTmp(nodep, "ExpandSel_LoShift", loShftp); + loShftp = new AstVarRef{flp, loTmpp, VAccess::READ}; + + // Compute if aligned: Low word shift amount is 0 + AstNodeExpr* const zerop = new AstConst{flp, 0}; + AstNodeExpr* alignedp = new AstEq{flp, zerop, loShftp->cloneTreePure(false)}; + AstVar* const alignedTmpp = addLocalTmp(nodep, "ExpandSel_Aligned", alignedp); + alignedp = new AstVarRef{flp, alignedTmpp, VAccess::READ}; + + // Computed High word shift amount: 0 if aligned else VL_EDATASIZE - Low word shift + AstNodeExpr* const edsp = new AstConst{flp, VL_EDATASIZE}; + AstNodeExpr* const subp = new AstSub{flp, edsp, loShftp->cloneTreePure(false)}; + hiShftp = new AstCond{flp, alignedp, new AstConst{flp, 0}, subp}; + AstVar* const hiTmpp = addLocalTmp(nodep, "ExpandSel_HiShift", hiShftp); + hiShftp = new AstVarRef{flp, hiTmpp, VAccess::READ}; + + // Compute the High word mask: 0 if aligned, ones otherwise + hiMaskp = new AstCond{flp, alignedp->cloneTreePure(false), + new AstConst{flp, AstConst::SizedEData{}, 0}, + new AstConst{flp, AstConst::SizedEData{}, -1ULL}}; + AstVar* const maskTmpp = addLocalTmp(nodep, "ExpandSel_HiMask", hiMaskp); + hiMaskp = new AstVarRef{flp, maskTmpp, VAccess::READ}; + } + + // Create each word of the selected result + AstNodeExpr* const fromp = rhsp->fromp(); + const int selWords = nodep->widthWords(); + for (int w = 0; w < selWords; ++w) { + // Grab bits from word 'VL_BITWORD_E(lsb) + w' + AstNodeExpr* const loWordp = newWordSelWord(fromp->fileline(), fromp, wordIdxp, w); + AstNodeExpr* const loShftClonep = loShftp->cloneTreePure(false); + AstNodeExpr* const lop = new AstShiftR{flp, loWordp, loShftClonep, VL_EDATASIZE}; + // Grab bits from word 'VL_BITWORD_E(lsb) + w + 1' + AstNodeExpr* hiWordp = newWordSelWord(fromp->fileline(), fromp, wordIdxp, w + 1); + // For the last word of the result, avoid an OOB access when the Sel is not OOB + if (w == selWords - 1) { + const uint32_t max = fromp->widthWords() - w - 1; + AstNodeExpr* const maxp = new AstConst{flp, max}; + AstNodeExpr* const condp = new AstGte{flp, wordIdxp->cloneTreePure(false), maxp}; + AstNodeExpr* const zerop = new AstConst{flp, AstConst::SizedEData{}, 0}; + hiWordp = new AstCond{flp, condp, zerop, hiWordp}; + } + AstNodeExpr* const hiShftClonep = hiShftp->cloneTreePure(false); + AstNodeExpr* const hiPartp = new AstShiftL{flp, hiWordp, hiShftClonep, VL_EDATASIZE}; + AstNodeExpr* const hip + = hiMaskp ? new AstAnd{flp, hiPartp, hiMaskp->cloneTreePure(false)} : hiPartp; + // Combine them + addWordAssign(nodep, w, new AstOr{nodep->fileline(), hip, lop}); + } + + // Delete parts not captured during construction + if (!wordIdxp->backp()) VL_DO_DANGLING(wordIdxp->deleteTree(), wordIdxp); + if (!loShftp->backp()) VL_DO_DANGLING(loShftp->deleteTree(), loShftp); + if (!hiShftp->backp()) VL_DO_DANGLING(hiShftp->deleteTree(), hiShftp); + if (hiMaskp && !hiMaskp->backp()) VL_DO_DANGLING(hiMaskp->deleteTree(), hiMaskp); + + return true; } bool expandLhs(AstNodeAssign* nodep, AstSel* lhsp) { @@ -614,7 +722,7 @@ class ExpandVisitor final : public VNVisitor { UINFO(8, " ASSIGNSEL(varlsb,wide,1bit) " << nodep); AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const destp = lhsp->fromp()->unlinkFrBack(); - AstNodeExpr* oldvalp = newWordSel(lfl, destp->cloneTreePure(true), lhsp->lsbp()); + AstNodeExpr* oldvalp = newWordSelBit(lfl, destp, lhsp->lsbp()); fixCloneLvalue(oldvalp); if (!ones) { oldvalp = new AstAnd{ @@ -631,7 +739,7 @@ class ExpandVisitor final : public VNVisitor { AstNodeExpr* const shiftp = new AstAnd{nfl, lhsp->lsbp()->cloneTreePure(true), new AstConst{nfl, VL_EDATASIZE - 1}}; AstNode* const newp = new AstAssign{ - nfl, newWordSel(nfl, destp, lhsp->lsbp()), + nfl, newWordSelBit(nfl, destp, lhsp->lsbp()), new AstOr{lfl, oldvalp, new AstShiftL{lfl, rhsp, shiftp, VL_EDATASIZE}}}; insertBefore(nodep, newp); return true; diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index f86e4fa8c..f3fcf9bcb 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -118,15 +118,21 @@ public: } // ACCESSORS AstNodeExpr* substWhole(AstNode* errp) { - if (!m_varp->isWide() && !m_whole.m_complex && m_whole.m_assignp && !m_wordAssign) { - const AstNodeAssign* const assp = m_whole.m_assignp; - UASSERT_OBJ(assp, errp, "Reading whole that was never assigned"); - // AstCvtPackedToArray can't be anywhere else than on the RHS of assignment - if (VN_IS(assp->rhsp(), CvtPackedToArray)) return nullptr; - return assp->rhsp(); - } else { - return nullptr; - } + if (m_varp->isWide()) return nullptr; + if (m_whole.m_complex) return nullptr; + if (!m_whole.m_assignp) return nullptr; + if (m_wordAssign) return nullptr; + + const AstNodeAssign* const assp = m_whole.m_assignp; + UASSERT_OBJ(assp, errp, "Reading whole that was never assigned"); + AstNodeExpr* const rhsp = assp->rhsp(); + + // AstCvtPackedToArray can't be anywhere else than on the RHS of assignment + if (VN_IS(rhsp, CvtPackedToArray)) return nullptr; + // Check if only substitute if constant + if (m_varp->substConstOnly() && !VN_IS(rhsp, Const)) return nullptr; + // Substitute it + return rhsp; } // Return what to substitute given word number for AstNodeExpr* substWord(AstNode* errp, int word) { @@ -227,7 +233,7 @@ class SubstVisitor final : public VNVisitor { int m_ops = 0; // Number of operators on assign rhs int m_assignStep = 0; // Assignment number to determine var lifetime const AstCFunc* m_funcp = nullptr; // Current function we are under - VDouble0 m_statSubsts; // Statistic tracking + size_t m_nSubst = 0; // Number of substitutions performed enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in @@ -251,7 +257,10 @@ class SubstVisitor final : public VNVisitor { VL_RESTORER(m_ops); m_ops = 0; m_assignStep++; + const size_t nSubstBefore = m_nSubst; iterateAndNextNull(nodep->rhsp()); + if (nSubstBefore != m_nSubst) V3Const::constifyEditCpp(nodep->rhsp()); + if (VN_IS(nodep->rhsp(), Const)) m_ops = 0; bool hit = false; if (AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) { if (isSubstVar(varrefp->varp())) { @@ -292,11 +301,14 @@ class SubstVisitor final : public VNVisitor { UINFOTREE(6, newp, "", "w_new"); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); - ++m_statSubsts; + ++m_nSubst; } void visit(AstWordSel* nodep) override { if (!m_funcp) return; + const size_t nSubstBefore = m_nSubst; iterate(nodep->bitp()); + // Simplify in case it was substituted and became constant + if (nSubstBefore != m_nSubst) V3Const::constifyEditCpp(nodep->bitp()); AstVarRef* const varrefp = VN_CAST(nodep->fromp(), VarRef); const AstConst* const constp = VN_CAST(nodep->bitp(), Const); if (varrefp && isSubstVar(varrefp->varp()) && varrefp->access().isReadOnly() && constp) { @@ -380,7 +392,7 @@ public: // CONSTRUCTORS explicit SubstVisitor(AstNode* nodep) { iterate(nodep); } ~SubstVisitor() override { - V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); + V3Stats::addStat("Optimizations, Substituted temps", m_nSubst); UASSERT(m_entries.empty(), "References outside functions"); } }; From 3770273637bb431bad3785cf7c79e723a9590fd6 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 30 Aug 2025 15:35:16 +0100 Subject: [PATCH 247/252] Optimize logic in non-virtual interfaces with DFG (#6347) --- src/V3DfgAstToDfg.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 57e310df7..18df9a1b0 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -233,6 +233,13 @@ class AstToDfgVisitor final : public VNVisitor { // Containers to descend through to find logic constructs void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); } void visit(AstModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); } + void visit(AstIface* nodep) override { + if (!nodep->hasVirtualRef()) { + iterateAndNextNull(nodep->stmtsp()); + } else { + markReferenced(nodep); + } + } void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); } void visit(AstScope* nodep) override { iterateChildren(nodep); } void visit(AstActive* nodep) override { From b0044da8a7a5d1bbd3e71870927ad18e2fafe163 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 30 Aug 2025 16:08:44 +0100 Subject: [PATCH 248/252] Internals: Upload published nightly RTLMeter results artifact (#6346) First half of #6301 --- .github/workflows/reusable-rtlmeter-run.yml | 6 +- .github/workflows/rtlmeter.yml | 106 ++++++++++++++++---- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index ba8d45199..259fe1042 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -7,6 +7,10 @@ name: reusable-rtlmeter-run on: workflow_call: inputs: + tag: + description: "Unique identifier for storing results" + type: string + required: true runs-on: description: "Runner to use, e.g.: ubuntu-24.04" type: string @@ -113,7 +117,7 @@ jobs: uses: actions/upload-artifact@v4 with: path: results-${{ steps.results.outputs.hash }}.json - name: rtlmeter-results-${{ steps.results.outputs.hash }} + name: rtlmeter-results-${{ inputs.tag }}-${{ steps.results.outputs.hash }} overwrite: true retention-days: 2 diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index f7135084d..d60bfeb1b 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -66,6 +66,7 @@ jobs: needs: build-gcc uses: ./.github/workflows/reusable-rtlmeter-run.yml with: + tag: gcc runs-on: ubuntu-24.04 cc: gcc cases: ${{ matrix.cases }} @@ -107,6 +108,7 @@ jobs: needs: build-clang uses: ./.github/workflows/reusable-rtlmeter-run.yml with: + tag: clang runs-on: ubuntu-24.04 cc: clang cases: ${{ matrix.cases }} @@ -150,29 +152,40 @@ jobs: # That is: do not run if all skipped, or the workflow was cancelled. if: ${{ (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure')) && !cancelled() }} runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + tag: + - gcc + - clang steps: + - name: Checkout RTLMeter + uses: actions/checkout@v4 + with: + repository: "verilator/rtlmeter" + path: rtlmeter + - name: Setup RTLMeter venv + working-directory: rtlmeter + run: make venv - name: Download all results uses: actions/download-artifact@v4 with: - pattern: rtlmeter-results-* - path: all-results-${{ github.run_id }} + pattern: rtlmeter-results-${{ matrix.tag }}-* + path: all-results-${{ matrix.tag }} merge-multiple: true - - name: Tar up all results into single archive + - name: Combine results + working-directory: rtlmeter run: | - # Ensure combined result directory exists in case of no results - mkdir -p all-results-${{ github.run_id }} - ls -la all-results-${{ github.run_id }} - # Tar up the results directory - tar --posix -c -z -f all-results-${{ github.run_id }}.tar.gz all-results-${{ github.run_id }} + ./rtlmeter collate ../all-results-${{ matrix.tag }}/*.json > ../all-results-${{ matrix.tag }}.json - name: Upload combined results uses: actions/upload-artifact@v4 with: - path: all-results-${{ github.run_id }}.tar.gz - name: all-results + path: all-results-${{ matrix.tag }}.json + name: all-results-${{ matrix.tag }} overwrite: true retention-days: 30 - publish-results: + publish-scheduled-results: name: Publish results to verilator/verilator-rtlmeter-results needs: combine-results # Only run on scheduled builds on the main repository. We also restrict @@ -186,20 +199,21 @@ jobs: - name: Download combined results uses: actions/download-artifact@v4 with: - name: all-results + pattern: all-results-* path: results - - name: Extract combined results - working-directory: results - run: | - tar xzfv all-results-${{ github.run_id }}.tar.gz - ls -la + merge-multiple: true + - name: Upload published results + uses: actions/upload-artifact@v4 + with: + path: results/*.json + name: published-results # Pushing to verilator/verilator-rtlmeter-results requires elevated permissions - name: Generate access token id: generate-token uses: actions/create-github-app-token@v2.0.6 with: - app-id: ${{ vars.RTLMETER_RESULTS_CI_APP_ID }} - private-key: ${{ secrets.RTLMETER_RESULTS_CI_APP_PRIVATE_KEY }} + app-id: ${{ vars.VERILATOR_CI_ID }} + private-key: ${{ secrets.VERILATOR_CI_KEY }} owner: verilator repositories: verilator-rtlmeter-results permission-contents: write @@ -228,11 +242,59 @@ jobs: git commit -m "Verilator CI: Results of 'RTLMeter' workflow run #${{ github.run_number }}" git push origin + #publish-pr-results: + # name: Publish results to Pull Request + # needs: combine-results + # if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && contains(needs.*.result, 'success') && !cancelled() }} + # runs-on: ubuntu-24.04 + # steps: + # - name: Checkout RTLMeter + # uses: actions/checkout@v4 + # with: + # repository: "verilator/rtlmeter" + # path: rtlmeter + # - name: Setup RTLMeter venv + # working-directory: rtlmeter + # run: make venv + # - name: Download combined results + # uses: actions/download-artifact@v4 + # with: + # pattern: all-results-* + # path: all-results + # merge-multiple: true + # - name: Get scheduled run info + # id: scheduled-info + # run: + # IDLATEST=$(gh run list --workflow RTLMeter --event schedule --status success --limit 1 --json databaseId --jq ".[0].databaseId") + # echo "id=$IDLATEST" >> $GITHUB_OUTPUT + # URL=$(gh run view $IDLATEST --json url --jq ".url") + # echo "url=$URL" >> $GITHUB_OUTPUT + # NUM=$(gh run view $IDLATEST --json number --jq ".number") + # echo "num=$NUM" >> $GITHUB_OUTPUT + # # Fetching artifacts from different workflow requires elevated privilege + # - name: Generate access token + # id: generate-token + # uses: actions/create-github-app-token@v2.0.6 + # with: + # app-id: ${{ vars.VERILATOR_CI_ID }} + # private-key: ${{ secrets.VERILATOR_CI_KEY }} + # owner: verilator + # repositories: verilator + # permission-actions: read + # - name: Download scheduled run results + # uses: actions/download-artifact@v4 + # with: + # name: published-results + # path: nightly-results + # run-id: ${{ steps.schedueld-info.outputs.id }} + # github-token: ${{ steps.generate-token.outputs.token }} + # # TODO: diff and present + # Create GitHub issue for failed schedueld jobs # This should always be the last job (we want an issue if anything breaks) create-issue: name: Create issue on failure - needs: publish-results + needs: publish-scheduled-results if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} runs-on: ubuntu-24.04 steps: @@ -241,8 +303,8 @@ jobs: id: generate-token uses: actions/create-github-app-token@v2.0.6 with: - app-id: ${{ vars.RTLMETER_RESULTS_CI_APP_ID }} - private-key: ${{ secrets.RTLMETER_RESULTS_CI_APP_PRIVATE_KEY }} + app-id: ${{ vars.VERILATOR_CI_ID }} + private-key: ${{ secrets.VERILATOR_CI_KEY }} owner: verilator repositories: verilator permission-issues: write From ca2c40347b775b9b05504791533f84314d873f3a Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 31 Aug 2025 02:25:20 +0800 Subject: [PATCH 249/252] Fix undefined weak link for Apple GCC etc (#6348) --- docs/CONTRIBUTORS | 1 + verilator-config.cmake.in | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index ba0af2f62..c9ead87f7 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -35,6 +35,7 @@ Chris Randall Christopher Taylor Chuxuan Wang Chykon +Congcong Cai Conor McCullough Dan Petrisko Danny Oler diff --git a/verilator-config.cmake.in b/verilator-config.cmake.in index 6214c02d4..db4d4dbc5 100644 --- a/verilator-config.cmake.in +++ b/verilator-config.cmake.in @@ -119,7 +119,7 @@ if(NOT CMAKE_CXX_COMPILER_ID MATCHES MSVC) endif() endif() -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") +if(Apple) add_link_options(-Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv) endif() From 7bb38d21b986c26392d3d647ae7a22e473e813e3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 17:58:12 -0400 Subject: [PATCH 250/252] Commentary: Changes update --- Changes | 4 ++++ docs/spelling.txt | 1 + 2 files changed, 5 insertions(+) diff --git a/Changes b/Changes index 551cba559..9a8e1aebb 100644 --- a/Changes +++ b/Changes @@ -57,6 +57,7 @@ Verilator 5.039 devel * Optimize acyclic DFG components into the original acyclic sub-graph. (#6261). [Geza Lore] * Optimize multiplexers in DFG synthesis (#6331). [Geza Lore] * Optimize interfaces in DFG (#6332). [Geza Lore] +* Optimize logic in non-virtual interfaces with DFG (#6347). [Geza Lore] * Fix loop initialization visibility outside loop (#4237). * Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica] * Fix replicate of negative giving 'REPLICATE has no expected width' internal error (#6048) (#6229). @@ -107,6 +108,9 @@ Verilator 5.039 devel * Fix to select UDPs when they are the only candidate for a top module. * Fix splitting of assignments to SC variables (#6329) (#6336). [Geza Lore] * Fix to localize for super constructors with function calls as arguments (#6330). [Igor Zaworski, Antmicro Ltd.] +* Fix wide select expansion and substitution (#6341) (#6345). [Geza Lore] +* Fix upcasting class type parameters (#6344). [Krzysztof Bieganski, Antmicro Ltd.] +* Fix undefined weak link for Apple GCC etc (#6348). [Congcong Cai] Verilator 5.038 2025-07-08 diff --git a/docs/spelling.txt b/docs/spelling.txt index 3f35bb844..99593957d 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -1113,6 +1113,7 @@ unroller unsized unsup untyped +upcasting urandom uselib utimes From 6a14facf4800ef9ed2aa30cd51ad19451160d669 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 18:10:13 -0400 Subject: [PATCH 251/252] Tests: Fix version miscompare in t_uvm_dpi --- test_regress/t/t_uvm_dpi.out | 1 - test_regress/t/t_uvm_dpi.v | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_regress/t/t_uvm_dpi.out b/test_regress/t/t_uvm_dpi.out index 37b3ebd24..c79bab699 100644 --- a/test_regress/t/t_uvm_dpi.out +++ b/test_regress/t/t_uvm_dpi.out @@ -1,5 +1,4 @@ uvm_dpi_get_tool_name_c() = Verilator -uvm_dpi_get_tool_version_c() = 5.039 devel = uvm_re = uvm_hdl_check_path = uvm_hdl_read simple variable diff --git a/test_regress/t/t_uvm_dpi.v b/test_regress/t/t_uvm_dpi.v index 4c44dc9cf..b747562d2 100644 --- a/test_regress/t/t_uvm_dpi.v +++ b/test_regress/t/t_uvm_dpi.v @@ -44,7 +44,8 @@ module t; `checks(s, "Verilator"); s = uvm_dpi_get_tool_version_c(); - $display("uvm_dpi_get_tool_version_c() = %s", s); + // - is so doesn't compare in .out file, in case version changes + $display("- uvm_dpi_get_tool_version_c() = %s", s); if (s == "") $stop; //===== Regexp From 51cdf52d0f3a3e59b51fd87762646a851e9f4aff Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 30 Aug 2025 18:11:51 -0400 Subject: [PATCH 252/252] Version bump --- CMakeLists.txt | 2 +- Changes | 2 +- configure.ac | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 502871301..50d1db3bd 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.039 + VERSION 5.040 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index 9a8e1aebb..c23fff5bf 100644 --- a/Changes +++ b/Changes @@ -8,7 +8,7 @@ The changes in each Verilator version are described below. The contributors that suggested or implemented a given issue are shown in []. Thanks! -Verilator 5.039 devel +Verilator 5.040 2025-08-30 ========================== **Other:** diff --git a/configure.ac b/configure.ac index 5e9f2458d..9a4899ec5 100644 --- a/configure.ac +++ b/configure.ac @@ -7,10 +7,11 @@ # When releasing, also update header of Changes file, and CMakeLists.txt, # and commit using "devel release" or "Version bump" message +# Then 'make distclean' 'autoconf' './configure' 'make' 'make test' # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.039 devel], +AC_INIT([Verilator],[5.040 2025-08-30], [https://verilator.org], [verilator],[https://verilator.org])