diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 203fef45a..2c044e918 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2780,9 +2780,8 @@ class ConstVisitor final : public VNVisitor { streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(), VSigning::UNSIGNED); srcp = packedp; - } - if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) - || VN_IS(srcDTypep, UnpackArrayDType))) { + } else if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType) + || VN_IS(srcDTypep, UnpackArrayDType))) { if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { int blockSize = 1; if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) { diff --git a/test_regress/t/t_stream_unpacked_array_struct.py b/test_regress/t/t_stream_unpacked_array_struct.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_stream_unpacked_array_struct.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_stream_unpacked_array_struct.v b/test_regress/t/t_stream_unpacked_array_struct.v new file mode 100644 index 000000000..773bb34bd --- /dev/null +++ b/test_regress/t/t_stream_unpacked_array_struct.v @@ -0,0 +1,60 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +// Ref. to IEEE 1800-2023 11.4.14 +// +// Streaming an unpacked array whose element is an unpacked struct. The left +// stream ({<<{}}) read used to crash V3EmitC: V3Const packed the aggregate +// source but then re-wrapped the now-packed expression in AstCvtArrayToPacked, +// which EmitC dereferenced as an array. + +module t( /*AUTOARG*/ + // Inputs + clk +); + input clk; + + `define checkh(gotv, expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__, `__LINE__, (gotv), (expv)); \ + $stop; \ + end while (0); + + typedef struct { + logic [7:0] a; + logic [7:0] b; + } s_t; + typedef s_t arr_t[2]; // unpacked array of unpacked struct + + localparam int W = $bits(arr_t); + + integer cyc = 0; + reg [63:0] crc = 64'h5aef0c8d_d70a4497; + wire [W-1:0] src = crc[W-1:0]; + + arr_t aw_r, aw_l; + always_comb {>>{aw_r}} = src; + always_comb {<<8{aw_l}} = src; + + wire [W-1:0] rd_r = {>>{aw_r}}; + wire [W-1:0] rd_l = {<<8{aw_l}}; // {<<{}} read of aggregate array previously crashed + + always @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + if (cyc > 1) begin + `checkh(rd_r, src); + `checkh(rd_l, src); + end + if (cyc == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule