From eb2cfe1d19126cfb2fb2b93132e06fbcc437b090 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 6 Dec 2023 14:02:04 +0100 Subject: [PATCH] Support parameterized virtual interaces (#4047) (#4743) --- src/V3LinkCells.cpp | 5 + src/V3LinkDot.cpp | 14 ++ src/V3Param.cpp | 12 ++ test_regress/t/t_interface_virtual_param.pl | 21 +++ test_regress/t/t_interface_virtual_param.v | 143 ++++++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100755 test_regress/t/t_interface_virtual_param.pl create mode 100644 test_regress/t/t_interface_virtual_param.v diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 8aa88deaa..6189b713b 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -239,6 +239,11 @@ class LinkCellsVisitor final : public VNVisitor { nodep->v3error("Non-interface used as an interface: " << nodep->prettyNameQ()); } } + iterateChildren(nodep); + for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + pinp->param(true); + if (pinp->name() == "") pinp->name("__paramNumber" + cvtToStr(pinp->pinNum())); + } // Note cannot do modport resolution here; modports are allowed underneath generates } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 066d144bf..9665ccc11 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3754,6 +3754,20 @@ class LinkDotResolveVisitor final : public VNVisitor { iterateChildren(nodep); } + void visit(AstIfaceRefDType* nodep) override { + if (nodep->paramsp()) { + // If there is no parameters, there is no need to visit this node. + AstIface* const ifacep = nodep->ifacep(); + UASSERT_OBJ(ifacep, nodep, "Port parameters of AstIfaceRefDType without ifacep()"); + if (ifacep->dead()) return; + checkNoDot(nodep); + m_usedPins.clear(); + VL_RESTORER(m_pinSymp); + m_pinSymp = m_statep->getNodeSym(ifacep); + iterate(nodep->paramsp()); + } + } + void visit(AstAttrOf* nodep) override { iterateChildren(nodep); } void visit(AstNode* nodep) override { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index fedeacb71..282f6e9f2 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -919,6 +919,11 @@ class ParamProcessor final { nodep->recursive(false); } + void ifaceRefDeparam(AstIfaceRefDType* const nodep, AstNodeModule*& srcModpr) { + nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false); + nodep->ifacep(VN_AS(srcModpr, Iface)); + } + void classRefDeparam(AstClassOrPackageRef* nodep, AstNodeModule*& srcModpr) { if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false)) nodep->classOrPackagep(srcModpr); @@ -948,6 +953,8 @@ public: if (auto* cellp = VN_CAST(nodep, Cell)) { cellDeparam(cellp, srcModpr); + } else if (auto* ifaceRefDTypep = VN_CAST(nodep, IfaceRefDType)) { + ifaceRefDeparam(ifaceRefDTypep, srcModpr); } else if (auto* classRefp = VN_CAST(nodep, ClassRefDType)) { classRefDeparam(classRefp, srcModpr); } else if (auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { @@ -1042,6 +1049,8 @@ class ParamVisitor final : public VNVisitor { if (VN_IS(classRefp->classOrPackageNodep(), ParamTypeDType)) continue; } else if (const auto* classRefp = VN_CAST(cellp, ClassRefDType)) { srcModp = classRefp->classp(); + } else if (const auto* ifaceRefp = VN_CAST(cellp, IfaceRefDType)) { + srcModp = ifaceRefp->ifacep(); } else { cellp->v3fatalSrc("Expected module parameterization"); } @@ -1137,6 +1146,9 @@ class ParamVisitor final : public VNVisitor { void visit(AstCell* nodep) override { visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface)); } + void visit(AstIfaceRefDType* nodep) override { + if (nodep->ifacep()) visitCellOrClassRef(nodep, true); + } void visit(AstClassRefDType* nodep) override { visitCellOrClassRef(nodep, false); } void visit(AstClassOrPackageRef* nodep) override { visitCellOrClassRef(nodep, false); } diff --git a/test_regress/t/t_interface_virtual_param.pl b/test_regress/t/t_interface_virtual_param.pl new file mode 100755 index 000000000..f45fe82b3 --- /dev/null +++ b/test_regress/t/t_interface_virtual_param.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1 + ); + +ok(1); +1; diff --git a/test_regress/t/t_interface_virtual_param.v b/test_regress/t/t_interface_virtual_param.v new file mode 100644 index 000000000..a5682a463 --- /dev/null +++ b/test_regress/t/t_interface_virtual_param.v @@ -0,0 +1,143 @@ +// DESCRIPTION: Verilator: Interface parameter getter +// +// A test of the import parameter used with modport +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2015 by Todd Strader +// SPDX-License-Identifier: CC0-1.0 + +interface test_if #(parameter integer FOO = 1); + + // Interface variable + logic data; + + localparam integer BAR = FOO + 1; + + // Modport + modport mp( + import getFoo, + output data + ); + + function integer getFoo (); + return FOO; + endfunction + +endinterface // test_if + +function integer identity (input integer x); + return x; +endfunction + + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + virtual test_if#(.FOO(identity(5))) the_vif; + + test_if #( .FOO (identity(5)) ) the_interface (); + test_if #( .FOO (identity(7)) ) array_interface [1:0] (); + + testmod testmod_i (.clk (clk), + .intf (the_interface), + .intf_no_mp (the_interface), + .intf_array (array_interface) + ); + + localparam THE_TOP_FOO = the_interface.FOO; + localparam THE_TOP_FOO_BITS = $bits({the_interface.FOO, the_interface.FOO}); + localparam THE_ARRAY_FOO = array_interface[0].FOO; + + initial begin + if (THE_TOP_FOO != 5) begin + $display("%%Error: THE_TOP_FOO = %0d", THE_TOP_FOO); + $stop; + end + if (THE_TOP_FOO_BITS != 64) begin + $display("%%Error: THE_TOP_FOO_BITS = %0d", THE_TOP_FOO_BITS); + $stop; + end + if (THE_ARRAY_FOO != 7) begin + $display("%%Error: THE_ARRAY_FOO = %0d", THE_ARRAY_FOO); + $stop; + end + end + +endmodule + + +module testmod + ( + input clk, + test_if.mp intf, + test_if intf_no_mp, + test_if.mp intf_array [1:0] + ); + + localparam THE_FOO = intf.FOO; + localparam THE_OTHER_FOO = intf_no_mp.FOO; + localparam THE_ARRAY_FOO = intf_array[0].FOO; + localparam THE_BAR = intf.BAR; + localparam THE_OTHER_BAR = intf_no_mp.BAR; + localparam THE_ARRAY_BAR = intf_array[0].BAR; + + always @(posedge clk) begin + if (THE_FOO != 5) begin + $display("%%Error: THE_FOO = %0d", THE_FOO); + $stop; + end + if (THE_OTHER_FOO != 5) begin + $display("%%Error: THE_OTHER_FOO = %0d", THE_OTHER_FOO); + $stop; + end + if (THE_ARRAY_FOO != 7) begin + $display("%%Error: THE_ARRAY_FOO = %0d", THE_ARRAY_FOO); + $stop; + end + if (intf.FOO != 5) begin + $display("%%Error: intf.FOO = %0d", intf.FOO); + $stop; + end + if (intf_no_mp.FOO != 5) begin + $display("%%Error: intf_no_mp.FOO = %0d", intf_no_mp.FOO); + $stop; + end + if (intf_array[0].FOO != 7) begin + $display("%%Error: intf_array[0].FOO = %0d", intf_array[0].FOO); + $stop; + end + // if (i.getFoo() != 5) begin + // $display("%%Error: i.getFoo() = %0d", i.getFoo()); + // $stop; + // end + if (THE_BAR != 6) begin + $display("%%Error: THE_BAR = %0d", THE_BAR); + $stop; + end + if (THE_OTHER_BAR != 6) begin + $display("%%Error: THE_OTHER_BAR = %0d", THE_OTHER_BAR); + $stop; + end + if (THE_ARRAY_BAR != 8) begin + $display("%%Error: THE_ARRAY_BAR = %0d", THE_ARRAY_BAR); + $stop; + end + if (intf.BAR != 6) begin + $display("%%Error: intf.BAR = %0d", intf.BAR); + $stop; + end + if (intf_no_mp.BAR != 6) begin + $display("%%Error: intf_no_mp.BAR = %0d", intf_no_mp.BAR); + $stop; + end + if (intf_array[0].BAR != 8) begin + $display("%%Error: intf_array[0].BAR = %0d", intf_array[0].BAR); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule