diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 3282e9ef2..3df645406 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -56,6 +56,9 @@ class SliceVisitor final : public VNVisitor { // AstNodeUniop::user1() -> bool. True if find is complete // AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to const VNUser1InUse m_inuser1; + // AstInitArray::user2() -> Previously accessed itemIdx + // AstInitItem::user2() -> Corresponding first elemIdx + const VNUser2InUse m_inuser2; // STATE AstNode* m_assignp = nullptr; // Assignment we are under @@ -63,7 +66,8 @@ class SliceVisitor final : public VNVisitor { bool m_okInitArray = false; // Allow InitArray children // METHODS - AstNodeExpr* cloneAndSel(AstNode* nodep, int elements, int offset) { + + AstNodeExpr* cloneAndSel(AstNode* nodep, int elements, int elemIdx) { // Insert an ArraySel, except for a few special cases const AstUnpackArrayDType* const arrayp = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); @@ -87,40 +91,126 @@ class SliceVisitor final : public VNVisitor { } m_assignError = true; elements = 1; - offset = 0; + elemIdx = 0; } AstNodeExpr* newp; - if (const AstInitArray* const initp = VN_CAST(nodep, InitArray)) { - UINFO(9, " cloneInitArray(" << elements << "," << offset << ") " << nodep << endl); - const int leOffset = !arrayp->rangep()->ascending() - ? arrayp->rangep()->elementsConst() - 1 - offset - : offset; - AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(leOffset); - if (!itemp) { - nodep->v3error("Array initialization has too few elements, need element " - << offset); + if (AstInitArray* const initp = VN_CAST(nodep, InitArray)) { + UINFO(9, " cloneInitArray(" << elements << "," << elemIdx << ") " << nodep << endl); + + auto considerOrder = [](const auto* nodep, int idxFromLeft) -> int { + return !nodep->rangep()->ascending() + ? nodep->rangep()->elementsConst() - 1 - idxFromLeft + : idxFromLeft; + }; + newp = nullptr; + int itemIdx = 0; + int i = 0; + if (const int prevItemIdx = initp->user2()) { + const AstInitArray::KeyItemMap& itemMap = initp->map(); + const auto it = itemMap.find(considerOrder(arrayp, prevItemIdx)); + if (it != itemMap.end()) { + const AstInitItem* itemp = it->second; + if (itemp->user2() && itemp->user2() < elemIdx) { + // Let's resume traversal from the previous position + itemIdx = prevItemIdx; + i = itemp->user2(); + } + } } - newp = itemp ? itemp->cloneTreePure(false) : new AstConst{nodep->fileline(), 0}; + const AstNodeDType* const expectedItemDTypep = arrayp->subDTypep()->skipRefp(); + while (i <= elemIdx) { + AstNodeExpr* const itemp + = initp->getIndexDefaultedValuep(considerOrder(arrayp, itemIdx)); + if (!itemp && !m_assignError) { + nodep->v3error("Array initialization has too few elements, need element " + << elemIdx); + m_assignError = true; + } + const AstNodeDType* itemRawDTypep = itemp->dtypep()->skipRefp(); + const VCastable castable + = AstNode::computeCastable(expectedItemDTypep, itemRawDTypep, itemp); + if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) { + if (i == elemIdx) { + newp = itemp->cloneTreePure(false); + break; + } else { // Check the next item + ++i; + ++itemIdx; + } + } else { + const AstUnpackArrayDType* const itemDTypep + = VN_CAST(itemRawDTypep, UnpackArrayDType); + if (!itemDTypep + || !expectedItemDTypep->same(itemDTypep->subDTypep()->skipRefp())) { + if (!m_assignError) { + itemp->v3error("Item is incompatible with the array type."); + } + m_assignError = true; + break; + } + if (i + itemDTypep->elementsConst() + > elemIdx) { // This item contains the element + int offset = considerOrder(itemDTypep, elemIdx - i); + if (AstSliceSel* const slicep = VN_CAST(itemp, SliceSel)) { + offset += slicep->declRange().lo(); + newp = new AstArraySel{nodep->fileline(), + slicep->lhsp()->cloneTreePure(false), offset}; + } else { + newp = new AstArraySel{nodep->fileline(), itemp->cloneTreePure(false), + offset}; + } + + if (!m_assignError && elemIdx + 1 == elements + && i + itemDTypep->elementsConst() > elements) { + nodep->v3error("Array initialization has too many elements. " + << elements << " elements are expected, but at least " + << i + itemDTypep->elementsConst() + << " elements exist."); + m_assignError = true; + } + break; + } else { // Check the next item + i += itemDTypep->elementsConst(); + ++itemIdx; + } + } + } + if (elemIdx + 1 == elements && static_cast(itemIdx) + 1 < initp->map().size() + && !m_assignError) { + nodep->v3error("Array initialization has too many elements. " + << elements << " elements are expected, but at least " + << i + initp->map().size() - itemIdx << " elements exist."); + m_assignError = true; + } + if (newp) { + const AstInitArray::KeyItemMap& itemMap = initp->map(); + const auto it = itemMap.find(considerOrder(arrayp, itemIdx)); + if (it != itemMap.end()) { // Remember current position for the next invocation. + initp->user2(itemIdx); + it->second->user2(i); + } + } + if (!newp) newp = new AstConst{nodep->fileline(), 0}; } else if (AstNodeCond* const snodep = VN_CAST(nodep, NodeCond)) { - UINFO(9, " cloneCond(" << elements << "," << offset << ") " << nodep << endl); + UINFO(9, " cloneCond(" << elements << "," << elemIdx << ") " << nodep << endl); return snodep->cloneType(snodep->condp()->cloneTreePure(false), - cloneAndSel(snodep->thenp(), elements, offset), - cloneAndSel(snodep->elsep(), elements, offset)); + cloneAndSel(snodep->thenp(), elements, elemIdx), + cloneAndSel(snodep->elsep(), elements, elemIdx)); } else if (const AstSliceSel* const snodep = VN_CAST(nodep, SliceSel)) { - UINFO(9, " cloneSliceSel(" << elements << "," << offset << ") " << nodep << endl); + UINFO(9, " cloneSliceSel(" << elements << "," << elemIdx << ") " << nodep << endl); const int leOffset = (snodep->declRange().lo() + (!snodep->declRange().ascending() - ? snodep->declRange().elements() - 1 - offset - : offset)); + ? snodep->declRange().elements() - 1 - elemIdx + : elemIdx)); newp = new AstArraySel{nodep->fileline(), snodep->fromp()->cloneTreePure(false), leOffset}; } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel) || VN_IS(nodep, CMethodHard) || VN_IS(nodep, MemberSel) || VN_IS(nodep, ExprStmt)) { - UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); + UINFO(9, " cloneSel(" << elements << "," << elemIdx << ") " << nodep << endl); const int leOffset = !arrayp->rangep()->ascending() - ? arrayp->rangep()->elementsConst() - 1 - offset - : offset; + ? arrayp->rangep()->elementsConst() - 1 - elemIdx + : elemIdx; newp = new AstArraySel{nodep->fileline(), VN_AS(nodep, NodeExpr)->cloneTreePure(false), leOffset}; } else { @@ -149,10 +239,10 @@ class SliceVisitor final : public VNVisitor { // elements AstNodeAssign* newlistp = nullptr; const int elements = arrayp->rangep()->elementsConst(); - for (int offset = 0; offset < elements; ++offset) { + for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { AstNodeAssign* const newp - = nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, offset), - cloneAndSel(nodep->rhsp(), elements, offset)); + = nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx), + cloneAndSel(nodep->rhsp(), elements, elemIdx)); if (debug() >= 9) newp->dumpTree("- new: "); newlistp = AstNode::addNext(newlistp, newp); } @@ -199,12 +289,12 @@ class SliceVisitor final : public VNVisitor { << " on non-slicable (e.g. non-vector) right-hand-side operand"); } else { const int elements = adtypep->rangep()->elementsConst(); - for (int offset = 0; offset < elements; ++offset) { + for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) - AstNodeBiop* const clonep - = VN_AS(nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, offset), - cloneAndSel(nodep->rhsp(), elements, offset)), - NodeBiop); + AstNodeBiop* const clonep = VN_AS( + nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx), + cloneAndSel(nodep->rhsp(), elements, elemIdx)), + NodeBiop); if (!logp) { logp = clonep; } else { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index cecd6ad1b..cc6f2551d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4120,6 +4120,7 @@ private: PatVecMap patmap = patVectorMap(nodep, range); UINFO(9, "ent " << range.left() << " to " << range.right() << endl); AstNode* newp = nullptr; + bool allConstant = true; for (int entn = 0, ent = range.left(); entn < range.elements(); ++entn, ent += range.leftToRightInc()) { AstPatMember* newpatp = nullptr; @@ -4129,7 +4130,10 @@ private: if (defaultp) { newpatp = defaultp->cloneTree(false); patp = newpatp; - } else { + } else if (!(VN_IS(arrayDtp, UnpackArrayDType) && !allConstant)) { + // If arrayDtp is an unpacked array and item is not constant, + // the number of elemnt cannot be determined here as the dtype of each element + // is not set yet. V3Slice checks for such cases. nodep->v3error("Assignment pattern missed initializing elements: " << ent); } } else { @@ -4140,6 +4144,7 @@ private: if (patp) { // Don't want the RHS an array patp->dtypep(arrayDtp->subDTypep()); + allConstant &= VN_IS(patp->lhssp(), Const); AstNodeExpr* const valuep = patternMemberValueIterate(patp); if (VN_IS(arrayDtp, UnpackArrayDType)) { if (!newp) { diff --git a/test_regress/t/t_unpacked_concat.pl b/test_regress/t/t_unpacked_concat.pl new file mode 100755 index 000000000..354bf40cc --- /dev/null +++ b/test_regress/t/t_unpacked_concat.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_unpacked_concat.v b/test_regress/t/t_unpacked_concat.v new file mode 100644 index 000000000..1c73f8ffd --- /dev/null +++ b/test_regress/t/t_unpacked_concat.v @@ -0,0 +1,102 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Yutetsu TAKATSUKASA. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + typedef int AI3[1:3]; + AI3 A3; + int A9[1:9]; + + logic [2:0] s0; + logic [2:0] s1[1:3]; + logic [2:0] s2[3:1]; + logic [2:0] s3[2:8]; + logic [2:0] s4[8:2]; + + initial begin + s0 = 3'd1; + s1[1] = 3'd2; + s1[2] = 3'd3; + s1[3] = 3'd4; + s2[1] = 3'd5; + s2[2] = 3'd6; + s2[3] = 3'd7; + + A3 = '{1, 2, 3}; + A9 = {A3, 4, 5, A3, 6}; + if (A9[1] != 1) $stop; + if (A9[2] != 2) $stop; + if (A9[3] != 3) $stop; + if (A9[4] != 4) $stop; + if (A9[5] != 5) $stop; + if (A9[6] != 1) $stop; + if (A9[7] != 2) $stop; + if (A9[8] != 3) $stop; + if (A9[9] != 6) $stop; + + s3 = {s0, s1, s2}; + if (s3[2] != s0) $stop; + if (s3[3] != s1[1]) $stop; + if (s3[4] != s1[2]) $stop; + if (s3[5] != s1[3]) $stop; + if (s3[6] != s2[3]) $stop; + if (s3[7] != s2[2]) $stop; + if (s3[8] != s2[1]) $stop; + + s3[2:8] = {s0, s1[1:2], s1[3], s2[3], s2[2:1]}; + if (s3[2] != s0) $stop; + if (s3[3] != s1[1]) $stop; + if (s3[4] != s1[2]) $stop; + if (s3[5] != s1[3]) $stop; + if (s3[6] != s2[3]) $stop; + if (s3[7] != s2[2]) $stop; + if (s3[8] != s2[1]) $stop; + + s3 = {s0, s1[1], s1[2:3], s2[3:2], s2[1]}; + if (s3[2] != s0) $stop; + if (s3[3] != s1[1]) $stop; + if (s3[4] != s1[2]) $stop; + if (s3[5] != s1[3]) $stop; + if (s3[6] != s2[3]) $stop; + if (s3[7] != s2[2]) $stop; + if (s3[8] != s2[1]) $stop; + + s4 = {s0, s1, s2}; + if (s4[8] != s0) $stop; + if (s4[7] != s1[1]) $stop; + if (s4[6] != s1[2]) $stop; + if (s4[5] != s1[3]) $stop; + if (s4[4] != s2[3]) $stop; + if (s4[3] != s2[2]) $stop; + if (s4[2] != s2[1]) $stop; + + s4[8:2] = {s0, s1[1:2], s1[3], s2[3], s2[2:1]}; + if (s4[8] != s0) $stop; + if (s4[7] != s1[1]) $stop; + if (s4[6] != s1[2]) $stop; + if (s4[5] != s1[3]) $stop; + if (s4[4] != s2[3]) $stop; + if (s4[3] != s2[2]) $stop; + if (s4[2] != s2[1]) $stop; + + s4 = {s0, s1[1], s1[2:3], s2[3:2], s2[1]}; + if (s4[8] != s0) $stop; + if (s4[7] != s1[1]) $stop; + if (s4[6] != s1[2]) $stop; + if (s4[5] != s1[3]) $stop; + if (s4[4] != s2[3]) $stop; + if (s4[3] != s2[2]) $stop; + if (s4[2] != s2[1]) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_unpacked_concat_bad.out b/test_regress/t/t_unpacked_concat_bad.out index 4f2954473..e2c0f4797 100644 --- a/test_regress/t/t_unpacked_concat_bad.out +++ b/test_regress/t/t_unpacked_concat_bad.out @@ -3,8 +3,4 @@ 17 | localparam bit_int_t count_bits [1:0] = {2{$bits(count_t)}}; | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_unpacked_concat_bad.v:17:46: Assignment pattern missed initializing elements: 0 - : ... note: In instance 't' - 17 | localparam bit_int_t count_bits [1:0] = {2{$bits(count_t)}}; - | ^ %Error: Exiting due to diff --git a/test_regress/t/t_unpacked_concat_bad2.out b/test_regress/t/t_unpacked_concat_bad2.out new file mode 100644 index 000000000..1a5e93630 --- /dev/null +++ b/test_regress/t/t_unpacked_concat_bad2.out @@ -0,0 +1,13 @@ +%Error: t/t_unpacked_concat_bad2.v:25:15: Array initialization has too many elements. 2 elements are expected, but at least 5 elements exist. + 25 | s1 = {s0, s2}; + | ^ +%Error: t/t_unpacked_concat_bad2.v:26:23: Array initialization has too many elements. 4 elements are expected, but at least 5 elements exist. + 26 | s2 = {s1, s0, s0, s0}; + | ^ +%Error: t/t_unpacked_concat_bad2.v:28:17: Item is incompatible with the array type. + 28 | s2 = {s0, s3}; + | ^~ +%Error: t/t_unpacked_concat_bad2.v:30:19: Item is incompatible with the array type. + 30 | A9_logic = {A3, 4, 5, A3, 6}; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_unpacked_concat_bad2.pl b/test_regress/t/t_unpacked_concat_bad2.pl new file mode 100755 index 000000000..63947fd00 --- /dev/null +++ b/test_regress/t/t_unpacked_concat_bad2.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(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_unpacked_concat_bad2.v b/test_regress/t/t_unpacked_concat_bad2.v new file mode 100644 index 000000000..ba3800237 --- /dev/null +++ b/test_regress/t/t_unpacked_concat_bad2.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Yutetsu TAKATSUKASA. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + logic [7:0] s0; + logic [7:0] s1[1:2]; + logic [7:0] s2[1:4]; + logic [7:0] s3[2][2]; + + typedef int AI3[1:3]; + AI3 A3; + logic [31:0] A9_logic[1:9]; + + initial begin + // RHS has too many elements. + s1 = {s0, s2}; + s2 = {s1, s0, s0, s0}; + // Incompatible type + s2 = {s0, s3}; + + A9_logic = {A3, 4, 5, A3, 6}; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule