diff --git a/include/verilated_types.h b/include/verilated_types.h index 07c910343..90e2b9b78 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -638,6 +638,24 @@ public: VlQueue sliceBackBack(int32_t lsb, int32_t msb) const { return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb); } + // Assign src elements to q[lsb:msb] + void sliceAssign(int32_t lsb, int32_t msb, const VlQueue& src) { + const int32_t sz = static_cast(m_deque.size()); + const int32_t srcSz = static_cast(src.m_deque.size()); + if (VL_UNLIKELY(sz <= 0 || srcSz <= 0)) return; + if (VL_UNLIKELY(lsb < 0)) lsb = 0; + if (VL_UNLIKELY(lsb >= sz)) lsb = sz - 1; + if (VL_UNLIKELY(msb >= sz)) msb = sz - 1; + const int32_t count = std::min(msb - lsb + 1, srcSz); + if (VL_UNLIKELY(count <= 0)) return; + std::copy_n(src.m_deque.begin(), count, m_deque.begin() + lsb); + } + void sliceAssignFrontBack(int32_t lsb, int32_t msb, const VlQueue& src) { + sliceAssign(lsb, m_deque.size() - 1 - msb, src); + } + void sliceAssignBackBack(int32_t lsb, int32_t msb, const VlQueue& src) { + sliceAssign(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb, src); + } // For save/restore const_iterator begin() const { return m_deque.begin(); } diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index cdfb5442f..9123f125e 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -802,6 +802,9 @@ public: DYN_RESIZE, DYN_SIZE, DYN_SLICE, + DYN_SLICE_ASSIGN, + DYN_SLICE_ASSIGN_BACK_BACK, + DYN_SLICE_ASSIGN_FRONT_BACK, DYN_SLICE_BACK_BACK, DYN_SLICE_FRONT_BACK, EVENT_CLEAR_FIRED, @@ -943,6 +946,9 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) { {DYN_RESIZE, "resize", false}, \ {DYN_SIZE, "size", true}, \ {DYN_SLICE, "slice", true}, \ + {DYN_SLICE_ASSIGN, "sliceAssign", false}, \ + {DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false}, \ + {DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false}, \ {DYN_SLICE_BACK_BACK, "sliceBackBack", true}, \ {DYN_SLICE_FRONT_BACK, "sliceFrontBack", true}, \ {EVENT_CLEAR_FIRED, "clearFired", false}, \ diff --git a/src/V3Width.cpp b/src/V3Width.cpp index e0ada32d0..ab54eb30e 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6026,6 +6026,42 @@ class WidthVisitor final : public VNVisitor { return; } + // Queue slice on LHS: q[a:b] = rhs -> q.sliceAssign(a, b, rhs) + // The LHS was lowered from AstSelExtract to CMethodHard(DYN_SLICE) + // by V3WidthSel; that returns a temporary copy so the assignment is + // silently discarded. Transform into a mutating sliceAssign call. + if (AstCMethodHard* const slicep = VN_CAST(nodep->lhsp(), CMethodHard)) { + VCMethod assignMethod; + if (slicep->method() == VCMethod::DYN_SLICE) { + assignMethod = VCMethod::DYN_SLICE_ASSIGN; + } else if (slicep->method() == VCMethod::DYN_SLICE_BACK_BACK) { + assignMethod = VCMethod::DYN_SLICE_ASSIGN_BACK_BACK; + } else if (slicep->method() == VCMethod::DYN_SLICE_FRONT_BACK) { + assignMethod = VCMethod::DYN_SLICE_ASSIGN_FRONT_BACK; + } else { + assignMethod = VCMethod::_ENUM_MAX; // not a slice + } + if (assignMethod != VCMethod::_ENUM_MAX) { + UINFO(9, "LHS queue slice -> sliceAssign: " << nodep); + AstNodeExpr* const fromp = slicep->fromp()->unlinkFrBack(); + // Collect existing slice index pins (lsb, msb) + AstNodeExpr* const lsbp = slicep->pinsp()->unlinkFrBack(); + AstNodeExpr* const msbp = slicep->pinsp()->unlinkFrBack(); + AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); + AstCMethodHard* const newp + = new AstCMethodHard{nodep->fileline(), fromp, assignMethod}; + newp->addPinsp(lsbp); + newp->addPinsp(msbp); + newp->addPinsp(rhsp); + newp->didWidth(true); + newp->protect(false); + newp->dtypeSetVoid(); + nodep->replaceWith(newp->makeStmt()); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } + if (nodep->hasDType() && nodep->dtypep()->isEvent()) { checkEventAssignment(nodep); v3Global.setAssignsEvents(); diff --git a/test_regress/t/t_queue_slice_assign.py b/test_regress/t/t_queue_slice_assign.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_queue_slice_assign.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_queue_slice_assign.v b/test_regress/t/t_queue_slice_assign.v new file mode 100644 index 000000000..b84821cf6 --- /dev/null +++ b/test_regress/t/t_queue_slice_assign.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +`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); + +module t; + initial begin + int q[$]; + + // Basic slice assignment: overwrite middle elements + q = '{10, 20, 30, 40, 50}; + q[1:3] = '{99, 88, 77}; + `checkh(q[0], 10); + `checkh(q[1], 99); + `checkh(q[2], 88); + `checkh(q[3], 77); + `checkh(q[4], 50); + `checkh(q.size, 5); + + // Slice assignment at start + q = '{10, 20, 30, 40, 50}; + q[0:1] = '{11, 22}; + `checkh(q[0], 11); + `checkh(q[1], 22); + `checkh(q[2], 30); + `checkh(q[3], 40); + `checkh(q[4], 50); + + // Slice assignment at end + q = '{10, 20, 30, 40, 50}; + q[3:4] = '{44, 55}; + `checkh(q[0], 10); + `checkh(q[1], 20); + `checkh(q[2], 30); + `checkh(q[3], 44); + `checkh(q[4], 55); + + // Single-element slice + q = '{10, 20, 30, 40, 50}; + q[2:2] = '{66}; + `checkh(q[0], 10); + `checkh(q[1], 20); + `checkh(q[2], 66); + `checkh(q[3], 40); + `checkh(q[4], 50); + + // Verify size unchanged after all operations + `checkh(q.size, 5); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule