diff --git a/include/verilated.cpp b/include/verilated.cpp index af1f419b1..a77094f65 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1896,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE { return std::string{destout, len}; } +std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE { + std::string output; + for (const std::string& s : q) output += s; + return output; +} + std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE { std::string lstring = lhs; const int32_t rhs_s = rhs; // To signed value diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index ec5ff4eb3..63bcda06f 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2196,6 +2196,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE; // Conversion functions extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE; +extern std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE; inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { VlWide lw; VL_SET_WQ(lw, lhs); diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 0be9e89a5..5f6c6576b 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1406,6 +1406,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const { AstNodeDType* AstNode::findVoidDType() const { return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); } +AstNodeDType* AstNode::findStreamDType() const { + return v3Global.rootp()->typeTablep()->findStreamDType(fileline()); +} //###################################################################### // VNDeleter diff --git a/src/V3Ast.h b/src/V3Ast.h index b871314c0..db79c7ba4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1867,6 +1867,7 @@ public: void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); } void dtypeSetVoid() { dtypep(findVoidDType()); } + void dtypeSetStream() { dtypep(findStreamDType()); } // Data type locators AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); } @@ -1878,6 +1879,7 @@ public: AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); } AstNodeDType* findEmptyQueueDType() const; AstNodeDType* findVoidDType() const; + AstNodeDType* findStreamDType() const; AstNodeDType* findQueueIndexDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const; diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index a74eeec6f..7645fb3e6 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -1188,6 +1188,34 @@ public: int widthTotalBytes() const override { return sizeof(std::map); } bool isCompound() const override { return true; } }; +class AstStreamDType final : public AstNodeDType { + // Stream data type, used only as data type of stream operations + // Should behave like AstPackArrayDType, but it doesn't have a size +public: + explicit AstStreamDType(FileLine* fl) + : ASTGEN_SUPER_StreamDType(fl) { + dtypep(this); + } + ASTGEN_MEMBERS_AstStreamDType; + void dumpSmall(std::ostream& str) const override; + bool hasDType() const override { return true; } + bool maybePointedTo() const override { return true; } + bool undead() const override { return true; } + AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; } + AstNodeDType* virtRefDTypep() const override { return nullptr; } + void virtRefDTypep(AstNodeDType* nodep) override {} + bool similarDType(const AstNodeDType* samep) const override { return this == samep; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } + int widthAlignBytes() const override { return 1; } + int widthTotalBytes() const override { return 1; } + bool isCompound() const override { return false; } +}; class AstUnsizedArrayDType final : public AstNodeDType { // Unsized/open-range Array data type, ie "some_dtype var_name []" // @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 3ccd63be5..607a6b148 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4671,7 +4671,7 @@ public: ASTGEN_MEMBERS_AstCvtPackString; void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); } string emitVerilog() override { return "%f$_CAST(%l)"; } - string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } bool cleanLhs() const override { return true; } bool sizeMattersLhs() const override { return false; } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 90e475d15..e139a5905 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1513,6 +1513,7 @@ class AstTypeTable final : public AstNode { AstEmptyQueueDType* m_emptyQueuep = nullptr; AstQueueDType* m_queueIndexp = nullptr; AstVoidDType* m_voidp = nullptr; + AstStreamDType* m_streamp = nullptr; AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{}; // using DetailedMap = std::map; @@ -1538,6 +1539,7 @@ public: AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); AstQueueDType* findQueueIndexDType(FileLine* fl); AstVoidDType* findVoidDType(FileLine* fl); + AstStreamDType* findStreamDType(FileLine* fl); void clearCache(); void repairCache(); void dump(std::ostream& str = std::cout) const override; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 45a99e7ce..c766e2b02 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1071,6 +1071,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { return m_voidp; } +AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) { + if (VL_UNLIKELY(!m_streamp)) { + AstStreamDType* const newp = new AstStreamDType{fl}; + addTypesp(newp); + m_streamp = newp; + } + return m_streamp; +} + AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) { if (VL_UNLIKELY(!m_queueIndexp)) { AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr}; @@ -2055,6 +2064,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "void"; } +void AstStreamDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "stream"; +} void AstVarScope::dump(std::ostream& str) const { this->AstNode::dump(str); if (isTrace()) str << " [T]"; diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index f69d0e153..26ba6a088 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -95,6 +95,7 @@ private: || VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType) || VN_IS(nodep->dtypep()->skipRefp(), QueueDType) + || VN_IS(nodep->dtypep()->skipRefp(), StreamDType) || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { } else { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index f0656eaa5..f74282ffb 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2166,7 +2166,12 @@ private: AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack(); // Connect the rhs to the stream operator and update its width VN_AS(streamp, StreamL)->lhsp(srcp); - streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED); + if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType) + || VN_IS(srcp->dtypep(), UnpackArrayDType)) { + streamp->dtypeSetStream(); + } else { + streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED); + } // Shrink the RHS if necessary if (sWidth > dWidth) { streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth}; diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 195e41ea8..3e4346a93 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -452,6 +452,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) { putbs("std::string{"); putsQuoted(constp->num().toString()); puts("}"); + } else if (VN_IS(nodep->dtypep(), StreamDType)) { + putbs("VL_CVT_PACK_STR_ND("); + iterateAndNextConstNull(nodep); + puts(")"); } else { putbs("VL_CVT_PACK_STR_N"); emitIQW(nodep); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index be40a5f53..3e9b829d4 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1026,6 +1026,7 @@ public: UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way"); emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp()); } + void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); } void visit(AstRedXor* nodep) override { if (nodep->lhsp()->isWide()) { visit(static_cast(nodep)); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 334987da8..147c37111 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -848,8 +848,14 @@ private: } else { nodep->v3error("Slice size isn't a constant or basic data type."); } - nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(), - VSigning::UNSIGNED); + if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType) + || VN_IS(nodep->lhsp()->dtypep(), QueueDType) + || VN_IS(nodep->lhsp()->dtypep(), UnpackArrayDType)) { + nodep->dtypeSetStream(); + } else { + nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(), + VSigning::UNSIGNED); + } } if (m_vup->final()) { if (!nodep->dtypep()->widthSized()) { diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index 970a91901..6b4934e46 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -6,10 +6,4 @@ : ... In instance t 12 | initial packed_data_32 = {<<$random{byte_in}}; | ^~ -%Warning-WIDTHEXPAND: t/t_stream_bad.v:12:27: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's STREAML generates 8 bits. - : ... In instance t - 12 | initial packed_data_32 = {<<$random{byte_in}}; - | ^ - ... 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. %Error: Exiting due to diff --git a/test_regress/t/t_stream_integer_type.out b/test_regress/t/t_stream_integer_type.out index ab803e9a1..05640a0f4 100644 --- a/test_regress/t/t_stream_integer_type.out +++ b/test_regress/t/t_stream_integer_type.out @@ -1,149 +1,3 @@ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:118:28: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's STREAML generates 8 bits. - : ... In instance t - 118 | packed_data_32 = {<<8{byte_in}}; - | ^ - ... 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-WIDTHEXPAND: t/t_stream_integer_type.v:119:28: Operator ASSIGN expects 64 bits on the Assign RHS, but Assign RHS's STREAML generates 16 bits. - : ... In instance t - 119 | packed_data_64 = {<<16{shortint_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:120:28: Operator ASSIGN expects 128 bits on the Assign RHS, but Assign RHS's STREAML generates 32 bits. - : ... In instance t - 120 | packed_data_128 = {<<32{int_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:121:28: Operator ASSIGN expects 128 bits on the Assign RHS, but Assign RHS's STREAML generates 32 bits. - : ... In instance t - 121 | packed_data_128_i = {<<32{integer_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:122:28: Operator ASSIGN expects 256 bits on the Assign RHS, but Assign RHS's STREAML generates 64 bits. - : ... In instance t - 122 | packed_data_256 = {<<64{longint_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:123:28: Operator ASSIGN expects 256 bits on the Assign RHS, but Assign RHS's STREAML generates 64 bits. - : ... In instance t - 123 | packed_time_256 = {<<64{time_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:124:28: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's STREAML generates 8 bits. - : ... In instance t - 124 | v_packed_data_32 = {<<8{bit_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:125:28: Operator ASSIGN expects 64 bits on the Assign RHS, but Assign RHS's STREAML generates 16 bits. - : ... In instance t - 125 | v_packed_data_64 = {<<16{logic_in}}; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:126:28: Operator ASSIGN expects 128 bits on the Assign RHS, but Assign RHS's STREAML generates 32 bits. - : ... In instance t - 126 | v_packed_data_128 = {<<32{reg_in}}; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:128:31: Operator ASSIGN expects 8 bits on the Assign RHS, but Assign RHS's VARREF 'packed_data_32' generates 32 bits. - : ... In instance t - 128 | {<<8{byte_out}} = packed_data_32; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:129:31: Operator ASSIGN expects 16 bits on the Assign RHS, but Assign RHS's VARREF 'packed_data_64' generates 64 bits. - : ... In instance t - 129 | {<<16{shortint_out}} = packed_data_64; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:130:31: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's VARREF 'packed_data_128' generates 128 bits. - : ... In instance t - 130 | {<<32{int_out}} = packed_data_128; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:131:31: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's VARREF 'packed_data_128_i' generates 128 bits. - : ... In instance t - 131 | {<<32{integer_out}} = packed_data_128_i; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:132:31: Operator ASSIGN expects 64 bits on the Assign RHS, but Assign RHS's VARREF 'packed_data_256' generates 256 bits. - : ... In instance t - 132 | {<<64{longint_out}} = packed_data_256; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:133:31: Operator ASSIGN expects 64 bits on the Assign RHS, but Assign RHS's VARREF 'packed_time_256' generates 256 bits. - : ... In instance t - 133 | {<<64{time_out}} = packed_time_256; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:134:31: Operator ASSIGN expects 8 bits on the Assign RHS, but Assign RHS's VARREF 'v_packed_data_32' generates 32 bits. - : ... In instance t - 134 | {<<8{bit_out}} = v_packed_data_32; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:135:31: Operator ASSIGN expects 16 bits on the Assign RHS, but Assign RHS's VARREF 'v_packed_data_64' generates 64 bits. - : ... In instance t - 135 | {<<16{logic_out}} = v_packed_data_64; - | ^ -%Warning-WIDTHTRUNC: t/t_stream_integer_type.v:136:31: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's VARREF 'v_packed_data_128' generates 128 bits. - : ... In instance t - 136 | {<<32{reg_out}} = v_packed_data_128; - | ^ -%Warning-WIDTHEXPAND: t/t_stream_integer_type.v:150:28: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's STREAML generates 8 bits. - : ... In instance t - 150 | packed_data_32 = {< 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_stream_string_array.v b/test_regress/t/t_stream_string_array.v new file mode 100644 index 000000000..6e8081dd9 --- /dev/null +++ b/test_regress/t/t_stream_string_array.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t; + string qs[$]; + string as[]; + string s; + initial begin + s = {>>{qs}}; + if (s != "") $stop; + + s = {>>{as}}; + if (s != "") $stop; + + qs = '{"ab", "c", ""}; + s = {>>{qs}}; + if (s != "abc") $stop; + + as = new[3]; + as[0] = "abcd"; + as[2] = "ef"; + s = {>>{as}}; + if (s != "abcdef") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule