diff --git a/Changes b/Changes index 0b4e62124..a16337032 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,8 @@ Verilator 5.007 devel **Minor:** +* Support unpacked unions. + Verilator 5.006 2022-01-22 ========================== diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index a50a88006..d64e81ab0 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -200,6 +200,7 @@ private: using MemberNameMap = std::map; // MEMBERS string m_name; // Name from upper typedef, if any + AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy MemberNameMap m_members; const int m_uniqueNum; bool m_packed; @@ -255,6 +256,8 @@ public: static int lo() { return 0; } int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays VNumRange declRange() const { return VNumRange{hi(), lo()}; } + AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } + void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; } }; // === Concrete node types ===================================================== @@ -1319,15 +1322,12 @@ public: // === AstNodeUOrStructDType === class AstStructDType final : public AstNodeUOrStructDType { - AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy public: // VSigning below is mispurposed to indicate if packed or not AstStructDType(FileLine* fl, VSigning numericUnpack) : ASTGEN_SUPER_StructDType(fl, numericUnpack) {} ASTGEN_MEMBERS_AstStructDType; string verilogKwd() const override { return "struct"; } - AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } - void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; } }; class AstUnionDType final : public AstNodeUOrStructDType { public: diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7c69d1007..d3ed29858 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -737,8 +737,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { info.m_type = "VlUnpacked<" + sub.m_type; info.m_type += ", " + cvtToStr(adtypep->declRange().elements()); info.m_type += ">"; - } else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) { - const auto* const sdtypep = VN_AS(dtypep, StructDType); + } else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) { + const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType); info.m_type = EmitCBaseVisitor::prefixNameProtect(sdtypep); } else if (const AstBasicDType* const bdtypep = dtypep->basicp()) { // We don't print msb()/lsb() as multidim packed would require recursion, diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 85a03b697..9085ba8d9 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -75,7 +75,7 @@ class CUseVisitor final : public VNVisitor { if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p()); // Add a CUse for every struct that requires a declaration - AstStructDType* const stypep = VN_CAST(nodep->skipRefp(), StructDType); + AstNodeUOrStructDType* const stypep = VN_CAST(nodep->skipRefp(), NodeUOrStructDType); if (stypep && stypep->classOrPackagep()) { addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name()); iterateChildren(stypep); diff --git a/src/V3Class.cpp b/src/V3Class.cpp index f5e65b2a0..755c94663 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -171,24 +171,25 @@ private: } } - void setStructModulep(AstStructDType* const dtypep) { + void setStructModulep(AstNodeUOrStructDType* const dtypep) { // Give it a pointer to its package and a final name dtypep->classOrPackagep(m_modp); - dtypep->name(dtypep->name() + "__struct" + cvtToStr(dtypep->uniqueNum())); + dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct") + + cvtToStr(dtypep->uniqueNum())); for (const AstMemberDType* itemp = dtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstStructDType* const subp = VN_CAST(itemp->skipRefp(), StructDType); + AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType); // Recurse only into anonymous unpacked structs inside this definition, // other unpacked structs will be reached from another typedefs - if (subp && !subp->packed() && subp->name().empty()) { setStructModulep(subp); } + if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp); } } void visit(AstTypedef* nodep) override { if (nodep->user1SetOnce()) return; iterateChildren(nodep); - AstStructDType* const dtypep = VN_CAST(nodep->dtypep(), StructDType); + AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType); if (dtypep && !dtypep->packed()) { dtypep->name(nodep->name()); setStructModulep(dtypep); diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index bcd392abd..1ac678acc 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -97,9 +97,9 @@ private: || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { } else { - const AstStructDType* const dtypep - = VN_CAST(nodep->dtypep()->skipRefp(), StructDType); - if (!dtypep || dtypep->packed()) { setCppWidth(nodep); } + const AstNodeUOrStructDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), NodeUOrStructDType); + if (!dtypep || dtypep->packed()) setCppWidth(nodep); } } } diff --git a/src/V3Common.cpp b/src/V3Common.cpp index 5e589d4d7..68999f3fd 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -61,7 +61,7 @@ static void makeVlToString(AstIface* nodep) { funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); nodep->addStmtsp(funcp); } -static void makeVlToString(AstStructDType* nodep) { +static void makeVlToString(AstNodeUOrStructDType* nodep) { AstNodeModule* const modp = nodep->classOrPackagep(); AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; @@ -168,7 +168,7 @@ void V3Common::commonAll() { } for (AstNode* nodep = v3Global.rootp()->typeTablep()->typesp(); nodep; nodep = nodep->nextp()) { - if (AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) { + if (AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) { if (!dtypep->packed()) makeVlToString(dtypep); } } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 2b45be47f..5faad31e1 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -324,7 +324,7 @@ private: } } bool shouldDeleteTypedef(AstTypedef* typedefp) { - if (auto* structp = VN_CAST(typedefp->subDTypep(), StructDType)) { + if (auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) { if (structp->user1() && !structp->packed()) return false; } return m_elimCells && !typedefp->attrPublic(); diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 0184a8b88..2922d6706 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -673,8 +673,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP depth + 1, suffix + "[" + ivar + "]"); const string post = "}\n"; return below.empty() ? "" : pre + below + post; - } else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) { - const auto* const sdtypep = VN_AS(dtypep, StructDType); + } else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) { + const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType); string literal; for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 744e13baf..3b0ef1abe 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -208,13 +208,13 @@ class EmitCHeader final : public EmitCConstInit { } } } - void emitStructDecl(const AstNodeModule* modp, AstStructDType* sdtypep, - std::set& emitted) { + void emitStructDecl(const AstNodeModule* modp, AstNodeUOrStructDType* sdtypep, + std::set& emitted) { if (emitted.count(sdtypep) > 0) return; emitted.insert(sdtypep); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstStructDType* subp = VN_CAST(itemp->skipRefp(), StructDType); + AstNodeUOrStructDType* subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType); if (subp && !subp->packed()) { // Recurse if it belongs to the current module if (subp->classOrPackagep() == modp) { @@ -223,7 +223,8 @@ class EmitCHeader final : public EmitCConstInit { } } } - puts("struct " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n"); + puts(sdtypep->verilogKwd()); // "struct"/"union" + puts(" " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n"); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false)); @@ -234,12 +235,12 @@ class EmitCHeader final : public EmitCConstInit { void emitStructs(const AstNodeModule* modp) { bool first = true; // Track structs that've been emitted already - std::set emitted; + std::set emitted; for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { const AstTypedef* const tdefp = VN_CAST(nodep, Typedef); if (!tdefp) continue; - AstStructDType* const sdtypep - = VN_CAST(tdefp->dtypep()->skipRefToEnump(), StructDType); + AstNodeUOrStructDType* const sdtypep + = VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType); if (!sdtypep) continue; if (sdtypep->packed()) continue; decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n"); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 16e24ac64..efd8d6c22 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -50,7 +50,8 @@ class EmitCGatherDependencies final : VNVisitor { if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) { m_dependencies.insert( EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep())); - } else if (const AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) { + } else if (const AstNodeUOrStructDType* const dtypep + = VN_CAST(nodep, NodeUOrStructDType)) { if (!dtypep->packed()) { m_dependencies.insert( EmitCBaseVisitor::prefixNameProtect(dtypep->classOrPackagep())); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index b77d89840..116596dad 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -122,7 +122,7 @@ private: if (AstRefDType* const refdtypep = VN_CAST(dtypep, RefDType)) { // dtypep = refdtypep->skipRefp(); } - if (AstStructDType* const stp = VN_CAST(dtypep, StructDType)) { + if (AstNodeUOrStructDType* const stp = VN_CAST(dtypep, NodeUOrStructDType)) { if (stp->packed()) { std::ostringstream out; out << "'{"; @@ -418,7 +418,7 @@ private: if (!VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType) && !VN_IS(nodep->varp()->dtypeSkipRefp(), PackArrayDType) && !VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType) - && !VN_IS(nodep->varp()->dtypeSkipRefp(), StructDType)) + && !VN_IS(nodep->varp()->dtypeSkipRefp(), NodeUOrStructDType)) clearOptimizable(nodep, "Array references/not basic"); if (nodep->access().isWriteOrRW()) { if (m_inDlyAssign) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 32899dce3..52cec557d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2478,13 +2478,7 @@ private: if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed UINFO(5, " NODECLASS " << nodep << endl); // if (debug() >= 9) nodep->dumpTree("- class-in: "); - if (!nodep->packed()) { - if (VN_IS(nodep, UnionDType)) { - nodep->v3warn(UNPACKED, "Unsupported: Unpacked union"); - } else if (v3Global.opt.structsPacked()) { - nodep->packed(true); - } - } + if (!nodep->packed() && v3Global.opt.structsPacked()) { nodep->packed(true); } userIterateChildren(nodep, nullptr); // First size all members nodep->repairMemberCache(); nodep->dtypep(nodep); @@ -4594,7 +4588,7 @@ private: || VN_IS(dtypep, DynArrayDType) // || VN_IS(dtypep, UnpackArrayDType) // || VN_IS(dtypep, QueueDType) - || (VN_IS(dtypep, StructDType) + || (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, StructDType)->packed())) { added = true; newFormat += "%@"; diff --git a/test_regress/t/t_union_unpacked_bad.pl b/test_regress/t/t_union_unpacked.pl similarity index 70% rename from test_regress/t/t_union_unpacked_bad.pl rename to test_regress/t/t_union_unpacked.pl index 59ba0d6c6..859050d63 100755 --- a/test_regress/t/t_union_unpacked_bad.pl +++ b/test_regress/t/t_union_unpacked.pl @@ -2,17 +2,19 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2003 by Wilson Snyder. This program is free software; you +# Copyright 2023 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); +scenarios(simulator => 1); -lint( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, +compile( + ); + +execute( + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_union_unpacked_bad.v b/test_regress/t/t_union_unpacked.v similarity index 52% rename from test_regress/t/t_union_unpacked_bad.v rename to test_regress/t/t_union_unpacked.v index d93b5ec5b..2cb7ccc39 100644 --- a/test_regress/t/t_union_unpacked_bad.v +++ b/test_regress/t/t_union_unpacked.v @@ -1,21 +1,22 @@ // 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, 2023 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -module x; +module t(/*AUTOARG*/); - typedef union { - int a; - } union_t; - - union_t b; + union { + bit [7:0] val1; + bit [3:0] val2; + } u; initial begin - b = 1; - if (b != 1) $stop; + u.val1 = 8'h7c; + if (u.val1 != 8'h7c) $stop; + if (u.val2 != 4'hc) $stop; $write("*-* All Finished *-*\n"); $finish; end + endmodule diff --git a/test_regress/t/t_union_unpacked_bad.out b/test_regress/t/t_union_unpacked_bad.out deleted file mode 100644 index 783f81c13..000000000 --- a/test_regress/t/t_union_unpacked_bad.out +++ /dev/null @@ -1,7 +0,0 @@ -%Warning-UNPACKED: t/t_union_unpacked_bad.v:9:12: Unsupported: Unpacked union - : ... In instance x - 9 | typedef union { - | ^~~~~ - ... For warning description see https://verilator.org/warn/UNPACKED?v=latest - ... Use "/* verilator lint_off UNPACKED */" and lint_on around source to disable this message. -%Error: Exiting due to