diff --git a/Changes b/Changes index 907b3acc0..910cba754 100644 --- a/Changes +++ b/Changes @@ -42,6 +42,7 @@ Verilator 5.027 devel * Fix randomization when used with inheritance (#5268). [Krzysztof Bieganski, Antmicro Ltd.] * Fix inline constraints creating class random generator (#5280). [Krzysztof Bieganski, Antmicro Ltd.] * Fix elaborating foreach loops (#5285). [Arkadiusz Kozdra, Antmicro Ltd.] +* Fix initializing static array in dynamic arrays and queues (#5287). [Baruch Sterin] * Fix randomizing current object with `rand` class instance member (#5292). [Krzysztof Bieganski, Antmicro Ltd.] diff --git a/include/verilated_types.h b/include/verilated_types.h index 4affc6e4b..fa99d32e5 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -512,28 +512,29 @@ public: return *this; } - static VlQueue cons(const T_Value& lhs) { + // Construct new object from _V_alue and/or _C_ontainer child objects + static VlQueue consV(const T_Value& lhs) { VlQueue out; out.push_back(lhs); return out; } - static VlQueue cons(const T_Value& lhs, const T_Value& rhs) { + static VlQueue consVV(const T_Value& lhs, const T_Value& rhs) { VlQueue out; out.push_back(rhs); out.push_back(lhs); return out; } - static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) { + static VlQueue consCV(const VlQueue& lhs, const T_Value& rhs) { VlQueue out = lhs; out.push_front(rhs); return out; } - static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) { + static VlQueue consVC(const T_Value& lhs, const VlQueue& rhs) { VlQueue out = rhs; out.push_back(lhs); return out; } - static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) { + static VlQueue consCC(const VlQueue& lhs, const VlQueue& rhs) { VlQueue out = rhs; for (const auto& i : lhs.m_deque) out.push_back(i); return out; @@ -751,7 +752,7 @@ public: // Can't use std::find_if as need index number IData index = 0; for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(i); + if (with_func(index, i)) return VlQueue::consV(i); ++index; } return VlQueue{}; @@ -760,7 +761,7 @@ public: VlQueue find_first_index(Func with_func) const { IData index = 0; for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(index); + if (with_func(index, i)) return VlQueue::consV(index); ++index; } return VlQueue{}; @@ -769,7 +770,7 @@ public: VlQueue find_last(Func with_func) const { IData index = m_deque.size() - 1; for (auto& item : vlstd::reverse_view(m_deque)) { - if (with_func(index, item)) return VlQueue::cons(item); + if (with_func(index, item)) return VlQueue::consV(item); --index; } return VlQueue{}; @@ -778,7 +779,7 @@ public: VlQueue find_last_index(Func with_func) const { IData index = m_deque.size() - 1; for (auto& item : vlstd::reverse_view(m_deque)) { - if (with_func(index, item)) return VlQueue::cons(index); + if (with_func(index, item)) return VlQueue::consV(index); --index; } return VlQueue{}; @@ -788,7 +789,7 @@ public: VlQueue min() const { if (m_deque.empty()) return VlQueue{}; const auto it = std::min_element(m_deque.cbegin(), m_deque.cend()); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } template VlQueue min(Func with_func) const { @@ -797,12 +798,12 @@ public: [&with_func](const IData& a, const IData& b) { return with_func(0, a) < with_func(0, b); }); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } VlQueue max() const { if (m_deque.empty()) return VlQueue{}; const auto it = std::max_element(m_deque.cbegin(), m_deque.cend()); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } template VlQueue max(Func with_func) const { @@ -811,7 +812,7 @@ public: [&with_func](const IData& a, const IData& b) { return with_func(0, a) < with_func(0, b); }); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } T_Value r_sum() const { @@ -1092,7 +1093,7 @@ public: return with_func(i.first, i.second); }); if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } template VlQueue find_first_index(Func with_func) const { @@ -1101,7 +1102,7 @@ public: return with_func(i.first, i.second); }); if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->first); + return VlQueue::consV(it->first); } template VlQueue find_last(Func with_func) const { @@ -1109,7 +1110,7 @@ public: m_map.crbegin(), m_map.crend(), [=](const std::pair& i) { return with_func(i.first, i.second); }); if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } template VlQueue find_last_index(Func with_func) const { @@ -1117,7 +1118,7 @@ public: m_map.crbegin(), m_map.crend(), [=](const std::pair& i) { return with_func(i.first, i.second); }); if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->first); + return VlQueue::consV(it->first); } // Reduction operators @@ -1128,7 +1129,7 @@ public: [](const std::pair& a, const std::pair& b) { return a.second < b.second; }); - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } template VlQueue min(Func with_func) const { @@ -1138,7 +1139,7 @@ public: [&with_func](const std::pair& a, const std::pair& b) { return with_func(a.first, a.second) < with_func(b.first, b.second); }); - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } VlQueue max() const { if (m_map.empty()) return VlQueue(); @@ -1147,7 +1148,7 @@ public: [](const std::pair& a, const std::pair& b) { return a.second < b.second; }); - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } template VlQueue max(Func with_func) const { @@ -1157,7 +1158,7 @@ public: [&with_func](const std::pair& a, const std::pair& b) { return with_func(a.first, a.second) < with_func(b.first, b.second); }); - return VlQueue::cons(it->second); + return VlQueue::consV(it->second); } T_Value r_sum() const { @@ -1438,7 +1439,7 @@ public: // Can't use std::find_if as need index number IData index = 0; for (const auto& i : m_storage) { - if (with_func(index, i)) return VlQueue::cons(i); + if (with_func(index, i)) return VlQueue::consV(i); ++index; } return VlQueue{}; @@ -1447,7 +1448,7 @@ public: VlQueue find_first_index(Func with_func) const { IData index = 0; for (const auto& i : m_storage) { - if (with_func(index, i)) return VlQueue::cons(index); + if (with_func(index, i)) return VlQueue::consV(index); ++index; } return VlQueue{}; @@ -1455,14 +1456,14 @@ public: template VlQueue find_last(Func with_func) const { for (int i = T_Depth - 1; i >= 0; i--) { - if (with_func(i, m_storage[i])) return VlQueue::cons(m_storage[i]); + if (with_func(i, m_storage[i])) return VlQueue::consV(m_storage[i]); } return VlQueue{}; } template VlQueue find_last_index(Func with_func) const { for (int i = T_Depth - 1; i >= 0; i--) { - if (with_func(i, m_storage[i])) return VlQueue::cons(i); + if (with_func(i, m_storage[i])) return VlQueue::consV(i); } return VlQueue{}; } @@ -1470,7 +1471,7 @@ public: // Reduction operators VlQueue min() const { const auto it = std::min_element(std::begin(m_storage), std::end(m_storage)); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } template VlQueue min(Func with_func) const { @@ -1478,11 +1479,11 @@ public: [&with_func](const IData& a, const IData& b) { return with_func(0, a) < with_func(0, b); }); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } VlQueue max() const { const auto it = std::max_element(std::begin(m_storage), std::end(m_storage)); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } template VlQueue max(Func with_func) const { @@ -1490,7 +1491,7 @@ public: [&with_func](const IData& a, const IData& b) { return with_func(0, a) < with_func(0, b); }); - return VlQueue::cons(*it); + return VlQueue::consV(*it); } // Dumping. Verilog: str = $sformatf("%p", assoc) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 4956420e9..e0a15b40d 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1585,7 +1585,7 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp); UINFO(9, " castable=" << castable << " for " << toDtp << endl); UINFO(9, " =?= " << fromDtp << endl); - UINFO(9, " const= " << fromConstp << endl); + if (fromConstp) UINFO(9, " const= " << fromConstp << endl); return castable; } diff --git a/src/V3Ast.h b/src/V3Ast.h index f6d42fdaf..10fd44c1e 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1537,6 +1537,7 @@ public: "ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"}; return names[m_e]; } + bool isAssignable() const { return m_e != UNSUPPORTED && m_e != INCOMPATIBLE; } VCastable() : m_e{UNSUPPORTED} {} // cppcheck-suppress noExplicitConstructor diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index f8d35f2d1..5e4057447 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -810,19 +810,38 @@ class AstConsDynArray final : public AstNodeExpr { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // @astgen op1 := lhsp : Optional[AstNode] // @astgen op2 := rhsp : Optional[AstNode] + const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat + const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat public: - explicit AstConsDynArray(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr) - : ASTGEN_SUPER_ConsDynArray(fl) { + explicit AstConsDynArray(FileLine* fl) + : ASTGEN_SUPER_ConsDynArray(fl) {} + explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp) + : ASTGEN_SUPER_ConsDynArray(fl) + , m_lhsIsValue(lhsIsValue) { + this->lhsp(lhsp); + } + explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue, + AstNode* rhsp) + : ASTGEN_SUPER_ConsDynArray(fl) + , m_lhsIsValue(lhsIsValue) + , m_rhsIsValue(rhsIsValue) { this->lhsp(lhsp); this->rhsp(rhsp); } ASTGEN_MEMBERS_AstConsDynArray; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; string emitVerilog() override { return "'{%l, %r}"; } string emitC() override { V3ERROR_NA_RETURN(""); } string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } int instrCount() const override { return widthInstrs(); } - bool same(const AstNode* /*samep*/) const override { return true; } + bool same(const AstNode* samep) const override { + const AstConsDynArray* const sp = VN_DBG_AS(samep, ConsDynArray); + return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue; + } + bool lhsIsValue() const { return m_lhsIsValue; } + bool rhsIsValue() const { return m_rhsIsValue; } }; class AstConsPackMember final : public AstNodeExpr { // Construct a packed array single emement [member1: value1] @@ -873,19 +892,38 @@ class AstConsQueue final : public AstNodeExpr { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // @astgen op1 := lhsp : Optional[AstNode] // @astgen op2 := rhsp : Optional[AstNode] + const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat + const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat public: - explicit AstConsQueue(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr) - : ASTGEN_SUPER_ConsQueue(fl) { + explicit AstConsQueue(FileLine* fl) + : ASTGEN_SUPER_ConsQueue(fl) {} + explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp) + : ASTGEN_SUPER_ConsQueue(fl) + , m_lhsIsValue(lhsIsValue) { + this->lhsp(lhsp); + } + explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue, + AstNode* rhsp) + : ASTGEN_SUPER_ConsQueue(fl) + , m_lhsIsValue(lhsIsValue) + , m_rhsIsValue(rhsIsValue) { this->lhsp(lhsp); this->rhsp(rhsp); } ASTGEN_MEMBERS_AstConsQueue; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; string emitVerilog() override { return "'{%l, %r}"; } string emitC() override { V3ERROR_NA_RETURN(""); } string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } int instrCount() const override { return widthInstrs(); } - bool same(const AstNode* /*samep*/) const override { return true; } + bool same(const AstNode* samep) const override { + const AstConsQueue* const sp = VN_DBG_AS(samep, ConsQueue); + return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue; + } + bool lhsIsValue() const { return m_lhsIsValue; } + bool rhsIsValue() const { return m_rhsIsValue; } }; class AstConsWildcard final : public AstNodeExpr { // Construct a wildcard assoc array and return object, '{} diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 3e67a3034..31ea2684d 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -322,6 +322,28 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l return new AstLogAnd{fileline(), ap, bp}; } +void AstConsDynArray::dump(std::ostream& str) const { + this->AstNodeExpr::dump(str); + if (lhsIsValue()) str << " [LVAL]"; + if (rhsIsValue()) str << " [RVAL]"; +} +void AstConsDynArray::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, lhsIsValue); + dumpJsonBoolFunc(str, rhsIsValue); + dumpJsonGen(str); +} + +void AstConsQueue::dump(std::ostream& str) const { + this->AstNodeExpr::dump(str); + if (lhsIsValue()) str << " [LVAL]"; + if (rhsIsValue()) str << " [RVAL]"; +} +void AstConsQueue::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, lhsIsValue); + dumpJsonBoolFunc(str, rhsIsValue); + dumpJsonGen(str); +} + AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) { bool success = false; if (literal[0] == '"') { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 6dea21240..ea2e23482 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1420,9 +1420,12 @@ public: void visit(AstConsDynArray* nodep) override { putnbs(nodep, nodep->dtypep()->cType("", false, false)); if (!nodep->lhsp()) { - putns(nodep, "()"); + putns(nodep, "{}"); } else { - putns(nodep, "::cons("); + puts("::cons"); + puts(nodep->lhsIsValue() ? "V" : "C"); + if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C"); + puts("("); iterateAndNextConstNull(nodep->lhsp()); if (nodep->rhsp()) { puts(", "); @@ -1451,9 +1454,12 @@ public: void visit(AstConsQueue* nodep) override { putnbs(nodep, nodep->dtypep()->cType("", false, false)); if (!nodep->lhsp()) { - puts("()"); + puts("{}"); } else { - puts("::cons("); + puts("::cons"); + puts(nodep->lhsIsValue() ? "V" : "C"); + if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C"); + puts("("); iterateAndNextConstNull(nodep->lhsp()); if (nodep->rhsp()) { puts(", "); diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index ca5b40c59..618cbaa13 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -262,6 +262,16 @@ class SliceVisitor final : public VNVisitor { m_okInitArray = true; iterateChildren(nodep); } + void visit(AstConsDynArray* nodep) override { + VL_RESTORER(m_okInitArray); + m_okInitArray = true; + iterateChildren(nodep); + } + void visit(AstConsQueue* nodep) override { + VL_RESTORER(m_okInitArray); + m_okInitArray = true; + iterateChildren(nodep); + } void visit(AstInitArray* nodep) override { UASSERT_OBJ(!m_assignp || m_okInitArray, nodep, "Array initialization should have been removed earlier"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5bb1ad3c4..417ad4d69 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -547,18 +547,37 @@ class WidthVisitor final : public VNVisitor { AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); userIterate(vdtypep, WidthVP{SELF, BOTH}.p()); // Conversions - if (VN_IS(vdtypep, QueueDType)) { + if (const AstDynArrayDType* const adtypep = VN_CAST(vdtypep, DynArrayDType)) { + // IEEE 1800-2023 10.10 requires looking at argument data type + // to determine if value or push + userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); // Queue "element 0" is lhsp, so we need to swap arguments - auto* const newp = new AstConsQueue{nodep->fileline(), nodep->rhsp()->unlinkFrBack(), - nodep->lhsp()->unlinkFrBack()}; + const bool lhsIsValue = AstNode::computeCastable(adtypep->subDTypep(), + nodep->lhsp()->dtypep(), nullptr).isAssignable(); + const bool rhsIsValue = AstNode::computeCastable(adtypep->subDTypep(), + nodep->rhsp()->dtypep(), nullptr).isAssignable(); + AstConsDynArray* const newp + = new AstConsDynArray{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(), + lhsIsValue, nodep->lhsp()->unlinkFrBack()}; nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); userIterateChildren(newp, m_vup); return; } - if (VN_IS(vdtypep, DynArrayDType)) { - auto* const newp = new AstConsDynArray{ - nodep->fileline(), nodep->rhsp()->unlinkFrBack(), nodep->lhsp()->unlinkFrBack()}; + if (const AstQueueDType* const adtypep = VN_CAST(vdtypep, QueueDType)) { + // IEEE 1800-2023 10.10 requires looking at argument data type + // to determine if value or push + userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); + // Queue "element 0" is lhsp, so we need to swap arguments + const bool lhsIsValue = AstNode::computeCastable(adtypep->subDTypep(), + nodep->lhsp()->dtypep(), nullptr).isAssignable(); + const bool rhsIsValue = AstNode::computeCastable(adtypep->subDTypep(), + nodep->rhsp()->dtypep(), nullptr).isAssignable(); + AstConsQueue* const newp + = new AstConsQueue{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(), + lhsIsValue, nodep->lhsp()->unlinkFrBack()}; nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); userIterateChildren(newp, m_vup); @@ -2498,29 +2517,31 @@ class WidthVisitor final : public VNVisitor { AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType); UASSERT_OBJ(vdtypep, nodep, "ConsDynArray requires queue upper parent data type"); if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); - userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); - if (nodep->didWidthAndSet()) return; + AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; + AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; + userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p()); nodep->dtypeFrom(vdtypep); } if (m_vup->final()) { + if (nodep->didWidthAndSet()) return; // Arguments can be either elements of the queue or a queue itself // Concats (part of tree of concats) must always become ConsDynArray's + AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; + AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; if (nodep->lhsp()) { - if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType) - || VN_IS(nodep->lhsp(), ConsDynArray)) { - userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, FINAL}.p()); - } else { + if (nodep->lhsIsValue()) { // Sub elements are not queues, but concats, must always pass concats down - iterateCheckTyped(nodep, "LHS", nodep->lhsp(), vdtypep->subDTypep(), FINAL); + iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL); + } else { + userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p()); } } if (nodep->rhsp()) { - if (VN_IS(nodep->rhsp()->dtypep(), DynArrayDType) - || VN_IS(nodep->rhsp(), ConsDynArray)) { - userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, FINAL}.p()); + if (nodep->rhsIsValue()) { + iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL); } else { - iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL); + userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p()); } } if (nodep->didWidthAndSet()) return; @@ -2532,28 +2553,31 @@ class WidthVisitor final : public VNVisitor { AstQueueDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), QueueDType); UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type"); if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); - userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); + AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; + AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; + userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p()); nodep->dtypeFrom(vdtypep); } if (m_vup->final()) { + if (nodep->didWidthAndSet()) return; // Arguments can be either elements of the queue or a queue itself // Concats (part of tree of concats) must always become ConsQueue's + AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; + AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; if (nodep->lhsp()) { - if (VN_IS(nodep->lhsp()->dtypep(), QueueDType) - || VN_IS(nodep->lhsp(), ConsQueue)) { - userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, FINAL}.p()); - } else { + if (nodep->lhsIsValue()) { // Sub elements are not queues, but concats, must always pass concats down - iterateCheckTyped(nodep, "LHS", nodep->lhsp(), vdtypep->subDTypep(), FINAL); + iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL); + } else { + userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p()); } } if (nodep->rhsp()) { - if (VN_IS(nodep->rhsp()->dtypep(), QueueDType) - || VN_IS(nodep->rhsp(), ConsQueue)) { - userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, FINAL}.p()); + if (nodep->rhsIsValue()) { + iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL); } else { - iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL); + userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p()); } } nodep->dtypeFrom(vdtypep); @@ -2572,7 +2596,8 @@ class WidthVisitor final : public VNVisitor { if (VN_IS(arrayp, NodeArrayDType) || VN_IS(arrayp, AssocArrayDType)) { userIterateChildren(nodep, WidthVP{arrayp->subDTypep(), BOTH}.p()); } else { - UINFO(1, "dtype object " << vdtypep->skipRefp() << endl); + UINFO(1, "on " << nodep << endl); + UINFO(1, "dtype object " << arrayp << endl); nodep->v3fatalSrc("InitArray on non-array"); } } @@ -4503,7 +4528,8 @@ class WidthVisitor final : public VNVisitor { patp = VN_AS(patp->nextp(), PatMember)) { patp->dtypep(arrayp->subDTypep()); AstNodeExpr* const valuep = patternMemberValueIterate(patp); - AstConsDynArray* const newap = new AstConsDynArray{nodep->fileline(), valuep, newp}; + AstConsDynArray* const newap + = new AstConsDynArray{nodep->fileline(), true, valuep, false, newp}; newap->dtypeFrom(arrayp); newp = newap; } @@ -4518,7 +4544,8 @@ class WidthVisitor final : public VNVisitor { patp = VN_AS(patp->nextp(), PatMember)) { patp->dtypep(arrayp->subDTypep()); AstNodeExpr* const valuep = patternMemberValueIterate(patp); - AstConsQueue* const newap = new AstConsQueue{nodep->fileline(), valuep, newp}; + AstConsQueue* const newap + = new AstConsQueue{nodep->fileline(), true, valuep, false, newp}; newap->dtypeFrom(arrayp); newp = newap; } diff --git a/test_regress/t/t_dynarray_concat.pl b/test_regress/t/t_dynarray_concat.pl new file mode 100755 index 000000000..e64ab41be --- /dev/null +++ b/test_regress/t/t_dynarray_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 2024 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_concat.v b/test_regress/t/t_dynarray_concat.v new file mode 100644 index 000000000..35593262d --- /dev/null +++ b/test_regress/t/t_dynarray_concat.v @@ -0,0 +1,65 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`ifdef VERILATOR + `define stop $stop +`else + `define stop +`endif +`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); + +module t(/*AUTOARG*/); + + int da[][2] = '{}; + int da2[][2] = '{'{1, 2}}; + + int dd[][] = '{}; + int dd1[][] = '{'{1}}; + int dd2[][] = '{'{1, 2}}; + + int dq[][$] = '{}; + int dq1[][$] = '{'{1}}; + int dq2[][$] = '{'{1, 2}}; + + int qa[$][2] = '{}; + int qa2[$][2] = '{'{1, 2}}; + + int qd[$][] = '{}; + int qd1[$][] = '{'{1}}; + int qd2[$][] = '{'{1, 2}}; + + int qq[$][$] = '{}; + int qq1[$][$] = '{'{1}}; + int qq2[$][$] = '{'{1, 2}}; + + initial begin + `checkp(da, "'{}"); + `checkp(da2, "'{'{'h1, 'h2} } "); + + `checkp(dd, "'{}"); + `checkp(dd1, "'{'{'h1} } "); + `checkp(dd2, "'{'{'h1, 'h2} } "); + + `checkp(dq, "'{}"); + `checkp(dq1, "'{'{'h1} } "); + `checkp(dq2, "'{'{'h1, 'h2} } "); + + `checkp(qa, "'{}"); + `checkp(qa2, "'{'{'h1, 'h2} } "); + + `checkp(qd, "'{}"); + `checkp(qd1, "'{'{'h1} } "); + `checkp(qd2, "'{'{'h1, 'h2} } "); + + `checkp(qq, "'{}"); + `checkp(qq1, "'{'{'h1} } "); + `checkp(qq2, "'{'{'h1, 'h2} } "); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule