diff --git a/Changes b/Changes index 8782e98b2..3a52d1e32 100644 --- a/Changes +++ b/Changes @@ -17,6 +17,7 @@ Verilator 5.007 devel * Support interface classes and class implements. * Support global clocking and $global_clock. * Fix real parameters of infinity and NaN. +* Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal] Verilator 5.006 2022-01-22 diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index d64e81ab0..6639b6c9d 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -865,6 +865,7 @@ public: widthFromSub(subDTypep()); } ASTGEN_MEMBERS_AstMemberDType; + void dumpSmall(std::ostream& str) const override; string name() const override { return m_name; } // * = Var name bool hasDType() const override { return true; } bool maybePointedTo() const override { return true; } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 4eedb1177..e14842bdb 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -790,6 +790,51 @@ public: int instrCount() const override { return widthInstrs(); } bool same(const AstNode* /*samep*/) const override { return true; } }; +class AstConsPackMember final : public AstNodeExpr { + // Construct a packed array single emement [member1: value1] + // Don't need the member we are constructing, as the dtypep can get us to it + // @astgen op2 := rhsp : AstNodeExpr +public: + explicit AstConsPackMember(FileLine* fl, AstMemberDType* dtypep, AstNodeExpr* rhsp) + : ASTGEN_SUPER_ConsPackMember(fl) { + this->dtypep(dtypep); + this->rhsp(rhsp); + } + ASTGEN_MEMBERS_AstConsPackMember; + const char* broken() const override { + BROKEN_RTN(dtypep() && !VN_IS(dtypep(), MemberDType)); + return nullptr; + } + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + 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; } +}; +class AstConsPackUOrStruct final : public AstNodeExpr { + // Construct a packed struct and return object, '{member1: value1, member2: value2} + // Don't need the class we are constructing, as the dtypep can get us to it + // @astgen op1 := membersp : List[AstConsPackMember] +public: + explicit AstConsPackUOrStruct(FileLine* fl, AstNodeUOrStructDType* dtypep, + AstConsPackMember* membersp = nullptr) + : ASTGEN_SUPER_ConsPackUOrStruct(fl) { + this->dtypep(dtypep); + this->addMembersp(membersp); + } + ASTGEN_MEMBERS_AstConsPackUOrStruct; + const char* broken() const override { + BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType)); + return nullptr; + } + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + 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; } +}; class AstConsQueue final : public AstNodeExpr { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // @astgen op1 := lhsp : Optional[AstNode] diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index cf76a1687..1a4a07cc6 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1646,6 +1646,10 @@ void AstLogOr::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); if (sideEffect()) str << " [SIDE]"; } +void AstMemberDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "member"; +} void AstMemberSel::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); str << " -> "; diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 1ac678acc..8d0718700 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -87,7 +87,8 @@ private: } void computeCppWidth(AstNode* nodep) { if (!nodep->user2() && nodep->hasDType()) { - if (VN_IS(nodep, Var) + if (VN_IS(nodep, Var) // + || VN_IS(nodep, ConsPackMember) // || VN_IS(nodep, NodeDType) // Don't want to change variable widths! || VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays || VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType) diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 7f1078505..cad8e132f 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1262,6 +1262,21 @@ public: puts(")"); } } + void visit(AstConsPackUOrStruct* nodep) override { + putbs(nodep->dtypep()->cType("", false, false)); + puts("{"); + for (AstNode* memberp = nodep->membersp(); memberp; memberp = memberp->nextp()) { + iterate(memberp); + if (memberp->nextp()) { puts(", "); } + } + puts("}"); + } + void visit(AstConsPackMember* nodep) override { + auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType); + putbs(vdtypep->name()); + puts(": "); + iterate(nodep->rhsp()); + } void visit(AstConsQueue* nodep) override { putbs(nodep->dtypep()->cType("", false, false)); if (!nodep->lhsp()) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 594d165db..0b739d092 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2320,6 +2320,18 @@ private: EXTEND_EXP); } } + void visit(AstConsPackUOrStruct* nodep) override { + // Type was computed when constructed in V3Width earlier + auto* const vdtypep = VN_AS(nodep->dtypep()->skipRefp(), NodeUOrStructDType); + UASSERT_OBJ(vdtypep, nodep, "ConsPackUOrStruct requires packed array parent data type"); + userIterateChildren(nodep, WidthVP{vdtypep, BOTH}.p()); + } + void visit(AstConsPackMember* nodep) override { + // Type was computed when constructed in V3Width earlier + auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType); + UASSERT_OBJ(vdtypep, nodep, "ConsPackMember requires member data type"); + if (m_vup->prelim()) userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, BOTH}.p()); + } void visit(AstConsDynArray* nodep) override { // Type computed when constructed here AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType); @@ -3835,26 +3847,45 @@ private: } } AstNodeExpr* newp = nullptr; - for (AstMemberDType* memp = vdtypep->membersp(); memp; - memp = VN_AS(memp->nextp(), MemberDType)) { - const auto it = patmap.find(memp); - AstPatMember* patp = nullptr; - if (it == patmap.end()) { - // default or default_type assignment - if (AstNodeUOrStructDType* const memp_nested_vdtypep - = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) { - newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp, newp, - nodep, dtypemap); - } else { - patp = Defaultpatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, - dtypemap); + if (vdtypep->packed()) { + for (AstMemberDType* memp = vdtypep->membersp(); memp; + memp = VN_AS(memp->nextp(), MemberDType)) { + const auto it = patmap.find(memp); + AstPatMember* patp = nullptr; + if (it == patmap.end()) { // default or default_type assignment + if (AstNodeUOrStructDType* const memp_nested_vdtypep + = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) { + newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp, + newp, nodep, dtypemap); + } else { + patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, + dtypemap); + newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); + } + } else { // member assignment + patp = it->second; newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); } - } else { - // member assignment - patp = it->second; - newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); } + } else { // Unpacked + AstConsPackMember* membersp = nullptr; + for (AstMemberDType* memp = vdtypep->membersp(); memp; + memp = VN_AS(memp->nextp(), MemberDType)) { + const auto it = patmap.find(memp); + AstPatMember* patp = nullptr; + if (it == patmap.end()) { // Default or default_type assignment + patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, + dtypemap); + } else { + patp = it->second; // Member assignment + } + patp->dtypep(memp); + AstNodeExpr* const valuep = patternMemberValueIterate(patp); + AstConsPackMember* const cpmp + = new AstConsPackMember{patp->fileline(), memp, valuep}; + membersp = membersp ? membersp->addNext(cpmp) : cpmp; + } + newp = new AstConsPackUOrStruct{nodep->fileline(), vdtypep, membersp}; } if (newp) { nodep->replaceWith(newp); @@ -3877,7 +3908,7 @@ private: newp = nestedvalueConcat_patternUOrStruct(memp_multinested_vdtypep, defaultp, newp, nodep, dtypemap); } else { - patp = Defaultpatp_patternUOrStruct(nodep, memp_nested, patp, memp_vdtypep, + patp = defaultPatp_patternUOrStruct(nodep, memp_nested, patp, memp_vdtypep, defaultp, dtypemap); newp = valueConcat_patternUOrStruct(patp, newp, memp_nested, nodep); } @@ -3885,7 +3916,7 @@ private: return newp; } - AstPatMember* Defaultpatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp, + AstPatMember* defaultPatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp, AstPatMember* patp, AstNodeUOrStructDType* memp_vdtypep, AstPatMember* defaultp, const DTypeMap& dtypemap) { diff --git a/test_regress/t/t_struct_unpacked.v b/test_regress/t/t_struct_unpacked.v index 29a1e69b9..14190f693 100644 --- a/test_regress/t/t_struct_unpacked.v +++ b/test_regress/t/t_struct_unpacked.v @@ -1,9 +1,12 @@ // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2009 by Wilson Snyder. +// any use, without warranty, 2009-2023 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 x; typedef struct { int a, b; @@ -15,14 +18,31 @@ module x; embedded_t tab [3:0]; } notembedded_t; + typedef struct { + logic [15:0] m_i; + string m_s; + } istr_t; + notembedded_t p; embedded_t t [1:0]; + istr_t istr; + string s; initial begin t[1].a = 2; p.b.a = 1; if (t[1].a != 2) $stop; if (p.b.a != 1) $stop; + + istr.m_i = 12; + istr.m_s = "str1"; + s = $sformatf("%p", istr); + `checks(s, "'{m_i:'hc, m_s:\"str1\"}"); + + istr = '{m_i: '1, m_s: "str2"}; + s = $sformatf("%p", istr); + `checks(s, "'{m_i:'hffff, m_s:\"str2\"}"); + $write("*-* All Finished *-*\n"); $finish; end