From a50aea770992d7165ed9dfbbc81959c25a60ed5c Mon Sep 17 00:00:00 2001 From: Kristof Marien Date: Mon, 21 Sep 2026 17:57:03 -0700 Subject: [PATCH] Fix streaming dynamic arrays with different element types failing to compile (#8434) (#8435) --- include/verilated_funcs.h | 32 ++++++++++++---- test_regress/t/t_stream_dynamic_cross_type.py | 18 +++++++++ test_regress/t/t_stream_dynamic_cross_type.v | 38 +++++++++++++++++++ 3 files changed, 80 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_stream_dynamic_cross_type.py create mode 100644 test_regress/t/t_stream_dynamic_cross_type.v diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 420da03d3..7478cdf9b 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2948,8 +2948,8 @@ inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) { for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } } -// This specialization works for both VlQueue (and similar) as well -// as VlQueue>. +// These overloads work for both VlQueue (and similar) as well +// as VlQueue>, including queues with different element types. template inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int /*lbits*/, int srcElementBits, int dstElementBits) { @@ -2971,16 +2971,32 @@ inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int /*lbits*/, int } } -// This specialization works for both VlQueue (and similar) as well -// as VlQueue>. -template -inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits, - int dstElementBits) { +// Keep the same-type overload separate for its direct-copy optimization and alias handling; +// differently typed queues cannot alias. +template +inline typename std::enable_if::value>::type +VL_COPY_Q(VlQueue& q, const VlQueue& from, int /*lbits*/, int srcElementBits, + int dstElementBits) { + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } + for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) { + VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex, + VL_GET_QUEUE_BIT(from, srcElementBits, bitIndex)); + } +} + +// This works for both VlQueue (and similar) as well as VlQueue>, +// including queues with different element types. +template +inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, + int srcElementBits, int dstElementBits) { const size_t srcTotalBits = from.size() * srcElementBits; const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; // Always make a copy to handle the case where q and from are the same queue - VlQueue srcCopy = from; + VlQueue srcCopy = from; // Initialize all elements to zero using appropriate method q.renew(dstSize); diff --git a/test_regress/t/t_stream_dynamic_cross_type.py b/test_regress/t/t_stream_dynamic_cross_type.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_stream_dynamic_cross_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('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_stream_dynamic_cross_type.v b/test_regress/t/t_stream_dynamic_cross_type.v new file mode 100644 index 000000000..d874d743e --- /dev/null +++ b/test_regress/t/t_stream_dynamic_cross_type.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by Verilator Authors. +// 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='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + bit source_bits[]; + int unsigned destination_words[]; + + initial begin + source_bits = new[37]; + source_bits[0] = 1; + source_bits[2] = 1; + source_bits[5] = 1; + source_bits[31] = 1; + source_bits[36] = 1; + + destination_words = {>>{source_bits}}; + `checkh(destination_words.size(), 2); + `checkh(destination_words[0], 32'ha4000001); + `checkh(destination_words[1], 32'h08000000); + + destination_words = {<<{source_bits}}; + `checkh(destination_words.size(), 2); + `checkh(destination_words[0], 32'h84000001); + `checkh(destination_words[1], 32'h28000000); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule