From 998ec5b1d7ab210ff9acc90e6a5a3c84e3b2d5ed Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Fri, 20 Mar 2026 14:51:35 +0100 Subject: [PATCH] Fix streaming with descending unpacked arrays and unpacked-to-queue (#7287) --- include/verilated_funcs.h | 40 +++++++++++++++++ src/V3Const.cpp | 64 ++++++++++++++++++++++++++++ src/V3EmitCFunc.h | 24 ++++++++++- test_regress/t/t_stream_unpack_lhs.v | 16 +++---- 4 files changed, 133 insertions(+), 11 deletions(-) diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index f6404392c..ce9643084 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2417,6 +2417,46 @@ static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits } } +// Reverse element order of an unpacked array in-place. +// Used by emitter for descending-range arrays after VL_UNPACK_*. +template +static inline void VL_UNPACK_REVERSED(VlUnpacked& q) { + for (size_t i = 0; i < N_Depth / 2; ++i) { + const T_Value tmp = q[i]; + q[i] = q[N_Depth - 1 - i]; + q[N_Depth - 1 - i] = tmp; + } +} + +// Return a reversed copy of an unpacked array. +// Used by emitter for descending-range arrays before VL_PACK_*. +template +static inline VlUnpacked +VL_PACK_REVERSED(const VlUnpacked& q) { + VlUnpacked ret; + for (size_t i = 0; i < N_Depth; ++i) ret[i] = q[N_Depth - 1 - i]; + return ret; +} + +// Overloads for VlUnpacked source -> VlQueue destination +template +static inline void VL_COPY_Q(VlQueue& q, const VlUnpacked& from, int lbits, + int srcElementBits, int dstElementBits) { + VlQueue srcQ; + srcQ.renew(N_Depth); + for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[i]; + VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits); +} + +template +static inline void VL_REVCOPY_Q(VlQueue& q, const VlUnpacked& from, int lbits, + int srcElementBits, int dstElementBits) { + VlQueue srcQ; + srcQ.renew(N_Depth); + for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[N_Depth - 1 - i]; + VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits); +} + //====================================================================== // Expressions needing insert/select diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 4c39092a1..8c74f675e 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2389,6 +2389,32 @@ class ConstVisitor final : public VNVisitor { AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp(); AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack(); const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp(); + // Handle unpacked/queue/dynarray source -> queue/dynarray dest via + // CvtArrayToArray (StreamL reverses, so reverse=true) + if ((VN_IS(srcDTypep, UnpackArrayDType) || VN_IS(srcDTypep, QueueDType) + || VN_IS(srcDTypep, DynArrayDType)) + && (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType))) { + int blockSize = 1; + if (const AstConst* const constp + = VN_CAST(VN_AS(streamp, StreamL)->rhsp(), Const)) { + blockSize = constp->toSInt(); + if (VL_UNLIKELY(blockSize <= 0)) blockSize = 1; + } + int srcElementBits = 0; + if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + nodep->lhsp(dstp); + nodep->rhsp(new AstCvtArrayToArray{srcp->fileline(), srcp, dstDTypep, true, + blockSize, dstElementBits, srcElementBits}); + nodep->dtypep(dstDTypep); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + return true; + } const int sWidth = srcp->width(); const int dWidth = dstp->width(); // Connect the rhs to the stream operator and update its width @@ -2420,6 +2446,44 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack(); AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp(); AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack(); + // Handle unpacked/queue/dynarray source -> queue/dynarray dest via + // CvtArrayToArray (StreamR does not reverse, so reverse=false). + // V3Width may have wrapped the source in CvtArrayToPacked; unwrap it. + if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) { + AstNodeExpr* origSrcp = srcp; + if (AstCvtArrayToPacked* const cvtp = VN_CAST(srcp, CvtArrayToPacked)) { + origSrcp = cvtp->fromp(); + } + const AstNodeDType* const origSrcDTypep = origSrcp->dtypep()->skipRefp(); + if (VN_IS(origSrcDTypep, UnpackArrayDType) || VN_IS(origSrcDTypep, QueueDType) + || VN_IS(origSrcDTypep, DynArrayDType)) { + int srcElementBits = 0; + if (const AstNodeDType* const elemDtp = origSrcDTypep->subDTypep()) { + srcElementBits = elemDtp->width(); + } + int dstElementBits = 0; + if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) { + dstElementBits = elemDtp->width(); + } + if (VN_IS(srcp, CvtArrayToPacked)) { + origSrcp = VN_AS(srcp, CvtArrayToPacked)->fromp()->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(srcp), srcp); + srcp = origSrcp; + } + // Descending unpacked arrays need element reversal + bool reverse = false; + if (const AstUnpackArrayDType* const unpackDtp + = VN_CAST(origSrcDTypep, UnpackArrayDType)) { + reverse = !unpackDtp->declRange().ascending(); + } + nodep->lhsp(dstp); + nodep->rhsp(new AstCvtArrayToArray{srcp->fileline(), srcp, dstDTypep, reverse, + 1, dstElementBits, srcElementBits}); + nodep->dtypep(dstDTypep); + VL_DO_DANGLING(pushDeletep(streamp), streamp); + return true; + } + } const int sWidth = srcp->width(); const int dWidth = dstp->width(); if (VN_IS(dstDTypep, UnpackArrayDType)) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 7c27f1f3b..aeab8fe8e 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -456,12 +456,23 @@ public: void visit(AstCvtArrayToPacked* nodep) override { AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); AstNodeDType* const elemDtp = fromDtp->subDTypep()->skipRefp(); + const bool descending = [&]() { + if (const AstUnpackArrayDType* const unpackDtp = VN_CAST(fromDtp, UnpackArrayDType)) + return !unpackDtp->declRange().ascending(); + return false; + }(); puts("VL_PACK_"); emitIQW(nodep); puts("_"); emitRU(fromDtp); emitIQW(elemDtp); - emitOpName(nodep, "(%nw, %rw, %P, %li)", nodep->fromp(), elemDtp, nullptr); + if (descending) { + // Wrap source in VL_PACK_REVERSED so VL_PACK sees ascending order + emitOpName(nodep, "(%nw, %rw, %P, VL_PACK_REVERSED(%li))", nodep->fromp(), elemDtp, + nullptr); + } else { + emitOpName(nodep, "(%nw, %rw, %P, %li)", nodep->fromp(), elemDtp, nullptr); + } } void visit(AstCvtUnpackedToQueue* nodep) override { @@ -499,6 +510,7 @@ public: bool paren = true; bool decind = false; bool rhs = true; + bool reverseUnpack = false; // Set for descending CvtPackedToArray if (AstSel* const selp = VN_CAST(nodep->lhsp(), Sel)) { UASSERT_OBJ(selp->widthMin() == selp->widthConst(), selp, "Width mismatch"); if (selp->widthMin() == 1) { @@ -567,6 +579,11 @@ public: puts(", "); rhs = false; iterateAndNextConstNull(castp->fromp()); + // Descending unpacked dest: reverse after unpack + if (const AstUnpackArrayDType* const unpackDtp + = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { + if (!unpackDtp->declRange().ascending()) reverseUnpack = true; + } } else if (const AstCvtArrayToArray* const castp = VN_CAST(nodep->rhsp(), CvtArrayToArray)) { if (castp->reverse()) { @@ -618,6 +635,11 @@ public: if (paren) puts(")"); if (decind) ofp()->blockDec(); puts(";\n"); + if (reverseUnpack) { + puts("VL_UNPACK_REVERSED("); + iterateAndNextConstNull(nodep->lhsp()); + puts(");\n"); + } } void visit(AstAssocSel* nodep) override { iterateAndNextConstNull(nodep->fromp()); diff --git a/test_regress/t/t_stream_unpack_lhs.v b/test_regress/t/t_stream_unpack_lhs.v index 9b5e9d0e5..9896adfd1 100644 --- a/test_regress/t/t_stream_unpack_lhs.v +++ b/test_regress/t/t_stream_unpack_lhs.v @@ -111,23 +111,19 @@ module t ( // 2D packed array into unpacked array if (unpacked_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; if (unpacked_asc_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - // TODO: VL_UNPACK does not account for descending unpacked array - // index direction -- re-enable once fixed. - // if (unpacked_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; + if (unpacked_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; // 2D unpacked array into packed array if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; if (packed_asc_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - // TODO: Descending-range packed array streaming + pattern comparison - // if (packed_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; + if (packed_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; // 2D packed array into queue if (packed_siz_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; if (packed_asc_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; if (packed_des_queue_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; - // TODO: Streaming from unpacked array into queue produces empty - // queue -- re-enable once fixed. - // if (unpacked_siz_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - // if (unpacked_asc_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - // if (unpacked_des_queue_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; + // 2D unpacked array into queue + if (unpacked_siz_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; + if (unpacked_asc_queue_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; + if (unpacked_des_queue_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; end if (cyc == 3) begin