From 1c79df8630f9a2b884742458afdabc02a687ea29 Mon Sep 17 00:00:00 2001 From: Fuad Ismail Date: Fri, 22 Mar 2024 05:26:42 +0700 Subject: [PATCH] Support stream operation on unpacked array (#4714) (#5006) --- include/verilated_funcs.h | 21 +++ src/V3AstNodeExpr.h | 28 ++++ src/V3Const.cpp | 60 ++++++- src/V3EmitCFunc.h | 28 ++++ src/V3Slice.cpp | 40 ++--- src/V3Width.cpp | 31 +++- test_regress/t/t_stream_bad.out | 5 - .../t/t_stream_integer_type_unsup.out | 146 +++--------------- test_regress/t/t_stream_unpack.pl | 21 +++ test_regress/t/t_stream_unpack.v | 83 ++++++++++ test_regress/t/t_stream_unpack_narrower.out | 11 ++ test_regress/t/t_stream_unpack_narrower.pl | 19 +++ test_regress/t/t_stream_unpack_narrower.v | 18 +++ test_regress/t/t_stream_unpack_wider.pl | 21 +++ test_regress/t/t_stream_unpack_wider.v | 55 +++++++ 15 files changed, 429 insertions(+), 158 deletions(-) create mode 100755 test_regress/t/t_stream_unpack.pl create mode 100644 test_regress/t/t_stream_unpack.v create mode 100644 test_regress/t/t_stream_unpack_narrower.out create mode 100755 test_regress/t/t_stream_unpack_narrower.pl create mode 100644 test_regress/t/t_stream_unpack_narrower.v create mode 100755 test_regress/t/t_stream_unpack_wider.pl create mode 100644 test_regress/t/t_stream_unpack_wider.v diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index fe9151a75..76a8fd67f 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -1578,6 +1578,13 @@ static inline void VL_ASSIGN_DYN_Q(VlQueue& q, int elem_size, int lbits, QDat for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask); } +template +static inline void VL_ASSIGN_UNPACK_Q(VlUnpacked& q, size_t elem_size, QData from) { + const QData mask = VL_MASK_Q(elem_size); + for (size_t i = 0; i < T_Depth; ++i) + q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask); +} + template static inline IData VL_DYN_TO_I(const VlQueue& q, int elem_size) { IData ret = 0; @@ -1592,6 +1599,20 @@ static inline QData VL_DYN_TO_Q(const VlQueue& q, int elem_size) { return ret; } +template +static inline IData VL_UNPACK_TO_I(const VlUnpacked& q, size_t elem_size) { + IData ret = 0; + for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size); + return ret; +} + +template +static inline QData VL_UNPACK_TO_Q(const VlUnpacked& q, size_t elem_size) { + QData ret = 0; + for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size); + return ret; +} + // Because concats are common and wide, it's valuable to always have a clean output. // Thus we specify inputs must be clean, so we don't need to clean the output. // Note the bit shifts are always constants, so the adds in these constify out. diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index dcebf3f3d..0146f6b70 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1112,6 +1112,34 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } }; +class AstCvtPackedToUnpackArray final : public AstNodeExpr { + // Cast from packed array to dynamic queue data type + // @astgen op1 := fromp : AstNodeExpr +public: + AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) + : ASTGEN_SUPER_CvtPackedToUnpackArray(fl) { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtPackedToUnpackArray; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } +}; +class AstCvtUnpackArrayToPacked final : public AstNodeExpr { + // Cast from dynamic queue data type to packed array + // @astgen op1 := fromp : AstNodeExpr +public: + AstCvtUnpackArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) + : ASTGEN_SUPER_CvtUnpackArrayToPacked(fl) { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtUnpackArrayToPacked; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } +}; class AstDot final : public AstNodeExpr { // A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef // These are eliminated in the link stage diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ff0337190..a640de897 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2168,6 +2168,21 @@ class ConstVisitor final : public VNVisitor { "array to a variable of size greater than 64"); } srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep}; + } else if (VN_IS(srcDTypep, UnpackArrayDType)) { + if (nodep->lhsp()->widthMin() > 64) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic " + "array to a variable of size greater than 64"); + } + srcp = new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp, + nodep->lhsp()->dtypep()}; + // Handling the case where lhs is wider than rhs by inserting zeros. StreamL does + // not require this, since the left streaming operator implicitly handles this. + const uint32_t packedBits = nodep->lhsp()->widthMin(); + const uint32_t unpackBits + = srcDTypep->arrayUnpackedElements() * srcDTypep->subDTypep()->widthMin(); + const uint32_t offset = packedBits > unpackBits ? packedBits - unpackBits : 0; + srcp = new AstShiftL{srcp->fileline(), srcp, + new AstConst{srcp->fileline(), offset}, 64}; } nodep->rhsp(srcp); VL_DO_DANGLING(pushDeletep(streamp), streamp); @@ -2188,10 +2203,17 @@ class ConstVisitor final : public VNVisitor { } else { streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED); } - if (dWidth == 0) { - streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()}; - } else if (sWidth > dWidth) { - streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth}; + if (VN_IS(dstp->dtypep(), UnpackArrayDType)) { + streamp + = new AstCvtPackedToUnpackArray{nodep->fileline(), streamp, dstp->dtypep()}; + } else { + UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier"); + if (dWidth == 0) { + streamp + = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()}; + } else if (sWidth >= dWidth) { + streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth}; + } } nodep->lhsp(dstp); nodep->rhsp(streamp); @@ -2205,10 +2227,24 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack(); const int sWidth = srcp->width(); const int dWidth = dstp->width(); - if (dWidth == 0) { - srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()}; - } else if (sWidth > dWidth) { - srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth}; + if (VN_IS(dstp->dtypep(), UnpackArrayDType)) { + const int dstBitWidth + = dWidth * VN_AS(dstp->dtypep(), UnpackArrayDType)->arrayUnpackedElements(); + // Handling the case where rhs is wider than lhs. StreamL does not require this + // since the combination of the left streaming operation and the implicit + // truncation in VL_ASSIGN_UNPACK automatically selects the left-most bits. + if (sWidth > dstBitWidth) { + srcp + = new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth}; + } + srcp = new AstCvtPackedToUnpackArray{nodep->fileline(), srcp, dstp->dtypep()}; + } else { + UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier"); + if (dWidth == 0) { + srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()}; + } else if (sWidth >= dWidth) { + srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth}; + } } nodep->lhsp(dstp); nodep->rhsp(srcp); @@ -2228,6 +2264,14 @@ class ConstVisitor final : public VNVisitor { srcp->unlinkFrBack(); streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep}); streamp->dtypeFrom(lhsDtypep); + } else if (VN_IS(srcDTypep, UnpackArrayDType)) { + if (lhsDtypep->widthMin() > 64) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of unpacked " + "array to a variable of size greater than 64"); + } + srcp->unlinkFrBack(); + streamp->lhsp(new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp, lhsDtypep}); + streamp->dtypeFrom(lhsDtypep); } } else if (m_doV && replaceAssignMultiSel(nodep)) { return true; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 3ff35f13f..a825d210a 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -380,6 +380,21 @@ public: puts(")"); } + void visit(AstCvtUnpackArrayToPacked* nodep) override { + putns(nodep, "VL_UNPACK_TO_"); + emitIQW(nodep); + const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep(); + putns(elemDTypep, "<"); + putbs(elemDTypep->cType("", false, false)); + puts(","); + puts(cvtToStr(nodep->fromp()->dtypep()->arrayUnpackedElements())); + puts(">("); + iterateAndNextConstNull(nodep->fromp()); + puts(", "); + putns(elemDTypep, cvtToStr(elemDTypep->widthMin())); + puts(")"); + } + void visit(AstNodeAssign* nodep) override { bool paren = true; bool decind = false; @@ -447,6 +462,19 @@ public: puts(", "); rhs = false; iterateAndNextConstNull(castp->fromp()); + } else if (const AstCvtPackedToUnpackArray* const castp + = VN_CAST(nodep->rhsp(), CvtPackedToUnpackArray)) { + putns(castp, "VL_ASSIGN_UNPACK_Q<"); + putbs(castp->dtypep()->subDTypep()->cType("", false, false)); + puts(", "); + puts(cvtToStr(castp->dtypep()->arrayUnpackedElements())); + puts(">("); + iterateAndNextConstNull(nodep->lhsp()); + puts(", "); + putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->widthMin())); + puts(", "); + rhs = false; + iterateAndNextConstNull(castp->fromp()); } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) // && !VN_IS(nodep->rhsp(), CExpr) // && !VN_IS(nodep->rhsp(), CMethodHard) // diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index f2fe8cb7e..229403338 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -226,26 +226,30 @@ class SliceVisitor final : public VNVisitor { m_assignError = false; if (debug() >= 9) nodep->dumpTree("- Deslice-In: "); AstNodeDType* const dtp = nodep->lhsp()->dtypep()->skipRefp(); + AstNode* stp = nodep->rhsp(); if (const AstUnpackArrayDType* const arrayp = VN_CAST(dtp, UnpackArrayDType)) { - // Left and right could have different ascending/descending range, - // but #elements is common and all variables are realigned to start at zero - // Assign of an ascending range slice to a descending range one must reverse the - // elements - AstNodeAssign* newlistp = nullptr; - const int elements = arrayp->rangep()->elementsConst(); - for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { - AstNodeAssign* const newp - = nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx), - cloneAndSel(nodep->rhsp(), elements, elemIdx)); - if (debug() >= 9) newp->dumpTree("- new: "); - newlistp = AstNode::addNext(newlistp, newp); + if (!VN_IS(stp, CvtPackedToUnpackArray)) { + // Left and right could have different ascending/descending range, + // but #elements is common and all variables are realigned to start at zero + // Assign of an ascending range slice to a descending range one must reverse + // the elements + AstNodeAssign* newlistp = nullptr; + const int elements = arrayp->rangep()->elementsConst(); + for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { + AstNodeAssign* const newp + = nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx), + cloneAndSel(nodep->rhsp(), elements, elemIdx)); + if (debug() >= 9) newp->dumpTree("- new: "); + newlistp = AstNode::addNext(newlistp, newp); + } + if (debug() >= 9) nodep->dumpTree("- Deslice-Dn: "); + nodep->replaceWith(newlistp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + // Normal edit iterator will now iterate on all of the expansion assignments + // This will potentially call this function again to resolve next level of + // slicing + return; } - if (debug() >= 9) nodep->dumpTree("- Deslice-Dn: "); - nodep->replaceWith(newlistp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - // Normal edit iterator will now iterate on all of the expansion assignments - // This will potentially call this function again to resolve next level of slicing - return; } VL_RESTORER(m_assignp); m_assignp = nodep; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4327785ed..17829f6a7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -843,9 +843,10 @@ class WidthVisitor final : public VNVisitor { nodep->v3error("Slice size isn't a constant or basic data type."); } const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefToEnump(); - if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType)) { + if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType) + || VN_IS(lhsDtypep, UnpackArrayDType)) { nodep->dtypeSetStream(); - } else if (VN_IS(lhsDtypep, UnpackArrayDType) || lhsDtypep->isCompound()) { + } else if (lhsDtypep->isCompound()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Stream operation on a variable of a type " << lhsDtypep->prettyDTypeNameQ()); @@ -4908,6 +4909,32 @@ class WidthVisitor final : public VNVisitor { return; } + // Width check for unpacked array stream assignment + if (const AstNodeStream* streamp = VN_CAST(nodep->rhsp(), NodeStream)) { + if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) { + int lwidth = nodep->lhsp()->width(); + int rwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements(); + if (lwidth != 0 && lwidth < rwidth) { + nodep->v3widthWarn(lwidth, rwidth, + "Target fixed size variable (" + << lwidth << " bits) is narrower than the stream (" + << rwidth << " bits) (IEEE 1800-2023 11.4.14)"); + } + } + } else if (const AstNodeStream* streamp = VN_CAST(nodep->lhsp(), NodeStream)) { + if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) { + int rwidth = nodep->rhsp()->width(); + int lwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements(); + if (rwidth != 0 && rwidth < lwidth) { + nodep->v3widthWarn(lwidth, rwidth, + "Stream target requires " + << lwidth + << " bits, but source expression only provides " + << rwidth << " bits (IEEE 1800-2023 11.4.14.3)"); + } + } + } + if (nodep->hasDType() && nodep->dtypep()->isEvent()) { checkEventAssignment(nodep); v3Global.setAssignsEvents(); diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index 34284f067..72fd8fe3d 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -6,9 +6,4 @@ : ... note: In instance 't' 12 | initial packed_data_32 = {<<$random{byte_in}}; | ^~ -%Error-UNSUPPORTED: t/t_stream_bad.v:12:30: Unsupported: Stream operation on a variable of a type 'byte$[0:3]' - : ... note: In instance 't' - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_stream_integer_type_unsup.out b/test_regress/t/t_stream_integer_type_unsup.out index 83e8c92a9..d752b12ff 100644 --- a/test_regress/t/t_stream_integer_type_unsup.out +++ b/test_regress/t/t_stream_integer_type_unsup.out @@ -1,146 +1,42 @@ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:118:31: Unsupported: Stream operation on a variable of a type 'byte$[0:3]' - : ... note: In instance 't' - 118 | packed_data_32 = {<<8{byte_in}}; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:119:31: Unsupported: Stream operation on a variable of a type 'shortint$[0:3]' - : ... note: In instance 't' - 119 | packed_data_64 = {<<16{shortint_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:120:31: Unsupported: Stream operation on a variable of a type 'int$[0:3]' +%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:120:28: Unsupported: Assignment of stream of unpacked array to a variable of size greater than 64 : ... note: In instance 't' 120 | packed_data_128 = {<<32{int_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:121:31: Unsupported: Stream operation on a variable of a type 'integer$[0:3]' + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:121:28: Unsupported: Assignment of stream of unpacked array to a variable of size greater than 64 : ... note: In instance 't' 121 | packed_data_128_i = {<<32{integer_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:122:31: Unsupported: Stream operation on a variable of a type 'longint$[0:3]' + | ^ +%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:122:28: Unsupported: Assignment of stream of unpacked array to a variable of size greater than 64 : ... note: In instance 't' 122 | packed_data_256 = {<<64{longint_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:123:31: Unsupported: Stream operation on a variable of a type 'time$[0:3]' + | ^ +%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:123:28: Unsupported: Assignment of stream of unpacked array to a variable of size greater than 64 : ... note: In instance 't' 123 | packed_time_256 = {<<64{time_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:124:31: Unsupported: Stream operation on a variable of a type 'bit[7:0]$[0:3]' - : ... note: In instance 't' - 124 | v_packed_data_32 = {<<8{bit_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:125:31: Unsupported: Stream operation on a variable of a type 'bit[15:0]$[0:3]' - : ... note: In instance 't' - 125 | v_packed_data_64 = {<<16{logic_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:126:31: Unsupported: Stream operation on a variable of a type 'bit[31:0]$[0:3]' + | ^ +%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:126:28: Unsupported: Assignment of stream of unpacked array to a variable of size greater than 64 : ... note: In instance 't' 126 | v_packed_data_128 = {<<32{reg_in}}; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:128:11: Unsupported: Stream operation on a variable of a type 'byte$[0:3]' - : ... note: In instance 't' - 128 | {<<8{byte_out}} = packed_data_32; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:129:11: Unsupported: Stream operation on a variable of a type 'shortint$[0:3]' - : ... note: In instance 't' - 129 | {<<16{shortint_out}} = packed_data_64; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:130:11: Unsupported: Stream operation on a variable of a type 'int$[0:3]' - : ... note: In instance 't' - 130 | {<<32{int_out}} = packed_data_128; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:131:11: Unsupported: Stream operation on a variable of a type 'integer$[0:3]' - : ... note: In instance 't' - 131 | {<<32{integer_out}} = packed_data_128_i; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:132:11: Unsupported: Stream operation on a variable of a type 'longint$[0:3]' - : ... note: In instance 't' - 132 | {<<64{longint_out}} = packed_data_256; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:133:11: Unsupported: Stream operation on a variable of a type 'time$[0:3]' - : ... note: In instance 't' - 133 | {<<64{time_out}} = packed_time_256; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:134:11: Unsupported: Stream operation on a variable of a type 'bit[7:0]$[0:3]' - : ... note: In instance 't' - 134 | {<<8{bit_out}} = v_packed_data_32; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:135:11: Unsupported: Stream operation on a variable of a type 'bit[15:0]$[0:3]' - : ... note: In instance 't' - 135 | {<<16{logic_out}} = v_packed_data_64; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:136:11: Unsupported: Stream operation on a variable of a type 'bit[31:0]$[0:3]' - : ... note: In instance 't' - 136 | {<<32{reg_out}} = v_packed_data_128; - | ^~ -%Error-UNSUPPORTED: t/t_stream_integer_type_unsup.v:150:31: Unsupported: Stream operation on a variable of a type 'byte$[0:3]' - : ... note: In instance 't' - 150 | packed_data_32 = {< 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v new file mode 100644 index 000000000..722de106a --- /dev/null +++ b/test_regress/t/t_stream_unpack.v @@ -0,0 +1,83 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// 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); +`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +typedef enum bit [5:0] { + A = 6'b111000, + B = 6,b111111 +} enum_t; + +module t (/*AUTOARG*/); + initial begin + bit arr[6]; + bit [1:0] arr2[3]; + bit [5:0] arr6[1]; + string v; + bit [5:0] bit6 = 6'b111000; + bit [5:0] ans; + enum_t ans_enum; + + { >> bit {arr}} = bit6; + v = $sformatf("%p", arr); `checks(v, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + + ans = { >> bit {arr} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ >> bit {arr} }); + `checkh(ans_enum, bit6); + + { << bit {arr}} = bit6; + v = $sformatf("%p", arr); `checks(v, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + + ans = { << bit {arr} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ << bit {arr} }); + `checkh(ans_enum, bit6); + + { >> bit[1:0] {arr2}} = bit6; + v = $sformatf("%p", arr2); `checks(v, "'{'h3, 'h2, 'h0} "); + + ans = { >> bit[1:0] {arr2} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ >> bit[1:0] {arr2} }); + `checkh(ans_enum, bit6); + + { << bit[1:0] {arr2}} = bit6; + v = $sformatf("%p", arr2); `checks(v, "'{'h0, 'h2, 'h3} "); + + ans = { << bit[1:0] {arr2} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ << bit[1:0] {arr2} }); + `checkh(ans_enum, bit6); + + { >> bit [5:0] {arr6} } = bit6; + v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + + ans = { >> bit[5:0] {arr6} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ >> bit[5:0] {arr6} }); + `checkh(ans_enum, bit6); + + { << bit [5:0] {arr6} } = bit6; + v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + + ans = { << bit[5:0] {arr6} }; + `checkh(ans, bit6); + + ans_enum = enum_t'({ << bit[5:0] {arr6} }); + `checkh(ans_enum, bit6); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_stream_unpack_narrower.out b/test_regress/t/t_stream_unpack_narrower.out new file mode 100644 index 000000000..cc932a986 --- /dev/null +++ b/test_regress/t/t_stream_unpack_narrower.out @@ -0,0 +1,11 @@ +%Warning-WIDTHEXPAND: t/t_stream_unpack_narrower.v:14:18: Stream target requires 32 bits, but source expression only provides 31 bits (IEEE 1800-2023 11.4.14.3) + : ... note: In instance 't' + 14 | {>>{stream}} = packed_data; + | ^ + ... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=latest + ... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message. +%Warning-WIDTHTRUNC: t/t_stream_unpack_narrower.v:15:17: Target fixed size variable (31 bits) is narrower than the stream (32 bits) (IEEE 1800-2023 11.4.14) + : ... note: In instance 't' + 15 | packed_data = {>>{stream}}; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_stream_unpack_narrower.pl b/test_regress/t/t_stream_unpack_narrower.pl new file mode 100755 index 000000000..a29ead0ed --- /dev/null +++ b/test_regress/t/t_stream_unpack_narrower.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_stream_unpack_narrower.v b/test_regress/t/t_stream_unpack_narrower.v new file mode 100644 index 000000000..7f7cb4867 --- /dev/null +++ b/test_regress/t/t_stream_unpack_narrower.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +module t; + + logic [30:0] packed_data; + logic [7:0] stream[4]; + + initial begin + packed_data = 31'h12345678; + {>>{stream}} = packed_data; + packed_data = {>>{stream}}; + end + +endmodule diff --git a/test_regress/t/t_stream_unpack_wider.pl b/test_regress/t/t_stream_unpack_wider.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_stream_unpack_wider.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_stream_unpack_wider.v b/test_regress/t/t_stream_unpack_wider.v new file mode 100644 index 000000000..94b522875 --- /dev/null +++ b/test_regress/t/t_stream_unpack_wider.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// 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); +`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t (/*AUTOARG*/); + initial begin + logic [7:0] src_1 = 8'b1010_0011; // 8 bits wide source + logic [1:0] dst_1 [3]; // 6 bits wide target + logic [1:0] exp_1 [3]; // 6 bits wide target + + logic [1:0] src_2 [3] = '{2'b10, 2'b10, 2'b10}; // 6 bits wide source + logic [7:0] dst_2; // 8 bits wide target + logic [7:0] exp_2; // 8 bits wide target + + string expv; + string gotv; + + // unpack as target, StreamR + {>>{dst_1}} = src_1; + exp_1 = '{2'b10, 2'b10, 2'b00}; + expv = $sformatf("%p", exp_1); + gotv = $sformatf("%p", dst_1); + `checks(gotv, expv); + + // unpack as target, StreamL + {<<{dst_1}} = src_1; + exp_1 = '{2'b00, 2'b01, 2'b01}; + expv = $sformatf("%p", exp_1); + gotv = $sformatf("%p", dst_1); + `checks(gotv, expv); + + // unpack as source, StreamR + dst_2 = {>>{src_2}}; + exp_2 = 8'b10101000; + expv = $sformatf("%p", exp_2); + gotv = $sformatf("%p", dst_2); + `checks(gotv, expv); + + // unpack as source, StreamL + dst_2 = {<<{src_2}}; + exp_2 = 8'b01010100; + expv = $sformatf("%p", exp_2); + gotv = $sformatf("%p", dst_2); + `checks(gotv, expv); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule