From 4babba16d60ffc9c9bd7714b929615bfaeff368a Mon Sep 17 00:00:00 2001 From: Valentin Atepalikhin Date: Sun, 9 Jun 2024 05:44:45 +0300 Subject: [PATCH] Support 2D dynamic array initialization (#4700) (#5122) * Support 2D dynamic array initialization (#4700) - new[] on sub arrays (as per original issue) - Built-in methods for sub-arrays - Initialization and literals assignmensts - Dynamic array as an element for other arrays and queues --- docs/CONTRIBUTORS | 1 + src/V3AstNodeDType.h | 5 + src/V3AstNodes.cpp | 7 +- src/V3ParseGrammar.cpp | 2 +- src/V3Width.cpp | 51 +++++-- test_regress/t/t_debug_emitv.out | 8 - test_regress/t/t_dynarray_bits.out | 10 ++ test_regress/t/t_dynarray_bits.pl | 19 +++ test_regress/t/t_dynarray_bits.v | 17 +++ test_regress/t/t_dynarray_multid.pl | 21 +++ test_regress/t/t_dynarray_multid.v | 222 ++++++++++++++++++++++++++++ 11 files changed, 342 insertions(+), 21 deletions(-) create mode 100644 test_regress/t/t_dynarray_bits.out create mode 100755 test_regress/t/t_dynarray_bits.pl create mode 100644 test_regress/t/t_dynarray_bits.v create mode 100755 test_regress/t/t_dynarray_multid.pl create mode 100644 test_regress/t/t_dynarray_multid.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 1f5c199cb..9491dd2fb 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -192,6 +192,7 @@ Tudor Timi Tymoteusz Blazejczyk Udi Finkelstein Unai Martinez-Corral +Valentin Atepalikhin Varun Koyyalagunta Vassilis Papaefstathiou Veripool API Bot diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index da81bf75f..a6a7daeb8 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -1292,6 +1292,11 @@ public: refDTypep(nullptr); dtypep(nullptr); // V3Width will resolve } + AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp) + : ASTGEN_SUPER_UnsizedArrayDType(fl) { + refDTypep(dtp); + dtypep(nullptr); // V3Width will resolve + } ASTGEN_MEMBERS_AstUnsizedArrayDType; const char* broken() const override { BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep()))); diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index e8384f8b1..d48897e19 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -903,9 +903,12 @@ std::pair AstNodeDType::dimensions(bool includeBasic) { } dtypep = adtypep->subDTypep(); continue; - } else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) { + } else if (VN_IS(dtypep, QueueDType) + || VN_IS(dtypep, DynArrayDType) + || VN_IS(dtypep, AssocArrayDType) + || VN_IS(dtypep, WildcardArrayDType)) { unpacked++; - dtypep = qdtypep->subDTypep(); + dtypep = dtypep->subDTypep(); continue; } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) { if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++; diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index cca937820..6df87f7f5 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -140,7 +140,7 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp, rangep}; } else if (VN_IS(nrangep, UnsizedRange)) { - arrayp = new AstUnsizedArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp}; + arrayp = new AstDynArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp}; VL_DO_DANGLING(nrangep->deleteTree(), nrangep); } else if (VN_IS(nrangep, BracketRange)) { const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index bbb7c6e55..f06341c61 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1528,7 +1528,8 @@ class WidthVisitor final : public VNVisitor { case VAttrType::DIM_SIZE: { UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression"); AstNodeDType* const dtypep = nodep->fromp()->dtypep(); - if (VN_IS(dtypep, QueueDType)) { + if (VN_IS(dtypep, QueueDType) + || VN_IS(dtypep, DynArrayDType)) { switch (nodep->attrType()) { case VAttrType::DIM_SIZE: { AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr); @@ -1570,7 +1571,11 @@ class WidthVisitor final : public VNVisitor { break; } case VAttrType::DIM_BITS: { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue"); + if (VN_IS(dtypep, DynArrayDType)) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for dynamic array"); + } else { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue"); + } break; } default: nodep->v3fatalSrc("Unhandled attribute type"); @@ -2151,14 +2156,32 @@ class WidthVisitor final : public VNVisitor { // Make sure dtype is sized nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var"); - if (const AstUnsizedArrayDType* const unsizedp - = VN_CAST(nodep->dtypeSkipRefp(), UnsizedArrayDType)) { - if (!(m_ftaskp && m_ftaskp->dpiImport())) { - UINFO(9, "Unsized becomes dynamic array " << nodep << endl); - AstDynArrayDType* const newp - = new AstDynArrayDType{unsizedp->fileline(), unsizedp->subDTypep()}; - nodep->dtypep(newp); - v3Global.rootp()->typeTablep()->addTypesp(newp); + if (m_ftaskp && m_ftaskp->dpiImport()) { + AstNodeDType *dtp = nodep->dtypep(); + AstNodeDType *np = nullptr; + while (VN_IS(dtp->skipRefp(), DynArrayDType) + || VN_IS(dtp->skipRefp(), UnpackArrayDType)) { + if (const AstDynArrayDType* const unsizedp + = VN_CAST(dtp->skipRefp(), DynArrayDType)) { + if (!np) { + UINFO(9, "Dynamic becomes unsized array (var itself) " << nodep << endl); + } else { + UINFO(9, "Dynamic becomes unsized array (subDType) " << dtp << endl); + } + AstUnsizedArrayDType* const newp + = new AstUnsizedArrayDType{unsizedp->fileline(), unsizedp->subDTypep()}; + newp->dtypep(newp); + if (!np) { // for Var itself + nodep->dtypep(newp); + } else { // for subDType + np->virtRefDTypep(newp); + } + v3Global.rootp()->typeTablep()->addTypesp(newp); + np = newp; + } else { + np = dtp->skipRefp(); + } + dtp = np->virtRefDTypep(); } } if (AstWildcardArrayDType* const wildp @@ -2458,6 +2481,7 @@ class WidthVisitor final : public VNVisitor { if (m_vup->prelim()) { userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); + if (nodep->didWidthAndSet()) return; nodep->dtypeFrom(vdtypep); } if (m_vup->final()) { @@ -2480,6 +2504,7 @@ class WidthVisitor final : public VNVisitor { iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL); } } + if (nodep->didWidthAndSet()) return; nodep->dtypeFrom(vdtypep); } } @@ -3348,6 +3373,12 @@ class WidthVisitor final : public VNVisitor { return VN_AS(nodep->pinsp(), Arg)->exprp(); } void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) { + if (const AstCMethodHard* const ichildp = VN_CAST(childp, CMethodHard)) { + if (ichildp->name() == "at") { + methodCallLValueRecurse(nodep, ichildp->fromp(), access); + return; + } + } if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) { varrefp->access(access); } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) { diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 2fcd6e906..5cbdc05a6 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -20,8 +20,6 @@ module Vt_debug_emitv_t; signed int [31:0] string ???? // ASSOCARRAYDTYPE signed int [31:0] - ???? // UNSIZEDARRAYDTYPE - ???? // DYNARRAYDTYPE signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] e_t; typedef struct packed @@ -37,8 +35,6 @@ module Vt_debug_emitv_t; signed int [31:0] string ???? // ASSOCARRAYDTYPE signed int [31:0] - ???? // UNSIZEDARRAYDTYPE - ???? // DYNARRAYDTYPE signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] ps_t; typedef struct @@ -53,8 +49,6 @@ module Vt_debug_emitv_t; signed int [31:0] string ???? // ASSOCARRAYDTYPE signed int [31:0] - ???? // UNSIZEDARRAYDTYPE - ???? // DYNARRAYDTYPE signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] us_t; typedef union @@ -68,8 +62,6 @@ module Vt_debug_emitv_t; signed int [31:0] string ???? // ASSOCARRAYDTYPE signed int [31:0] - ???? // UNSIZEDARRAYDTYPE - ???? // DYNARRAYDTYPE signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] union_t; struct packed diff --git a/test_regress/t/t_dynarray_bits.out b/test_regress/t/t_dynarray_bits.out new file mode 100644 index 000000000..7ea677817 --- /dev/null +++ b/test_regress/t/t_dynarray_bits.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_dynarray_bits.v:12:11: Unsupported: $bits for dynamic array + : ... note: In instance 't' + 12 | if ($bits(a) != 0) $stop; + | ^~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Internal Error: t/t_dynarray_bits.v:12:20: ../V3Width.cpp:#: Node has no type + : ... note: In instance 't' + 12 | if ($bits(a) != 0) $stop; + | ^~ + ... See the manual at https://verilator.org/verilator_doc.html for more assistance. diff --git a/test_regress/t/t_dynarray_bits.pl b/test_regress/t/t_dynarray_bits.pl new file mode 100755 index 000000000..27159da5b --- /dev/null +++ b/test_regress/t/t_dynarray_bits.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 2019 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_dynarray_bits.v b/test_regress/t/t_dynarray_bits.v new file mode 100644 index 000000000..1c0e76b15 --- /dev/null +++ b/test_regress/t/t_dynarray_bits.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + integer a[]; + + initial begin + if ($bits(a) != 0) $stop; + a = new [10]; + if ($bits(a) != 10*32) $stop; + end + +endmodule diff --git a/test_regress/t/t_dynarray_multid.pl b/test_regress/t/t_dynarray_multid.pl new file mode 100755 index 000000000..9a15dd2cc --- /dev/null +++ b/test_regress/t/t_dynarray_multid.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 2019 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_dynarray_multid.v b/test_regress/t/t_dynarray_multid.v new file mode 100644 index 000000000..dd73a10cb --- /dev/null +++ b/test_regress/t/t_dynarray_multid.v @@ -0,0 +1,222 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// 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*/); + + integer a1 [][]; + integer a2 [2][]; + + integer a3 [][] = '{'{1, 2, 3}, '{4, 5, 6}}; + integer a4 [][] = '{{7, 8, 9}, {10, 11, 12}}; + integer a5 [][] = '{3{'{13, 14}}}; + + integer aa1 [string][]; + integer wa1 [*][]; + integer qa1 [$][]; + struct { + integer i; + integer a[]; + } s1; + + integer a[] = '{1,2,3}; + + logic [7:0][3:0] a6 [][]; + + initial begin + `checkh(a1.size, 0); + a1 = new [3]; + `checkh(a1.size, 3); + `checkh($size(a1), 3); + `checkh($high(a1), 2); + `checkh($right(a1), 2); + + foreach (a1[i]) a1[i] = new [i + 1]; + + foreach (a1[i]) begin + `checkh(a1[i].size, i + 1); + `checkh($size(a1[i]), i + 1); + `checkh($high(a1[i]), i); + `checkh($right(a1[i]), i); + end + + foreach (a1[i, j]) a1[i][j] = i * 10 + j; + + `checkh(a1[0][0], 0); + `checkh(a1[1][0], 10); + `checkh(a1[1][1], 11); + `checkh(a1[2][0], 20); + `checkh(a1[2][1], 21); + `checkh(a1[2][2], 22); + + `checkh(a1[2].sum, 63); + + foreach (a1[i]) a1[i].delete; + foreach (a1[i]) begin + `checkh(a1[i].size, 0); + end + + a1.delete; + `checkh(a1.size, 0); + + a1 = new [2]; + `checkh(a1.size, 2); + + foreach (a1[i]) a1[i] = new [i + 2]; + foreach (a1[i]) begin + `checkh(a1[i].size, i + 2); + end + + foreach (a2[i]) begin + `checkh(a2[i].size, 0); + end + foreach (a2[i]) a2[i] = new [i + 1]; + foreach (a2[i]) begin + `checkh(a2[i].size, i + 1); + end + + foreach (a2[i]) a2[i].delete; + foreach (a2[i]) begin + `checkh(a2[i].size, 0); + end + + `checkh(a3.size, 2); + foreach (a3[i]) begin + `checkh(a3[i].size, 3); + end + + `checkh(a3[0][0], 1); + `checkh(a3[0][1], 2); + `checkh(a3[0][2], 3); + `checkh(a3[1][0], 4); + `checkh(a3[1][1], 5); + `checkh(a3[1][2], 6); + + `checkh(a4.size, 2); + foreach (a4[i]) begin + `checkh(a4[i].size, 3); + end + + `checkh(a4[0][0], 7); + `checkh(a4[0][1], 8); + `checkh(a4[0][2], 9); + `checkh(a4[1][0], 10); + `checkh(a4[1][1], 11); + `checkh(a4[1][2], 12); + + `checkh(a5.size, 3); + foreach (a5[i]) begin + `checkh(a5[i].size, 2); + end + + `checkh(a5[0][0], 13); + `checkh(a5[0][1], 14); + `checkh(a5[1][0], 13); + `checkh(a5[1][1], 14); + `checkh(a5[2][0], 13); + `checkh(a5[2][1], 14); + + a5 = a4; + `checkh(a5.size, 2); + foreach (a5[i]) begin + `checkh(a5[i].size, 3); + end + + `checkh(a5[0][0], 7); + `checkh(a5[0][1], 8); + `checkh(a5[0][2], 9); + `checkh(a5[1][0], 10); + `checkh(a5[1][1], 11); + `checkh(a5[1][2], 12); + + a4 = '{'{15, 16}, '{17, 18}}; + + `checkh(a4.size, 2); + foreach (a4[i]) begin + `checkh(a4[i].size, 2); + end + + `checkh(a4[0][0], 15); + `checkh(a4[0][1], 16); + `checkh(a4[1][0], 17); + `checkh(a4[1][1], 18); + + a4 = '{{19}, {20}, {21, 22}}; + + `checkh(a4.size, 3); + `checkh(a4[0].size, 1); + `checkh(a4[1].size, 1); + `checkh(a4[2].size, 2); + + `checkh(a4[0][0], 19); + `checkh(a4[1][0], 20); + `checkh(a4[2][0], 21); + `checkh(a4[2][1], 22); + + a5 = '{2{a}}; + + `checkh(a5.size, 2); + foreach (a5[i]) begin + `checkh(a5[i].size, 3); + end + + `checkh(a5[0][0], 1); + `checkh(a5[0][1], 2); + `checkh(a5[0][2], 3); + `checkh(a5[1][0], 1); + `checkh(a5[1][1], 2); + `checkh(a5[1][2], 3); + + a5 = '{}; + `checkh(a5.size, 0); + + a5 = '{2{'{}}}; + + `checkh(a5.size, 2); + foreach (a5[i]) begin + `checkh(a5[i].size, 0); + end + + aa1["k1"] = new [3]; + `checkh(aa1["k1"].size, 3); + aa1["k1"].delete; + + wa1[1] = new [3]; + `checkh(wa1[1].size, 3); + wa1[1].delete; + + qa1.push_back(a); + `checkh(qa1[0].size, 3); + qa1[0] = new [4]; + `checkh(qa1[0].size, 4); + qa1[0].delete; + + s1.a = new [4]; + `checkh(s1.a.size, 4); + s1.a.delete; + + `checkh($dimensions(a1), 3); + `checkh($dimensions(a2), 3); + `checkh($dimensions(aa1), 3); + `checkh($dimensions(wa1), 3); + `checkh($dimensions(qa1), 3); + `checkh($dimensions(a), 2); + `checkh($dimensions(a6), 4); + `checkh($unpacked_dimensions(a1), 2); + `checkh($unpacked_dimensions(a2), 2); + `checkh($unpacked_dimensions(aa1), 2); + `checkh($unpacked_dimensions(wa1), 2); + `checkh($unpacked_dimensions(qa1), 2); + `checkh($unpacked_dimensions(a), 1); + `checkh($unpacked_dimensions(a6), 2); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule