From 2daa32b98b27855a224ce4daea5a5b6702004af2 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 25 Aug 2023 11:24:12 +0200 Subject: [PATCH] Support assignments of packed values to stream expressions on queues (#4401) --- include/verilated_funcs.h | 8 + src/V3AstNodeExpr.h | 14 ++ src/V3Const.cpp | 30 ++-- src/V3EmitCFunc.h | 13 ++ src/V3Width.cpp | 10 +- test_regress/t/t_stream_bad.out | 5 + test_regress/t/t_stream_dynamic.pl | 21 +++ test_regress/t/t_stream_dynamic.v | 40 +++++ test_regress/t/t_stream_integer_type.out | 40 ----- .../t/t_stream_integer_type_unsup.out | 146 ++++++++++++++++++ ...type.pl => t_stream_integer_type_unsup.pl} | 0 ...r_type.v => t_stream_integer_type_unsup.v} | 0 12 files changed, 269 insertions(+), 58 deletions(-) create mode 100755 test_regress/t/t_stream_dynamic.pl create mode 100644 test_regress/t/t_stream_dynamic.v delete mode 100644 test_regress/t/t_stream_integer_type.out create mode 100644 test_regress/t/t_stream_integer_type_unsup.out rename test_regress/t/{t_stream_integer_type.pl => t_stream_integer_type_unsup.pl} (100%) rename test_regress/t/{t_stream_integer_type.v => t_stream_integer_type_unsup.v} (100%) diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 63bcda06f..98e14738a 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -1526,6 +1526,14 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const return owp; } +template +static inline void VL_ASSIGN_DYN_Q(VlQueue& q, int elem_size, int lbits, QData from) { + const int size = (lbits + elem_size - 1) / elem_size; + q.renew(size); + const QData mask = VL_MASK_Q(elem_size); + for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask); +} + // 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 9e198b39e..302c74c6c 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1013,6 +1013,20 @@ public: // May return nullptr on parse failure. static AstConst* parseParamLiteral(FileLine* fl, const string& literal); }; +class AstCvtPackedToDynArray final : public AstNodeExpr { + // Cast from packed array to dynamic queue data type + // @astgen op1 := fromp : AstNodeExpr +public: + AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) + : ASTGEN_SUPER_CvtPackedToDynArray(fl) { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtPackedToDynArray; + 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 8eca1a198..856a13b57 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2158,12 +2158,11 @@ private: return true; } else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) { // Push the stream operator to the rhs of the assignment statement - const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width(); - const int sWidth = nodep->rhsp()->width(); - // Unlink the stuff - AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack(); - AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack(); + AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack(); + AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack(); AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack(); + const int sWidth = srcp->width(); + const int dWidth = dstp->width(); // Connect the rhs to the stream operator and update its width VN_AS(streamp, StreamL)->lhsp(srcp); if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType) @@ -2172,11 +2171,11 @@ private: } else { streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED); } - // Shrink the RHS if necessary - if (sWidth > dWidth) { + if (dWidth == 0) { + streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()}; + } else if (sWidth > dWidth) { streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth}; } - // Link the nodes back in nodep->lhsp(dstp); nodep->rhsp(streamp); return true; @@ -2184,14 +2183,15 @@ private: // The right stream operator on lhs of assignment statement does // not reorder bits. However, if the rhs is wider than the lhs, // then we select bits from the left-most, not the right-most. - const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width(); - const int sWidth = nodep->rhsp()->width(); - // Unlink the stuff - AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack(); - AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack(); - AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack(); + AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack(); + AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack(); + AstNode* const sizep = VN_AS(streamp, StreamR)->rhsp()->unlinkFrBack(); AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack(); - if (sWidth > dWidth) { + 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}; } nodep->lhsp(dstp); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 351fd956a..b4817ab8d 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -401,6 +401,19 @@ public: puts(cvtToStr(nodep->widthMin()) + ","); iterateAndNextConstNull(nodep->lhsp()); puts(", "); + } else if (const AstCvtPackedToDynArray* const castp + = VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) { + puts("VL_ASSIGN_DYN_Q<"); + putbs(castp->dtypep()->subDTypep()->cType("", false, false)); + puts(">("); + iterateAndNextConstNull(nodep->lhsp()); + puts(", "); + puts(cvtToStr(castp->dtypep()->subDTypep()->widthMin())); + puts(", "); + puts(cvtToStr(castp->fromp()->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/V3Width.cpp b/src/V3Width.cpp index 965c511d8..ab3f2267a 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -840,9 +840,13 @@ private: } else { nodep->v3error("Slice size isn't a constant or basic data type."); } - if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType) - || VN_IS(nodep->lhsp()->dtypep(), QueueDType) - || VN_IS(nodep->lhsp()->dtypep(), UnpackArrayDType)) { + const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep(); + if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType)) { + nodep->dtypeSetStream(); + } else if (VN_IS(lhsDtypep, UnpackArrayDType) || lhsDtypep->isCompound()) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: Stream operation on a variable of a type " + << lhsDtypep->prettyDTypeNameQ()); nodep->dtypeSetStream(); } else { nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(), diff --git a/test_regress/t/t_stream_bad.out b/test_regress/t/t_stream_bad.out index 6b4934e46..966297307 100644 --- a/test_regress/t/t_stream_bad.out +++ b/test_regress/t/t_stream_bad.out @@ -6,4 +6,9 @@ : ... 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]' + : ... 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_dynamic.pl b/test_regress/t/t_stream_dynamic.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_stream_dynamic.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_dynamic.v b/test_regress/t/t_stream_dynamic.v new file mode 100644 index 000000000..60c127bef --- /dev/null +++ b/test_regress/t/t_stream_dynamic.v @@ -0,0 +1,40 @@ +// 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 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 + bit arr[]; + bit [1:0] arr2[$]; + bit [4:0] arr5[]; + bit [5:0] arr6[$]; + string v; + bit [5:0] bit6 = 6'b111000; + + { >> bit {arr}} = bit6; + v = $sformatf("%p", arr); `checks(v, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + + { << bit {arr}} = bit6; + v = $sformatf("%p", arr); `checks(v, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + + { >> bit[1:0] {arr2}} = bit6; + v = $sformatf("%p", arr2); `checks(v, "'{'h0, 'h2, 'h3} "); + + { << bit[1:0] {arr2}} = bit6; + v = $sformatf("%p", arr2); `checks(v, "'{'h3, 'h2, 'h0} "); + + { >> bit [5:0] {arr6} } = bit6; + v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + + { << bit [5:0] {arr6} } = bit6; + v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_stream_integer_type.out b/test_regress/t/t_stream_integer_type.out deleted file mode 100644 index 05640a0f4..000000000 --- a/test_regress/t/t_stream_integer_type.out +++ /dev/null @@ -1,40 +0,0 @@ -%Error: t/t_stream_integer_type.v:128:11: SEL is not an unpacked array, but is in an unpacked array context - 128 | {<<8{byte_out}} = packed_data_32; - | ^~ -%Error: t/t_stream_integer_type.v:129:11: SEL is not an unpacked array, but is in an unpacked array context - 129 | {<<16{shortint_out}} = packed_data_64; - | ^~ -%Error: t/t_stream_integer_type.v:130:11: SEL is not an unpacked array, but is in an unpacked array context - 130 | {<<32{int_out}} = packed_data_128; - | ^~ -%Error: t/t_stream_integer_type.v:131:11: SEL is not an unpacked array, but is in an unpacked array context - 131 | {<<32{integer_out}} = packed_data_128_i; - | ^~ -%Error: t/t_stream_integer_type.v:132:11: SEL is not an unpacked array, but is in an unpacked array context - 132 | {<<64{longint_out}} = packed_data_256; - | ^~ -%Error: t/t_stream_integer_type.v:133:11: SEL is not an unpacked array, but is in an unpacked array context - 133 | {<<64{time_out}} = packed_time_256; - | ^~ -%Error: t/t_stream_integer_type.v:134:11: SEL is not an unpacked array, but is in an unpacked array context - 134 | {<<8{bit_out}} = v_packed_data_32; - | ^~ -%Error: t/t_stream_integer_type.v:135:11: SEL is not an unpacked array, but is in an unpacked array context - 135 | {<<16{logic_out}} = v_packed_data_64; - | ^~ -%Error: t/t_stream_integer_type.v:136:11: SEL is not an unpacked array, but is in an unpacked array context - 136 | {<<32{reg_out}} = v_packed_data_128; - | ^~ -%Error: t/t_stream_integer_type.v:160:11: SEL is not an unpacked array, but is in an unpacked array context - 160 | {<