From 4e9792c34c76765b87cece47bac80ebdde9a899c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 5 Feb 2026 14:49:07 +0000 Subject: [PATCH] Fix C++ types of non-inlined module ports (#7002) We use special C++ types for ports, e.g. SystemC types in --sc mode, and native C arrays for unpacked arrays in --cc mode. These types are not substitutable for internal types, e.g. VlUnpacked, however all the runtime primitives expect internal types. I think the intention was to use these special IO types only for top level ports, but the current implementation also uses them for the ports of all non-inlined modules. This means the output C++ will not compile if such a port is passed to a runtime primitive (e.g. array 'sort' as in the new test) or DPI import. Changed to use the special IO types only on the top level ports. Note these are likely still broken if attempting to invoke on a top level port (we might be saved by wrapTop, but later optimizations might eliminate the intermediary) --- src/V3EmitCBase.cpp | 4 +-- test_regress/t/t_noninl_port_type.py | 18 +++++++++++ test_regress/t/t_noninl_port_type.v | 48 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_noninl_port_type.py create mode 100644 test_regress/t/t_noninl_port_type.v diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 4222de5a4..616eeebe0 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -176,7 +176,7 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { } }; - if (nodep->isIO() && nodep->isSc()) { + if (nodep->isPrimaryIO() && nodep->isSc()) { UASSERT_OBJ(basicp, nodep, "Unimplemented: Outputting this data type"); if (nodep->isInout()) { putns(nodep, "sc_core::sc_inout<"); @@ -197,7 +197,7 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { if (asRef && refNeedParens) puts(")"); emitDeclArrayBrackets(nodep); puts(";\n"); - } else if (nodep->isIO() && basicp && !basicp->isOpaque()) { + } else if (nodep->isPrimaryIO() && basicp && !basicp->isOpaque()) { if (nodep->isInout()) { putns(nodep, "VL_INOUT"); } else if (nodep->isWritable()) { diff --git a/test_regress/t/t_noninl_port_type.py b/test_regress/t/t_noninl_port_type.py new file mode 100755 index 000000000..b1a195a9c --- /dev/null +++ b/test_regress/t/t_noninl_port_type.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('vlt') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_noninl_port_type.v b/test_regress/t/t_noninl_port_type.v new file mode 100644 index 000000000..0ed7b7219 --- /dev/null +++ b/test_regress/t/t_noninl_port_type.v @@ -0,0 +1,48 @@ +// 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 + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module top; + + int x, y, z; + int out [3]; + + sub sub_i(x, y, z, out); + + initial begin + x = 2; + y = 1; + z = 3; + #1; + `checkh(out[0], 1); + `checkh(out[1], 2); + `checkh(out[2], 3); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +module sub( + input int a, + input int b, + input int c, + output int sorted [3] +); + + /* verilator no_inline_module */ + + always_comb begin + sorted = '{a, b, c}; + sorted.sort; + end + +endmodule