From 2a7daea2c3acf2f9db13d09dd30c949fd8a847e2 Mon Sep 17 00:00:00 2001 From: Eric Mejdrich Date: Tue, 21 Apr 2026 11:47:39 +0200 Subject: [PATCH] additional testcases for function and coverage, including nested interfaces, passing multidimensional arrays through module hierarchy, and dotted reference of inner interfaces. --- src/V3Inst.cpp | 105 ++++++++++++++++++ .../t/t_iface_array_multidim_3d_port.py | 18 +++ .../t/t_iface_array_multidim_3d_port.v | 59 ++++++++++ test_regress/t/t_iface_array_multidim_hier.py | 18 +++ test_regress/t/t_iface_array_multidim_hier.v | 58 ++++++++++ .../t/t_iface_array_multidim_modport.py | 18 +++ .../t/t_iface_array_multidim_modport.v | 59 ++++++++++ .../t/t_iface_array_multidim_nested_port.py | 18 +++ .../t/t_iface_array_multidim_nested_port.v | 69 ++++++++++++ test_regress/t/t_iface_array_multidim_port.py | 18 +++ test_regress/t/t_iface_array_multidim_port.v | 55 +++++++++ .../t/t_iface_array_multidim_port_write.py | 18 +++ .../t/t_iface_array_multidim_port_write.v | 40 +++++++ 13 files changed, 553 insertions(+) create mode 100644 test_regress/t/t_iface_array_multidim_3d_port.py create mode 100644 test_regress/t/t_iface_array_multidim_3d_port.v create mode 100644 test_regress/t/t_iface_array_multidim_hier.py create mode 100644 test_regress/t/t_iface_array_multidim_hier.v create mode 100644 test_regress/t/t_iface_array_multidim_modport.py create mode 100644 test_regress/t/t_iface_array_multidim_modport.v create mode 100644 test_regress/t/t_iface_array_multidim_nested_port.py create mode 100644 test_regress/t/t_iface_array_multidim_nested_port.v create mode 100644 test_regress/t/t_iface_array_multidim_port.py create mode 100644 test_regress/t/t_iface_array_multidim_port.v create mode 100644 test_regress/t/t_iface_array_multidim_port_write.py create mode 100644 test_regress/t/t_iface_array_multidim_port_write.v diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 4647fe100..dd8e2099c 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -446,6 +446,111 @@ private: } } else { AstVar* const pinVarp = nodep->modVarp(); + // Multi-dim whole-array iface pin fanout: cartesian-product the port's + // nested UnpackArrayDType layers and emit one pin + per-element var per cell. + // For 1-dim falls through to the original code below. + std::vector portArrs; + for (AstNodeDType* d = pinVarp->dtypep()->skipRefp(); d;) { + if (const AstUnpackArrayDType* const arrp = VN_CAST(d, UnpackArrayDType)) { + portArrs.push_back(arrp); + d = arrp->subDTypep()->skipRefp(); + } else { + break; + } + } + if (portArrs.size() >= 2) { + AstIfaceRefDType* const portIrp + = VN_CAST(portArrs.back()->subDTypep()->skipRefp(), IfaceRefDType); + if (!portIrp || portIrp->isVirtual()) return; + const int ndim = static_cast(portArrs.size()); + std::vector sizes(ndim); + int totalElems = 1; + for (int d = 0; d < ndim; ++d) { + sizes[d] = portArrs[d]->elementsConst(); + totalElems *= sizes[d]; + } + const AstVarRef* const varrefp = VN_CAST(nodep->exprp(), VarRef); + if (!varrefp) { + nodep->exprp()->v3error("Unexpected connection to multi-dim arrayed port"); + return; + } + std::vector exprArrs; + for (AstNodeDType* d = varrefp->dtypep()->skipRefp(); d;) { + if (const AstUnpackArrayDType* const arrp = VN_CAST(d, UnpackArrayDType)) { + exprArrs.push_back(arrp); + d = arrp->subDTypep()->skipRefp(); + } else { + break; + } + } + if (exprArrs.size() != static_cast(ndim)) { + nodep->exprp()->v3error( + "Multi-dim iface pin expression rank does not match port"); + return; + } + AstNode* prevp = nullptr; + AstNode* prevPinp = nullptr; + std::vector idx(ndim, 0); + for (int n = 0; n < totalElems; ++n) { + int rem = n; + for (int d = ndim - 1; d >= 0; --d) { + idx[d] = rem % sizes[d]; + rem /= sizes[d]; + } + string portSuffix; + string exprSuffix; + for (int d = 0; d < ndim; ++d) { + portSuffix += "__BRA__" + + AstNode::encodeNumber(portArrs[d]->lo() + idx[d]) + + "__KET__"; + exprSuffix += "__BRA__" + + AstNode::encodeNumber(exprArrs[d]->lo() + idx[d]) + + "__KET__"; + } + const string varNewName = pinVarp->name() + portSuffix; + AstVar* varNewp = nullptr; + if (!pinVarp->backp()) { + varNewp = m_deModVars.find(varNewName); + } else { + portIrp->cellp(nullptr); + varNewp = pinVarp->cloneTree(false); + varNewp->name(varNewName); + varNewp->origName(varNewp->origName() + portSuffix); + varNewp->dtypep(portIrp); + m_deModVars.insert(varNewp); + if (!prevp) { + prevp = varNewp; + } else { + prevp->addNextHere(varNewp); + } + } + if (!varNewp) { + if (debug() >= 9) m_deModVars.dump(); // LCOV_EXCL_LINE + nodep->v3fatalSrc("Module dearray failed for " + << AstNode::prettyNameQ(varNewName)); + } + AstPin* const newp = nodep->cloneTree(false); + newp->modVarp(varNewp); + newp->name(newp->name() + portSuffix); + AstVarXRef* const newVarXRefp = new AstVarXRef{ + nodep->fileline(), varrefp->name() + exprSuffix, "", VAccess::WRITE}; + newVarXRefp->varp(newp->modVarp()); + newp->exprp()->unlinkFrBack()->deleteTree(); + newp->exprp(newVarXRefp); + if (!prevPinp) { + prevPinp = newp; + } else { + prevPinp->addNextHere(newp); + } + } + if (prevp) { + pinVarp->replaceWith(prevp); + pushDeletep(pinVarp); + } + nodep->replaceWith(prevPinp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } const AstUnpackArrayDType* const pinArrp = VN_CAST(pinVarp->dtypep()->skipRefp(), UnpackArrayDType); if (!pinArrp || !VN_IS(pinArrp->subDTypep()->skipRefp(), IfaceRefDType)) return; diff --git a/test_regress/t/t_iface_array_multidim_3d_port.py b/test_regress/t/t_iface_array_multidim_3d_port.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_3d_port.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_3d_port.v b/test_regress/t/t_iface_array_multidim_3d_port.v new file mode 100644 index 000000000..029abf42d --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_3d_port.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// 3D iface-array port passed to a submodule. t_iface_array_multidim_3d +// covers 3D local declaration; this covers 3D through a port. + +interface simple_if; + logic [15:0] data; +endinterface + +module sink (simple_if b [1:0][1:0][2:0]); + logic [15:0] chk [1:0][1:0][2:0]; + genvar gi, gj, gk; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 2; gj++) begin : g_b + for (gk = 0; gk < 3; gk++) begin : g_c + always_comb chk[gi][gj][gk] = b[gi][gj][gk].data; + end + end + end + endgenerate +endmodule + +module t; + simple_if bus [1:0][1:0][2:0] (); + sink inst (.b(bus)); + + genvar gi, gj, gk; + generate + for (gi = 0; gi < 2; gi++) begin : g_drive_a + for (gj = 0; gj < 2; gj++) begin : g_drive_b + for (gk = 0; gk < 3; gk++) begin : g_drive_c + initial bus[gi][gj][gk].data = 16'(gi * 6 + gj * 3 + gk + 1); + end + end + end + endgenerate + + initial begin + #1; + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 2; j++) begin + for (int k = 0; k < 3; k++) begin + if (inst.chk[i][j][k] !== 16'(i * 6 + j * 3 + k + 1)) begin + $write("%%Error: chk[%0d][%0d][%0d]=%0d expected %0d\n", + i, j, k, inst.chk[i][j][k], i * 6 + j * 3 + k + 1); + $stop; + end + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_iface_array_multidim_hier.py b/test_regress/t/t_iface_array_multidim_hier.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_hier.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_hier.v b/test_regress/t/t_iface_array_multidim_hier.v new file mode 100644 index 000000000..40b0a100d --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_hier.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Three-level hierarchy passing a multi-dim iface array by port at each +// boundary. Top reads leaf's chk array via dotted cross-hier reference, +// which also exercises the multi-dim dotted-access resolver. + +interface simple_if; + logic [7:0] data; +endinterface + +module leaf (simple_if b [1:0][2:0]); + logic [7:0] chk [1:0][2:0]; + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 3; gj++) begin : g_b + always_comb chk[gi][gj] = b[gi][gj].data; + end + end + endgenerate +endmodule + +module mid (simple_if b [1:0][2:0]); + leaf leaf_inst (.b(b)); +endmodule + +module t; + simple_if bus [1:0][2:0] (); + mid mid_inst (.b(bus)); + + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_drive_a + for (gj = 0; gj < 3; gj++) begin : g_drive_b + initial bus[gi][gj].data = 8'(gi * 3 + gj + 7); + end + end + endgenerate + + initial begin + #1; + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 3; j++) begin + if (mid_inst.leaf_inst.chk[i][j] !== 8'(i * 3 + j + 7)) begin + $write("%%Error: leaf.chk[%0d][%0d]=%0d expected %0d\n", + i, j, mid_inst.leaf_inst.chk[i][j], i * 3 + j + 7); + $stop; + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_iface_array_multidim_modport.py b/test_regress/t/t_iface_array_multidim_modport.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_modport.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_modport.v b/test_regress/t/t_iface_array_multidim_modport.v new file mode 100644 index 000000000..d30703f2d --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_modport.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Multi-dim iface array ports typed with a modport (both source and sink). +// Mirrors 1D modport-port coverage in t_interface_array_loop for the +// multi-dim pin-fanout path. + +interface simple_if; + logic [7:0] data; + modport source(output data); + modport sink(input data); +endinterface + +module src (simple_if.source b [1:0][2:0]); + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 3; gj++) begin : g_b + initial b[gi][gj].data = 8'(gi * 3 + gj + 20); + end + end + endgenerate +endmodule + +module snk (simple_if.sink b [1:0][2:0]); + logic [7:0] chk [1:0][2:0]; + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 3; gj++) begin : g_b + always_comb chk[gi][gj] = b[gi][gj].data; + end + end + endgenerate +endmodule + +module t; + simple_if bus [1:0][2:0] (); + src src_inst (.b(bus)); + snk snk_inst (.b(bus)); + + initial begin + #1; + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 3; j++) begin + if (snk_inst.chk[i][j] !== 8'(i * 3 + j + 20)) begin + $write("%%Error: chk[%0d][%0d]=%0d expected %0d\n", + i, j, snk_inst.chk[i][j], i * 3 + j + 20); + $stop; + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_iface_array_multidim_nested_port.py b/test_regress/t/t_iface_array_multidim_nested_port.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_nested_port.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_nested_port.v b/test_regress/t/t_iface_array_multidim_nested_port.v new file mode 100644 index 000000000..443b4255b --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_nested_port.v @@ -0,0 +1,69 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// outer_if contains inner_if; an array of outer_if is passed as a port to +// a submodule which reaches through to inner_if's signal. Exercises +// hierarchical iface reference across the fanned-out multi-dim port. + +interface inner_if; + logic [7:0] data; +endinterface + +interface outer_if; + inner_if inner(); + logic [7:0] tag; +endinterface + +module sink (outer_if b [1:0][1:0]); + logic [7:0] chk_tag [1:0][1:0]; + logic [7:0] chk_inner [1:0][1:0]; + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 2; gj++) begin : g_b + always_comb chk_tag[gi][gj] = b[gi][gj].tag; + always_comb chk_inner[gi][gj] = b[gi][gj].inner.data; + end + end + endgenerate +endmodule + +module t; + outer_if oarr [1:0][1:0] (); + sink inst (.b(oarr)); + + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_drive_a + for (gj = 0; gj < 2; gj++) begin : g_drive_b + initial begin + oarr[gi][gj].tag = 8'(gi * 16 + gj); + oarr[gi][gj].inner.data = 8'(gi * 2 + gj + 100); + end + end + end + endgenerate + + initial begin + #1; + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 2; j++) begin + if (inst.chk_tag[i][j] !== 8'(i * 16 + j)) begin + $write("%%Error: chk_tag[%0d][%0d]=%0d expected %0d\n", + i, j, inst.chk_tag[i][j], i * 16 + j); + $stop; + end + if (inst.chk_inner[i][j] !== 8'(i * 2 + j + 100)) begin + $write("%%Error: chk_inner[%0d][%0d]=%0d expected %0d\n", + i, j, inst.chk_inner[i][j], i * 2 + j + 100); + $stop; + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_iface_array_multidim_port.py b/test_regress/t/t_iface_array_multidim_port.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_port.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_port.v b/test_regress/t/t_iface_array_multidim_port.v new file mode 100644 index 000000000..7ceb44155 --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_port.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Multi-dim iface array passed as a port to a submodule (the point of +// multi-dim iface arrays). Exercises the multi-dim pin-fanout branch in +// V3Inst::visit(AstPin) and the multi-dim branch in V3Inst::visit(AstVar) +// on the sink's port var. + +interface simple_if; + logic [7:0] data; +endinterface + +module sink (simple_if b [1:0][2:0]); + logic [7:0] chk [1:0][2:0]; + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 3; gj++) begin : g_b + always_comb chk[gi][gj] = b[gi][gj].data; + end + end + endgenerate +endmodule + +module t; + simple_if bus [1:0][2:0] (); + sink inst (.b(bus)); + + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_drive_a + for (gj = 0; gj < 3; gj++) begin : g_drive_b + initial bus[gi][gj].data = 8'(gi * 3 + gj + 1); + end + end + endgenerate + + initial begin + #1; + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 3; j++) begin + if (inst.chk[i][j] !== 8'(i * 3 + j + 1)) begin + $write("%%Error: inst.chk[%0d][%0d]=%0d expected %0d\n", + i, j, inst.chk[i][j], i * 3 + j + 1); + $stop; + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_iface_array_multidim_port_write.py b/test_regress/t/t_iface_array_multidim_port_write.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_port_write.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_array_multidim_port_write.v b/test_regress/t/t_iface_array_multidim_port_write.v new file mode 100644 index 000000000..161ea9b52 --- /dev/null +++ b/test_regress/t/t_iface_array_multidim_port_write.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Multi-dim iface array port, sink WRITES into the iface signals, top reads. +// Complements t_iface_array_multidim_port (which has sink reading). + +interface simple_if; + logic [7:0] data; +endinterface + +module src (simple_if b [1:0][2:0]); + genvar gi, gj; + generate + for (gi = 0; gi < 2; gi++) begin : g_a + for (gj = 0; gj < 3; gj++) begin : g_b + initial b[gi][gj].data = 8'(gi * 3 + gj + 50); + end + end + endgenerate +endmodule + +module t; + simple_if bus [1:0][2:0] (); + src inst (.b(bus)); + + initial begin + #1; + if (bus[0][0].data !== 8'd50) begin $write("%%Error: bus[0][0]=%0d\n", bus[0][0].data); $stop; end + if (bus[0][1].data !== 8'd51) begin $write("%%Error: bus[0][1]=%0d\n", bus[0][1].data); $stop; end + if (bus[0][2].data !== 8'd52) begin $write("%%Error: bus[0][2]=%0d\n", bus[0][2].data); $stop; end + if (bus[1][0].data !== 8'd53) begin $write("%%Error: bus[1][0]=%0d\n", bus[1][0].data); $stop; end + if (bus[1][1].data !== 8'd54) begin $write("%%Error: bus[1][1]=%0d\n", bus[1][1].data); $stop; end + if (bus[1][2].data !== 8'd55) begin $write("%%Error: bus[1][2]=%0d\n", bus[1][2].data); $stop; end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule