diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 16f63e2d7..f284241ea 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -96,7 +96,7 @@ public: // simply use a C style array (which is just a pointer). template class VlWide final { - WData m_storage[T_Words]; + EData m_storage[T_Words]; public: // cppcheck-suppress uninitVar @@ -104,11 +104,17 @@ public: ~VlWide() = default; VlWide(const VlWide&) = default; VlWide(VlWide&&) = default; + + // OPERATOR METHODS VlWide& operator=(const VlWide&) = default; VlWide& operator=(VlWide&&) = default; + const EData& operator[](size_t index) const { return m_storage[index]; }; + EData& operator[](size_t index) { return m_storage[index]; }; + operator WDataOutP() { return &m_storage[0]; } + // METHODS - const WData& at(size_t index) const { return m_storage[index]; } - WData& at(size_t index) { return m_storage[index]; } + const EData& at(size_t index) const { return m_storage[index]; } + EData& at(size_t index) { return m_storage[index]; } WData* data() { return &m_storage[0]; } const WData* data() const { return &m_storage[0]; } bool operator<(const VlWide& rhs) const { @@ -788,6 +794,41 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, } } +//=================================================================== +// Verilog packed array container +// For when a standard C++[] array is not sufficient, e.g. an +// array under a queue, or methods operating on the array + +template class VlUnpacked final { +private: + // TYPES + typedef std::array Array; + +public: + typedef typename Array::const_iterator const_iterator; + +private: + // MEMBERS + Array m_array; // State of the assoc array + +public: + // CONSTRUCTORS + VlUnpacked() = default; + ~VlUnpacked() = default; + VlUnpacked(const VlUnpacked&) = default; + VlUnpacked(VlUnpacked&&) = default; + VlUnpacked& operator=(const VlUnpacked&) = default; + VlUnpacked& operator=(VlUnpacked&&) = default; + + // METHODS + // Raw access + WData* data() { return &m_array[0]; } + const WData* data() const { return &m_array[0]; } + + T_Value& operator[](size_t index) { return m_array[index]; }; + const T_Value& operator[](size_t index) const { return m_array[index]; }; +}; + //=================================================================== // Verilog class reference container // There are no multithreaded locks on this; the base variable must diff --git a/src/V3Ast.h b/src/V3Ast.h index 7762a084d..cd703068f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2388,7 +2388,13 @@ public: virtual void dump(std::ostream& str) const override; virtual void dumpSmall(std::ostream& str) const; virtual bool hasDType() const override { return true; } - virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type + /// Require VlUnpacked, instead of [] for POD elements. + /// A non-POD object is always compound, but some POD elements + /// are compound when methods calls operate on object, or when + /// under another compound-requiring object e.g. class + virtual bool isCompound() const = 0; + // (Slow) recurse down to find basic data type + virtual AstBasicDType* basicp() const = 0; // recurses over typedefs/const/enum to next non-typeref type virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs to next non-typeref-or-const type @@ -2480,6 +2486,7 @@ public: ASTNODE_BASE_FUNCS(NodeUOrStructDType) virtual const char* broken() const override; virtual void dump(std::ostream& str) const override; + virtual bool isCompound() const { return false; } // Because don't support unpacked // For basicp() we reuse the size to indicate a "fake" basic type of same size virtual AstBasicDType* basicp() const override { return (isFourstate() diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 4db2a9fdd..4a9a48466 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -653,12 +653,16 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { } else if (const auto* adtypep = VN_CAST_CONST(dtypep, ClassRefDType)) { info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">"; } else if (const auto* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) { - if (compound) { - v3fatalSrc("Dynamic arrays or queues with unpacked elements are not yet supported"); - } + if (adtypep->isCompound()) compound = true; const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound); - info.m_type = sub.m_type; - info.m_dims = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + sub.m_dims; + if (compound) { + info.m_type = "VlUnpacked<" + sub.m_type; + info.m_type += ", " + cvtToStr(adtypep->declRange().elements()); + info.m_type += ">"; + } else { + info.m_type = sub.m_type; + info.m_dims = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + sub.m_dims; + } } else if (const AstBasicDType* bdtypep = dtypep->basicp()) { // We don't print msb()/lsb() as multidim packed would require recursion, // and may confuse users as C++ data is stored always with bit 0 used @@ -1387,15 +1391,17 @@ void AstNodeDType::dumpSmall(std::ostream& str) const { } void AstNodeArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); - if (VN_IS(this, PackArrayDType)) { - str << "p"; + if (auto* adtypep = VN_CAST_CONST(this, UnpackArrayDType)) { + // uc = packed compound object, u = unpacked POD + str << (adtypep->isCompound() ? "uc" : "u"); } else { - str << "u"; + str << "p"; } str << declRange(); } void AstNodeArrayDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); + if (isCompound()) str << " [COMPOUND]"; str << " " << declRange(); } string AstPackArrayDType::prettyDTypeName() const { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index acfddcfb7..6f20557f7 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -407,6 +407,10 @@ public: AstVarType varType() const { return m_varType; } // * = Type of variable bool isParam() const { return true; } bool isGParam() const { return (varType() == AstVarType::GPARAM); } + virtual bool isCompound() const override { + v3fatalSrc("call isCompound on subdata type, not reference"); + return false; + } }; class AstTypedef final : public AstNode { @@ -507,6 +511,7 @@ public: virtual int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); } virtual string name() const override { return m_name; } virtual void name(const string& flag) override { m_name = flag; } + virtual bool isCompound() const override { return false; } }; class AstAssocArrayDType final : public AstNodeDType { @@ -578,6 +583,7 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } + virtual bool isCompound() const override { return true; } }; class AstBracketArrayDType final : public AstNodeDType { @@ -608,6 +614,7 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { V3ERROR_NA_RETURN(0); } virtual int widthTotalBytes() const override { V3ERROR_NA_RETURN(0); } + virtual bool isCompound() const override { return true; } }; class AstDynArrayDType final : public AstNodeDType { @@ -667,6 +674,7 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } + virtual bool isCompound() const override { return true; } }; class AstPackArrayDType final : public AstNodeArrayDType { @@ -693,12 +701,14 @@ public: } ASTNODE_NODE_FUNCS(PackArrayDType) virtual string prettyDTypeName() const override; + virtual bool isCompound() const override { return false; } }; class AstUnpackArrayDType final : public AstNodeArrayDType { // Array data type, ie "some_dtype var_name [2:0]" // Children: DTYPE (moved to refDTypep() in V3Width) // Children: RANGE (array bounds) + bool m_isCompound = false; // Non-POD subDType, or parent requires compound public: AstUnpackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep) : ASTGEN_SUPER(fl) { @@ -721,8 +731,14 @@ public: } ASTNODE_NODE_FUNCS(UnpackArrayDType) virtual string prettyDTypeName() const override; + virtual bool same(const AstNode* samep) const override { + const AstUnpackArrayDType* sp = static_cast(samep); + return m_isCompound == sp->m_isCompound; + } // Outer dimension comes first. The first element is this node. std::vector unpackDimensions(); + void isCompound(bool flag) { m_isCompound = flag; } + virtual bool isCompound() const override { return m_isCompound; } }; class AstUnsizedArrayDType final : public AstNodeDType { @@ -775,6 +791,7 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } + virtual bool isCompound() const override { return true; } }; class AstBasicDType final : public AstNodeDType { @@ -930,6 +947,7 @@ public: rangep(nullptr); } } + virtual bool isCompound() const override { return isString(); } }; class AstConstDType final : public AstNodeDType { @@ -982,6 +1000,10 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } + virtual bool isCompound() const override { + v3fatalSrc("call isCompound on subdata type, not reference"); + return false; + } }; class AstClassRefDType final : public AstNodeDType { @@ -1033,6 +1055,7 @@ public: AstClass* classp() const { return m_classp; } void classp(AstClass* nodep) { m_classp = nodep; } AstPin* paramsp() const { return VN_CAST(op4p(), Pin); } + virtual bool isCompound() const override { return true; } }; class AstIfaceRefDType final : public AstNodeDType { @@ -1088,6 +1111,7 @@ public: AstModport* modportp() const { return m_modportp; } void modportp(AstModport* modportp) { m_modportp = modportp; } bool isModport() { return !m_modportName.empty(); } + virtual bool isCompound() const override { return true; } // But not relevant }; class AstQueueDType final : public AstNodeDType { @@ -1158,6 +1182,7 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } + virtual bool isCompound() const override { return true; } }; class AstRefDType final : public AstNodeDType { @@ -1259,6 +1284,10 @@ public: AstNode* typeofp() const { return op2p(); } AstNode* classOrPackageOpp() const { return op3p(); } AstPin* paramsp() const { return VN_CAST(op4p(), Pin); } + virtual bool isCompound() const override { + v3fatalSrc("call isCompound on subdata type, not reference"); + return false; + } }; class AstStructDType final : public AstNodeUOrStructDType { @@ -1341,6 +1370,10 @@ public: virtual string tag() const override { return m_tag; } int lsb() const { return m_lsb; } void lsb(int lsb) { m_lsb = lsb; } + virtual bool isCompound() const override { + v3fatalSrc("call isCompound on subdata type, not reference"); + return false; + } }; class AstVoidDType final : public AstNodeDType { @@ -1368,6 +1401,7 @@ public: virtual int widthAlignBytes() const override { return 1; } virtual int widthTotalBytes() const override { return 1; } virtual V3Hash sameHash() const override { return V3Hash(); } + virtual bool isCompound() const override { return false; } }; class AstEnumItem final : public AstNode { @@ -1489,6 +1523,7 @@ public: for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++; return count; } + virtual bool isCompound() const override { return false; } }; class AstParseTypeDType final : public AstNodeDType { @@ -1510,6 +1545,10 @@ public: virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual int widthAlignBytes() const override { return 0; } virtual int widthTotalBytes() const override { return 0; } + virtual bool isCompound() const override { + v3fatalSrc("call isCompound on subdata type, not reference"); + return false; + } }; //###################################################################### diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index bff977935..e7f67eda3 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -549,7 +549,7 @@ private: if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum)); } if (nodep->generate() && nodep->name() == "" - && (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) { + && (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) { nodep->name("genblk" + cvtToStr(m_genblkAbove)); } if (nodep->name() != "") { diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 3c66b1648..85be8c721 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -106,7 +106,8 @@ class SliceVisitor final : public AstNVisitor { ? snodep->declRange().elements() - 1 - offset : offset)); newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset); - } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)) { + } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel) + || VN_IS(nodep, CMethodHard)) { UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst() - 1 - offset diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4cfa81dd6..2499543b6 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1451,9 +1451,10 @@ private: // Cleanup array size userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); nodep->dtypep(nodep); // The array itself, not subDtype - if (VN_IS(nodep, UnpackArrayDType)) { + if (auto* adtypep = VN_CAST(nodep, UnpackArrayDType)) { // Historically array elements have width of the ref type not the full array nodep->widthFromSub(nodep->subDTypep()); + if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true); } else { int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst(); nodep->widthForce(width, width); diff --git a/test_regress/t/t_queue_array.pl b/test_regress/t/t_queue_array.pl new file mode 100755 index 000000000..9a15dd2cc --- /dev/null +++ b/test_regress/t/t_queue_array.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_queue_array.v b/test_regress/t/t_queue_array.v new file mode 100644 index 000000000..f6a1b57ea --- /dev/null +++ b/test_regress/t/t_queue_array.v @@ -0,0 +1,74 @@ +// 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 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*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + + integer i; + + typedef string sarray_t[2]; + typedef sarray_t q_sarray_t[$]; + + typedef bit [95:0] wide_t; + typedef wide_t warray_t[2]; + typedef warray_t q_warray_t[$]; + + initial begin + begin + q_sarray_t iq; + sarray_t a; + sarray_t b0; + sarray_t b1; + + a[0] = "hello"; + a[1] = "world"; + iq.push_back(a); + a[0] = "bye"; + a[1] = "world"; + iq.push_back(a); + + b0 = iq[0]; + b1 = iq[1]; + `checks(b0[0], "hello"); + `checks(b0[1], "world"); + `checks(b1[0], "bye"); + `checks(b1[1], "world"); + end + +`ifndef verilator + // Need wide conversion into VlUnpacked types + + // If we convert all arrays to VlUnpacked it works, so we need to track + // data types and insert conversions perhaps in V3Cast, but we currently + // don't know the output datatypes, so work needed. + begin + q_warray_t iq; + warray_t a; + warray_t b0; + + a[0] = "abcdefg_ijkl"; + a[1] = "012123123128"; + iq.push_back(a); + + b0 = iq[0]; + `checks(b0[0], "abcdefg_ijkl"); + `checks(b0[1], "012123123128"); + end +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule