diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 98e14738a..a33b9023b 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -1534,6 +1534,20 @@ 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 IData VL_DYN_TO_I(const VlQueue& q, int elem_size) { + IData ret = 0; + for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size); + return ret; +} + +template +static inline QData VL_DYN_TO_Q(const VlQueue& q, int elem_size) { + QData ret = 0; + for (int i = 0; i < q.size(); ++i) ret |= q.at(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 ed2e771bd..75694f61d 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 AstCvtDynArrayToPacked final : public AstNodeExpr { + // Cast from dynamic queue data type to packed array + // @astgen op1 := fromp : AstNodeExpr +public: + AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) + : ASTGEN_SUPER_CvtDynArrayToPacked(fl) { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtDynArrayToPacked; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } +}; class AstCvtPackedToDynArray final : public AstNodeExpr { // Cast from packed array to dynamic queue data type // @astgen op1 := fromp : AstNodeExpr diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ad8795e55..f43f31afe 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2146,14 +2146,19 @@ private: return true; } else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) { // The right-streaming operator on rhs of assignment does not - // change the order of bits. Eliminate stream but keep its lhsp - // Unlink the stuff - AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack(); - AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack(); - AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack(); + // change the order of bits. Eliminate stream but keep its lhsp. + // Add a cast if needed. + AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack(); + AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack(); + AstNodeDType* const srcDTypep = srcp->dtypep(); + if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { + 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 AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep}; + } nodep->rhsp(srcp); - // Cleanup - VL_DO_DANGLING(sizep->deleteTree(), sizep); VL_DO_DANGLING(streamp->deleteTree(), streamp); // Further reduce, any of the nodes may have more reductions. return true; @@ -2186,7 +2191,6 @@ private: // then we select bits from the left-most, not the right-most. 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(); const int sWidth = srcp->width(); const int dWidth = dstp->width(); @@ -2197,11 +2201,23 @@ private: } nodep->lhsp(dstp); nodep->rhsp(srcp); - // Cleanup - VL_DO_DANGLING(sizep->deleteTree(), sizep); VL_DO_DANGLING(streamp->deleteTree(), streamp); // Further reduce, any of the nodes may have more reductions. return true; + } else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) { + AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep(); + AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL); + AstNodeExpr* const srcp = streamp->lhsp(); + const AstNodeDType* const srcDTypep = srcp->dtypep(); + if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) { + if (lhsDtypep->widthMin() > 64) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic " + "array to a variable of size greater than 64"); + } + srcp->unlinkFrBack(); + streamp->lhsp(new AstCvtDynArrayToPacked{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 459618ff7..a1c1cd515 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -345,6 +345,19 @@ public: emitVarDecl(nodep); } + void visit(AstCvtDynArrayToPacked* nodep) override { + puts("VL_DYN_TO_"); + emitIQW(nodep); + puts("<"); + const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep(); + putbs(elemDTypep->cType("", false, false)); + puts(">("); + iterateAndNextConstNull(nodep->fromp()); + puts(", "); + puts(cvtToStr(elemDTypep->widthMin())); + puts(")"); + } + void visit(AstNodeAssign* nodep) override { bool paren = true; bool decind = false; diff --git a/test_regress/t/t_stream_dynamic.v b/test_regress/t/t_stream_dynamic.v index 60c127bef..0c02f1db8 100644 --- a/test_regress/t/t_stream_dynamic.v +++ b/test_regress/t/t_stream_dynamic.v @@ -5,6 +5,7 @@ // 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*/); @@ -15,25 +16,44 @@ module t (/*AUTOARG*/); bit [5:0] arr6[$]; string v; bit [5:0] bit6 = 6'b111000; + bit [5:0] ans; { >> bit {arr}} = bit6; v = $sformatf("%p", arr); `checks(v, "'{'h0, 'h0, 'h0, 'h1, 'h1, 'h1} "); + ans = { >> bit {arr} }; + `checkh(ans, bit6); + { << bit {arr}} = bit6; v = $sformatf("%p", arr); `checks(v, "'{'h1, 'h1, 'h1, 'h0, 'h0, 'h0} "); + ans = { << bit {arr} }; + `checkh(ans, bit6); + { >> bit[1:0] {arr2}} = bit6; v = $sformatf("%p", arr2); `checks(v, "'{'h0, 'h2, 'h3} "); + ans = { >> bit[1:0] {arr2} }; + `checkh(ans, bit6); + { << bit[1:0] {arr2}} = bit6; v = $sformatf("%p", arr2); `checks(v, "'{'h3, 'h2, 'h0} "); + ans = { << bit[1:0] {arr2} }; + `checkh(ans, bit6); + { >> bit [5:0] {arr6} } = bit6; v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + ans = { >> bit[5:0] {arr6} }; + `checkh(ans, bit6); + { << bit [5:0] {arr6} } = bit6; v = $sformatf("%p", arr6); `checks(v, "'{'h38} "); + ans = { << bit[5:0] {arr6} }; + `checkh(ans, bit6); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_stream_dynamic_wide_unsup.out b/test_regress/t/t_stream_dynamic_wide_unsup.out new file mode 100644 index 000000000..6ddca3aaf --- /dev/null +++ b/test_regress/t/t_stream_dynamic_wide_unsup.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_stream_dynamic_wide_unsup.v:15:14: Unsupported: Assignment of stream of dynamic array to a variable of size greater than 64 + : ... In instance t + 15 | bit100 = { >> bit {arr} }; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_stream_dynamic_wide_unsup.v:17:14: Unsupported: Assignment of stream of dynamic array to a variable of size greater than 64 + : ... In instance t + 17 | bit100 = { << bit {arr} }; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_stream_dynamic_wide_unsup.pl b/test_regress/t/t_stream_dynamic_wide_unsup.pl new file mode 100755 index 000000000..7be596e0f --- /dev/null +++ b/test_regress/t/t_stream_dynamic_wide_unsup.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 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(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_stream_dynamic_wide_unsup.v b/test_regress/t/t_stream_dynamic_wide_unsup.v new file mode 100644 index 000000000..a9860f430 --- /dev/null +++ b/test_regress/t/t_stream_dynamic_wide_unsup.v @@ -0,0 +1,23 @@ +// 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); + +module t (/*AUTOARG*/); + initial begin + bit[3:0] arr[] = '{25{4'b1000}}; + bit [99:0] bit100; + + bit100 = { >> bit {arr} }; + `checkh(bit100[3:0], 4'b1000); + bit100 = { << bit {arr} }; + `checkh(bit100[3:0], 4'b0001); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule