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