From 4d5393c1918d62f2800a1c7504c003e97c3ef0c2 Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:51:49 -0400 Subject: [PATCH] Fix tristate lowering for interface inout ports (#7134 repair) (#7708) (#7710) Fixes #7708. --- src/V3Tristate.cpp | 4 +- test_regress/t/t_tri_iface_port.py | 16 ++++++++ test_regress/t/t_tri_iface_port.v | 65 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_tri_iface_port.py create mode 100644 test_regress/t/t_tri_iface_port.v diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index ea1180588..6fa174ed0 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -739,7 +739,7 @@ class TristateVisitor final : public TristateBaseVisitor { // Check if the var is owned by a different module (cross-module reference). // For interface vars this is expected; for regular modules it's unsupported. AstNodeModule* const ownerModp = findParentModule(invarp); - const bool isCrossModule = ownerModp && ownerModp != nodep && !invarp->isIO(); + const bool isCrossModule = ownerModp && ownerModp != nodep; const bool isIfaceTri = isCrossModule && VN_IS(ownerModp, Iface); if (isCrossModule && !isIfaceTri) { @@ -778,7 +778,7 @@ class TristateVisitor final : public TristateBaseVisitor { kv.second.inlinedDots, findModportForDotted(nodep, kv.first)); } - } else if (VN_IS(nodep, Iface)) { + } else if (VN_IS(nodep, Iface) && !invarp->isIO()) { // Local driver in an interface module - use contribution mechanism // so it can be combined with any external drivers later insertTristatesSignal(nodep, invarp, refsp, true, "", "", nullptr); diff --git a/test_regress/t/t_tri_iface_port.py b/test_regress/t/t_tri_iface_port.py new file mode 100755 index 000000000..4348f3df1 --- /dev/null +++ b/test_regress/t/t_tri_iface_port.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_tri_iface_port.v b/test_regress/t/t_tri_iface_port.v new file mode 100644 index 000000000..366ad995c --- /dev/null +++ b/test_regress/t/t_tri_iface_port.v @@ -0,0 +1,65 @@ +// DESCRIPTION: Verilator: Interface inout port driven locally and externally +// +// 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 + +interface zest_if_local ( + inout [0:0] U27 +); + assign U27[0] = 1'b1; +endinterface + +interface zest_if_ext ( + inout [0:0] U28 +); +endinterface + +module ext_drv ( + zest_if_ext zif, + input drive_en +); + assign zif.U28[0] = drive_en ? 1'b1 : 1'bz; +endmodule + +interface zest_if_ext_mp ( + inout [0:0] U29 +); + modport drv(inout U29); +endinterface + +module ext_drv_mp ( + zest_if_ext_mp.drv zif, + input drive_en +); + assign zif.U29[0] = drive_en ? 1'b0 : 1'bz; +endmodule + +module t ( + inout [0:0] bus_local, + inout [0:0] bus_ext, + inout [0:0] bus_ext_mp, + input drive_en +); + zest_if_local zif_local ( + .U27(bus_local) + ); + + zest_if_ext zif_ext ( + .U28(bus_ext) + ); + ext_drv u_ext_drv ( + .zif(zif_ext), + .drive_en(drive_en) + ); + + zest_if_ext_mp zif_ext_mp ( + .U29(bus_ext_mp) + ); + ext_drv_mp u_ext_drv_mp ( + .zif(zif_ext_mp), + .drive_en(drive_en) + ); +endmodule