From aaafa6e8dfe7847d60d0802f99843d1c34dbdcba Mon Sep 17 00:00:00 2001 From: Paul Swirhun <63216497+paul-demo@users.noreply.github.com> Date: Sun, 9 Nov 2025 08:48:55 -0700 Subject: [PATCH] Fix local interface parameter hierarchical access (#6661) (#6666) Co-authored-by: Paul Swirhun --- src/V3Param.cpp | 12 ++++- .../t/t_interface_param_local_access.py | 16 ++++++ .../t/t_interface_param_local_access.v | 51 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_interface_param_local_access.py create mode 100644 test_regress/t/t_interface_param_local_access.v diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 4a197ec8d..b5d8472c9 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1283,6 +1283,7 @@ class ParamVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) AstNodeModule* m_modp; // Module iterating std::unordered_set m_ifacePortNames; // Interface port names in current module + std::unordered_set m_ifaceInstNames; // Interface decl names in current module string m_generateHierName; // Generate portion of hierarchy name // METHODS @@ -1314,8 +1315,10 @@ class ParamVisitor final : public VNVisitor { { VL_RESTORER(m_modp); VL_RESTORER(m_ifacePortNames); + VL_RESTORER(m_ifaceInstNames); m_modp = modp; m_ifacePortNames.clear(); + m_ifaceInstNames.clear(); iterateChildren(modp); } } @@ -1397,7 +1400,7 @@ class ParamVisitor final : public VNVisitor { }); } - // Check if cell parameters reference interface ports + // Check if cell parameters reference interface ports or local interface instances bool cellParamsReferenceIfacePorts(AstCell* cellp) { if (!cellp->paramsp()) return false; @@ -1406,7 +1409,10 @@ class ParamVisitor final : public VNVisitor { if (AstNode* const exprp = pinp->exprp()) { if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) { const string refname = getRefBaseName(refp); - if (!refname.empty() && m_ifacePortNames.count(refname)) return true; + if (!refname.empty() + && (m_ifacePortNames.count(refname) + || m_ifaceInstNames.count(refname))) + return true; } } } @@ -1490,6 +1496,8 @@ class ParamVisitor final : public VNVisitor { } void visit(AstCell* nodep) override { checkParamNotHier(nodep->paramsp()); + // Build cache of locally declared interface instance names + if (VN_IS(nodep->modp(), Iface)) { m_ifaceInstNames.insert(nodep->name()); } visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface)); } void visit(AstIfaceRefDType* nodep) override { diff --git a/test_regress/t/t_interface_param_local_access.py b/test_regress/t/t_interface_param_local_access.py new file mode 100755 index 000000000..c283ba235 --- /dev/null +++ b/test_regress/t/t_interface_param_local_access.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_st") + +test.compile() + +test.passes() diff --git a/test_regress/t/t_interface_param_local_access.v b/test_regress/t/t_interface_param_local_access.v new file mode 100644 index 000000000..18ab6bd09 --- /dev/null +++ b/test_regress/t/t_interface_param_local_access.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 + +interface intf #( + parameter int FOO = 32 +) (); +endinterface + +module sub ( + intf intf_a, + intf intf_b +); + localparam int INTF_A_FOO = intf_a.FOO; + localparam int INTF_B_FOO = intf_b.FOO; + if (INTF_A_FOO != INTF_B_FOO) + $error("INTF_A_FOO != INTF_B_FOO: %0d != %0d", INTF_A_FOO, INTF_B_FOO); +endmodule + +module t; + intf #(.FOO(21)) local_intf (); + + intf #(.FOO(21)) intf_a_1 (); + intf #(.FOO(21)) intf_b_1 (); + sub sub_1 ( + .intf_a(intf_a_1), + .intf_b(intf_b_1) + ); + + /* verilator lint_off HIERPARAM */ + localparam int LOCAL_INTF_FOO = local_intf.FOO; + /* verilator lint_on HIERPARAM */ + intf #(.FOO(LOCAL_INTF_FOO)) intf_a_2 (); + intf #(.FOO(21)) intf_b_2 (); + sub sub_2 ( + .intf_a(intf_a_2), + .intf_b(intf_b_2) + ); + + /* verilator lint_off HIERPARAM */ + intf #(.FOO(local_intf.FOO)) intf_a_3 (); + /* verilator lint_on HIERPARAM */ + intf #(.FOO(21)) intf_b_3 (); + sub sub_3 ( + .intf_a(intf_a_3), + .intf_b(intf_b_3) + ); + +endmodule