diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 7b0a4ecd3..2dd934f54 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -679,8 +679,9 @@ void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) { // 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)) { - AstAssignW* const ap - = new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)}; + AstVarRef* const lhsp = portRef(VAccess::WRITE); + lhsp->varp()->isContinuously(true); + AstAssignW* const ap = new AstAssignW{flp, lhsp, pinp->cloneTree(false)}; modp->addStmtsp(new AstAlways{ap}); return; } @@ -732,12 +733,14 @@ void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) { // Otherwise create the continuous assignment between the port var and the pin expression UINFO(6, "Not inlining port variable: " << nodep); if (nodep->direction() == VDirection::INPUT) { - AstAssignW* const ap - = new AstAssignW{flp, portRef(VAccess::WRITE), pinRefAsExpr(VAccess::READ)}; + AstVarRef* const lhsp = portRef(VAccess::WRITE); + lhsp->varp()->isContinuously(true); + AstAssignW* const ap = new AstAssignW{flp, lhsp, pinRefAsExpr(VAccess::READ)}; modp->addStmtsp(new AstAlways{ap}); } else if (nodep->direction() == VDirection::OUTPUT) { - AstAssignW* const ap - = new AstAssignW{flp, pinRefAsExpr(VAccess::WRITE), portRef(VAccess::READ)}; + AstNodeVarRef* const lhsp = VN_AS(pinRefAsExpr(VAccess::WRITE), NodeVarRef); + lhsp->varp()->isContinuously(true); + AstAssignW* const ap = new AstAssignW{flp, lhsp, portRef(VAccess::READ)}; modp->addStmtsp(new AstAlways{ap}); } else { pinExprp->v3fatalSrc("V3Tristate left INOUT port"); diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 43cea12ef..befe8b05b 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -29,6 +29,12 @@ VL_DEFINE_DEBUG_FUNCTIONS; +static void markContinuousLhs(AstNode* const nodep) { + nodep->foreach([](AstNodeVarRef* refp) { + if (refp->access().isWriteOrRW()) refp->varp()->isContinuously(true); + }); +} + //###################################################################### // Inst state, as a visitor of each AstNode @@ -73,16 +79,17 @@ class InstVisitor final : public VNVisitor { } else if (nodep->modVarp()->isWritable()) { AstNodeExpr* const rhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(), m_cellp->name(), VAccess::READ}; + markContinuousLhs(exprp); AstAssignW* const assp = new AstAssignW{exprp->fileline(), exprp, rhsp}; m_cellp->addNextHere(new AstAlways{assp}); } else if (nodep->modVarp()->isNonOutput()) { // Don't bother moving constants now, // we'll be pushing the const down to the cell soon enough. - AstAssignW* const assp - = new AstAssignW{exprp->fileline(), - new AstVarXRef{exprp->fileline(), nodep->modVarp(), - m_cellp->name(), VAccess::WRITE}, - exprp}; + AstVarXRef* const lhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(), + m_cellp->name(), VAccess::WRITE}; + + markContinuousLhs(lhsp); + AstAssignW* const assp = new AstAssignW{exprp->fileline(), lhsp, exprp}; m_cellp->addNextHere(new AstAlways{assp}); UINFOTREE(9, assp, "", "_new"); } else if (nodep->modVarp()->isIfaceRef() @@ -903,9 +910,11 @@ public: rhsp = extendOrSel(pinp->fileline(), rhsp, pinVarp); pinp->exprp(new AstVarRef{newvarp->fileline(), newvarp, VAccess::WRITE}); AstNodeExpr* const rhsSelp = extendOrSel(pinp->fileline(), rhsp, pinexprp); + markContinuousLhs(pinexprp); assignp = new AstAssignW{pinp->fileline(), pinexprp, rhsSelp}; } else { // V3 width should have range/extended to make the widths correct + newvarp->isContinuously(true); assignp = new AstAssignW{pinp->fileline(), new AstVarRef{pinp->fileline(), newvarp, VAccess::WRITE}, pinexprp}; diff --git a/test_regress/t/t_force_immediate_release_port_net.py b/test_regress/t/t_force_immediate_release_port_net.py new file mode 100755 index 000000000..5eb833c16 --- /dev/null +++ b/test_regress/t/t_force_immediate_release_port_net.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary --flatten"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_immediate_release_port_net.v b/test_regress/t/t_force_immediate_release_port_net.v new file mode 100644 index 000000000..6e755a163 --- /dev/null +++ b/test_regress/t/t_force_immediate_release_port_net.v @@ -0,0 +1,87 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// 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) +// verilog_format: on + +module t; + logic [31:0] b = 0; + logic [31:0] d = 32'h1111_2222; + wire [31:0] e; + wire [7:0] f; + wire [31:0] g; + logic [4:0] idx = 0; + sub s(b); + sub8 s_sel(d[7:0]); + sub8 s_const(8'h7c); + subout s_out(e[idx+:8]); + subout s_out_simple(f); + subout s_out_slice(g[15:8]); + + initial begin + #1; + + force s.c = 2; + `checkh(s.c, 2); + release s.c; + `checkh(s.c, 0); + + `checkh(s_sel.c, 8'h22); + force s_sel.c = 8'h33; + `checkh(s_sel.c, 8'h33); + release s_sel.c; + `checkh(s_sel.c, 8'h22); + + `checkh(s_const.c, 8'h7c); + force s_const.c = 8'ha5; + `checkh(s_const.c, 8'ha5); + release s_const.c; + `checkh(s_const.c, 8'h7c); + + `checkh(s_out_simple.c, 8'h5a); + force s_out_simple.c = 8'hc3; + `checkh(s_out_simple.c, 8'hc3); + release s_out_simple.c; + `checkh(s_out_simple.c, 8'h5a); + + `checkh(f, 8'h5a); + force f = 8'ha5; + `checkh(f, 8'ha5); + release f; + `checkh(f, 8'h5a); + + `checkh(g[15:8], 8'h5a); + force g[15:8] = 8'ha5; + `checkh(g[15:8], 8'ha5); + release g[15:8]; + `checkh(g[15:8], 8'h5a); + + force d = 32'h1234_5678; + `checkh(d, 32'h1234_5678); + release d; + `checkh(d, 32'h1234_5678); + + idx = 5'd4; + force idx = 5'd8; + `checkh(idx, 5'd8); + release idx; + `checkh(idx, 5'd8); + $finish; + end + +endmodule + +module sub(input logic [31:0] c); +endmodule + +module sub8(input logic [7:0] c); +endmodule + +module subout(output logic [7:0] c); + assign c = 8'h5a; +endmodule diff --git a/test_regress/t/t_force_immediate_release_port_net_noinline.py b/test_regress/t/t_force_immediate_release_port_net_noinline.py new file mode 100755 index 000000000..2ce6b5216 --- /dev/null +++ b/test_regress/t/t_force_immediate_release_port_net_noinline.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') +test.top_filename = "t_force_immediate_release_port_net.v" + +test.compile(verilator_flags2=["--binary -fno-inline"]) + +test.execute() + +test.passes()