Fix release of forced port nets (#7907) (#7901)

Fixes# 7901

Signed-off-by: Artur Bieniek <abieniek@antmicro.com>
This commit is contained in:
Artur Bieniek 2026-07-10 12:35:06 +02:00 committed by GitHub
parent b211af9d24
commit feb2fe0d94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 147 additions and 11 deletions

View File

@ -679,8 +679,9 @@ void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) {
// the port variable. The constant can still be inlined, in which case // the port variable. The constant can still be inlined, in which case
// this is needed for tracing the inlined port variable. // this is needed for tracing the inlined port variable.
if (AstConst* const pinp = VN_CAST(pinExprp, Const)) { if (AstConst* const pinp = VN_CAST(pinExprp, Const)) {
AstAssignW* const ap AstVarRef* const lhsp = portRef(VAccess::WRITE);
= new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)}; lhsp->varp()->isContinuously(true);
AstAssignW* const ap = new AstAssignW{flp, lhsp, pinp->cloneTree(false)};
modp->addStmtsp(new AstAlways{ap}); modp->addStmtsp(new AstAlways{ap});
return; 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 // Otherwise create the continuous assignment between the port var and the pin expression
UINFO(6, "Not inlining port variable: " << nodep); UINFO(6, "Not inlining port variable: " << nodep);
if (nodep->direction() == VDirection::INPUT) { if (nodep->direction() == VDirection::INPUT) {
AstAssignW* const ap AstVarRef* const lhsp = portRef(VAccess::WRITE);
= new AstAssignW{flp, portRef(VAccess::WRITE), pinRefAsExpr(VAccess::READ)}; lhsp->varp()->isContinuously(true);
AstAssignW* const ap = new AstAssignW{flp, lhsp, pinRefAsExpr(VAccess::READ)};
modp->addStmtsp(new AstAlways{ap}); modp->addStmtsp(new AstAlways{ap});
} else if (nodep->direction() == VDirection::OUTPUT) { } else if (nodep->direction() == VDirection::OUTPUT) {
AstAssignW* const ap AstNodeVarRef* const lhsp = VN_AS(pinRefAsExpr(VAccess::WRITE), NodeVarRef);
= new AstAssignW{flp, pinRefAsExpr(VAccess::WRITE), portRef(VAccess::READ)}; lhsp->varp()->isContinuously(true);
AstAssignW* const ap = new AstAssignW{flp, lhsp, portRef(VAccess::READ)};
modp->addStmtsp(new AstAlways{ap}); modp->addStmtsp(new AstAlways{ap});
} else { } else {
pinExprp->v3fatalSrc("V3Tristate left INOUT port"); pinExprp->v3fatalSrc("V3Tristate left INOUT port");

View File

@ -29,6 +29,12 @@
VL_DEFINE_DEBUG_FUNCTIONS; 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 // Inst state, as a visitor of each AstNode
@ -73,16 +79,17 @@ class InstVisitor final : public VNVisitor {
} else if (nodep->modVarp()->isWritable()) { } else if (nodep->modVarp()->isWritable()) {
AstNodeExpr* const rhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(), AstNodeExpr* const rhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(),
m_cellp->name(), VAccess::READ}; m_cellp->name(), VAccess::READ};
markContinuousLhs(exprp);
AstAssignW* const assp = new AstAssignW{exprp->fileline(), exprp, rhsp}; AstAssignW* const assp = new AstAssignW{exprp->fileline(), exprp, rhsp};
m_cellp->addNextHere(new AstAlways{assp}); m_cellp->addNextHere(new AstAlways{assp});
} else if (nodep->modVarp()->isNonOutput()) { } else if (nodep->modVarp()->isNonOutput()) {
// Don't bother moving constants now, // Don't bother moving constants now,
// we'll be pushing the const down to the cell soon enough. // we'll be pushing the const down to the cell soon enough.
AstAssignW* const assp AstVarXRef* const lhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(),
= new AstAssignW{exprp->fileline(), m_cellp->name(), VAccess::WRITE};
new AstVarXRef{exprp->fileline(), nodep->modVarp(),
m_cellp->name(), VAccess::WRITE}, markContinuousLhs(lhsp);
exprp}; AstAssignW* const assp = new AstAssignW{exprp->fileline(), lhsp, exprp};
m_cellp->addNextHere(new AstAlways{assp}); m_cellp->addNextHere(new AstAlways{assp});
UINFOTREE(9, assp, "", "_new"); UINFOTREE(9, assp, "", "_new");
} else if (nodep->modVarp()->isIfaceRef() } else if (nodep->modVarp()->isIfaceRef()
@ -903,9 +910,11 @@ public:
rhsp = extendOrSel(pinp->fileline(), rhsp, pinVarp); rhsp = extendOrSel(pinp->fileline(), rhsp, pinVarp);
pinp->exprp(new AstVarRef{newvarp->fileline(), newvarp, VAccess::WRITE}); pinp->exprp(new AstVarRef{newvarp->fileline(), newvarp, VAccess::WRITE});
AstNodeExpr* const rhsSelp = extendOrSel(pinp->fileline(), rhsp, pinexprp); AstNodeExpr* const rhsSelp = extendOrSel(pinp->fileline(), rhsp, pinexprp);
markContinuousLhs(pinexprp);
assignp = new AstAssignW{pinp->fileline(), pinexprp, rhsSelp}; assignp = new AstAssignW{pinp->fileline(), pinexprp, rhsSelp};
} else { } else {
// V3 width should have range/extended to make the widths correct // V3 width should have range/extended to make the widths correct
newvarp->isContinuously(true);
assignp = new AstAssignW{pinp->fileline(), assignp = new AstAssignW{pinp->fileline(),
new AstVarRef{pinp->fileline(), newvarp, VAccess::WRITE}, new AstVarRef{pinp->fileline(), newvarp, VAccess::WRITE},
pinexprp}; pinexprp};

View File

@ -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()

View File

@ -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

View File

@ -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()