From c2b09e35f8c3fb1be724ebd3b74465773d5c1894 Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Wed, 21 Dec 2022 01:22:42 +0100 Subject: [PATCH] Support unpacked structs (#3802) --- bin/verilator | 1 + docs/guide/exe_verilator.rst | 9 +-- src/V3AstNodeDType.h | 6 +- src/V3AstNodeExpr.h | 25 +++++++ src/V3AstNodes.cpp | 3 + src/V3CUse.cpp | 7 ++ src/V3Cast.cpp | 2 +- src/V3Class.cpp | 29 +++++++ src/V3Clean.cpp | 4 +- src/V3Common.cpp | 37 +++++++++ src/V3Coverage.cpp | 40 +++++++--- src/V3Dead.cpp | 30 ++++++-- src/V3EmitCFunc.cpp | 11 +++ src/V3EmitCFunc.h | 5 ++ src/V3EmitCHeaders.cpp | 40 ++++++++++ src/V3EmitCImp.cpp | 9 +++ src/V3Options.cpp | 2 +- src/V3Options.h | 2 +- src/V3TraceDecl.cpp | 23 +++++- src/V3Width.cpp | 75 +++++++++++++------ src/verilog.y | 6 +- test_regress/t/t_debug_emitv.out | 8 +- test_regress/t/t_flag_structs_packed.pl | 22 ++++++ ...unpacked_bad.v => t_flag_structs_packed.v} | 0 test_regress/t/t_flag_structs_packed_bad.out | 5 ++ ...ed_bad.pl => t_flag_structs_packed_bad.pl} | 2 + test_regress/t/t_package_struct.out | 2 + test_regress/t/t_package_struct.pl | 22 ++++++ test_regress/t/t_package_struct.v | 25 +++++++ test_regress/t/t_protect_ids.v | 16 +++- test_regress/t/t_struct_assign.out | 3 + test_regress/t/t_struct_assign.pl | 22 ++++++ test_regress/t/t_struct_assign.v | 27 +++++++ test_regress/t/t_struct_unpacked.v | 24 +++--- test_regress/t/t_struct_unpacked2.out | 10 --- test_regress/t/t_struct_unused.pl | 33 ++++++++ test_regress/t/t_struct_unused.v | 23 ++++++ .../t/t_structu_dataType_assignment.pl | 1 + .../t/t_structu_dataType_assignment.v | 4 +- .../t/t_structu_dataType_assignment_bad.pl | 1 + ...acked_bad.out => t_union_unpacked_bad.out} | 8 +- ...t_unpacked2.pl => t_union_unpacked_bad.pl} | 0 ...uct_unpacked2.v => t_union_unpacked_bad.v} | 12 +-- 43 files changed, 541 insertions(+), 95 deletions(-) create mode 100755 test_regress/t/t_flag_structs_packed.pl rename test_regress/t/{t_struct_unpacked_bad.v => t_flag_structs_packed.v} (100%) create mode 100644 test_regress/t/t_flag_structs_packed_bad.out rename test_regress/t/{t_struct_unpacked_bad.pl => t_flag_structs_packed_bad.pl} (93%) create mode 100644 test_regress/t/t_package_struct.out create mode 100755 test_regress/t/t_package_struct.pl create mode 100644 test_regress/t/t_package_struct.v create mode 100644 test_regress/t/t_struct_assign.out create mode 100755 test_regress/t/t_struct_assign.pl create mode 100644 test_regress/t/t_struct_assign.v delete mode 100644 test_regress/t/t_struct_unpacked2.out create mode 100755 test_regress/t/t_struct_unused.pl create mode 100644 test_regress/t/t_struct_unused.v rename test_regress/t/{t_struct_unpacked_bad.out => t_union_unpacked_bad.out} (52%) rename test_regress/t/{t_struct_unpacked2.pl => t_union_unpacked_bad.pl} (100%) rename test_regress/t/{t_struct_unpacked2.v => t_union_unpacked_bad.v} (72%) diff --git a/bin/verilator b/bin/verilator index be1156d0f..02506165f 100755 --- a/bin/verilator +++ b/bin/verilator @@ -416,6 +416,7 @@ detailed descriptions of these arguments. --no-skip-identical Disable skipping identical output --stats Create statistics file --stats-vars Provide statistics on variables + --structs-packed Convert all unpacked structures to packed structures -sv Enable SystemVerilog parsing +systemverilogext+ Synonym for +1800-2017ext+ --threads Enable multithreading diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 8230d3e6b..932597211 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1203,11 +1203,10 @@ Summary: .. option:: --structs-packed - Converts all unpacked structures to packed structures and issues an - :option:`UNPACKED` warning. Currently, this is the default, and - :vlopt:`--no-structs-packed <--structs-packed>` will not work. - Specifying this option allows forward compatibility when a future - version of Verilator no longer always packs unpacked structures. + Converts all unpacked structures to packed structures, and issues an + :option:`UNPACKED` warning. Specifying this option allows for backward + compatibility with versions before Verilator 5.006, when Verilator would + always pack unpacked structures. .. option:: -sv diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index d06961356..3fd96e40f 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -219,7 +219,7 @@ public: int uniqueNum() const { return m_uniqueNum; } const char* broken() const override; void dump(std::ostream& str) const override; - bool isCompound() const override { return false; } // Because don't support unpacked + bool isCompound() const override { return !packed(); } // For basicp() we reuse the size to indicate a "fake" basic type of same size AstBasicDType* basicp() const override { return (isFourstate() @@ -241,6 +241,7 @@ public: string name() const override { return m_name; } void name(const string& flag) override { m_name = flag; } bool packed() const VL_MT_SAFE { return m_packed; } + void packed(bool flag) { m_packed = flag; } // packed() but as don't support unpacked, presently all structs static bool packedUnsup() { return true; } void isFourstate(bool flag) { m_isFourstate = flag; } @@ -1274,12 +1275,15 @@ 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/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 185ac1484..ff2d37089 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1778,6 +1778,31 @@ public: bool cleanOut() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; +class AstStructSel final : public AstNodeExpr { + // Unpacked struct member access + // Parents: math|stmt + // Children: varref, math + // @astgen op1 := fromp : AstNodeExpr +private: + string m_name; // Name of the member +public: + AstStructSel(FileLine* fl, AstNodeExpr* fromp, const string& name) + : ASTGEN_SUPER_StructSel(fl) + , m_name{name} { + this->fromp(fromp); + dtypep(nullptr); // V3Width will resolve + } + ASTGEN_MEMBERS_AstStructSel; + string name() const override { return m_name; } + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return false; } + bool same(const AstNode* samep) const override { + const AstStructSel* const sp = static_cast(samep); + return m_name == sp->m_name; + } + int instrCount() const override { return widthInstrs(); } +}; class AstSysIgnore final : public AstNodeExpr { // @astgen op1 := exprsp : List[AstNode] // Expressions to output (???) public: diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 6d833054e..3332db08d 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -734,6 +734,9 @@ 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); + 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, // and may confuse users as C++ data is stored always with bit 0 used diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 11eb84957..f9d3ff3e0 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -73,6 +73,13 @@ class CUseVisitor final : public VNVisitor { if (nodep->user1SetOnce()) return; // Process once if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep()); if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p()); + + // Add a CUse for every struct that requires a declaration + AstStructDType* const stypep = VN_CAST(nodep->skipRefp(), StructDType); + if (stypep && stypep->classOrPackagep()) { + addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name()); + iterateChildren(stypep); + } } void visit(AstNode* nodep) override { if (nodep->user1SetOnce()) return; // Process once diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index b27e22669..7de1e524b 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -159,7 +159,7 @@ private: const AstNode* const backp = nodep->backp(); if (nodep->access().isReadOnly() && VN_IS(backp, NodeExpr) && !VN_IS(backp, CCast) && !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF) - && !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) + && !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor) && (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec() && !nodep->varp()->basicp()->isForkSync()) && backp->width() && castSize(nodep) != castSize(nodep->varp())) { diff --git a/src/V3Class.cpp b/src/V3Class.cpp index c717e0b6f..268a9ef6c 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -41,6 +41,7 @@ private: // MEMBERS string m_prefix; // String prefix to add to name based on hier + AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_classPackagep = nullptr; // Package moving into const AstScope* m_classScopep = nullptr; // Package moving scopes into AstScope* m_packageScopep = nullptr; // Class package scope @@ -92,7 +93,9 @@ private: VL_RESTORER(m_classPackagep); VL_RESTORER(m_classScopep); VL_RESTORER(m_packageScopep); + VL_RESTORER(m_modp); { + m_modp = nodep; m_classPackagep = packagep; m_classScopep = classScopep; m_packageScopep = scopep; @@ -104,7 +107,9 @@ private: void visit(AstNodeModule* nodep) override { // Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule) VL_RESTORER(m_prefix); + VL_RESTORER(m_modp); { + m_modp = nodep; m_prefix = nodep->name() + "__03a__03a"; // :: iterateChildren(nodep); } @@ -166,6 +171,30 @@ private: } } + void setStructModulep(AstStructDType* 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())); + + for (const AstMemberDType* itemp = dtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + AstStructDType* const subp = VN_CAST(itemp->skipRefp(), StructDType); + // 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); } + } + } + void visit(AstTypedef* nodep) override { + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + + AstStructDType* const dtypep = VN_CAST(nodep->dtypep(), StructDType); + if (dtypep && !dtypep->packed()) { + dtypep->name(nodep->name()); + setStructModulep(dtypep); + } + } + void visit(AstNodeExpr* nodep) override {} // Short circuit void visit(AstNodeStmt* nodep) override {} // Short circuit void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index c7190e401..40adf48ef 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -97,7 +97,9 @@ private: || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { } else { - setCppWidth(nodep); + const AstStructDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), StructDType); + if (!dtypep || dtypep->packed()) { setCppWidth(nodep); } } } } diff --git a/src/V3Common.cpp b/src/V3Common.cpp index a421c49f5..5c620939e 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -61,6 +61,37 @@ static void makeVlToString(AstIface* nodep) { funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); nodep->addStmtsp(funcp); } +static void makeVlToString(AstStructDType* nodep) { + AstNodeModule* const modp = nodep->classOrPackagep(); + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; + funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "& obj"); + funcp->isMethod(false); + funcp->isConst(false); + funcp->isStatic(false); + funcp->protect(false); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"}); + for (const AstMemberDType* itemp = nodep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + std::string stmt = "out += \""; + if (itemp == nodep->membersp()) { + stmt += "'{"; + } else { + stmt += ", "; + } + stmt += itemp->nameProtect() + ":\" + "; + if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) { + stmt += "VL_TO_STRING_W"; + } else { + stmt += "VL_TO_STRING"; + } + stmt += "(obj." + itemp->nameProtect() + ");\n"; + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + } + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"}); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"}); + modp->addStmtsp(funcp); +} static void makeToString(AstClass* nodep) { AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"}; funcp->isConst(true); @@ -135,5 +166,11 @@ void V3Common::commonAll() { makeVlToString(ifacep); } } + for (AstNode* nodep = v3Global.rootp()->typeTablep()->typesp(); nodep; + nodep = nodep->nextp()) { + if (AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) { + if (!dtypep->packed()) makeVlToString(dtypep); + } + } V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3); } diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 173b87725..94ca143b9 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -354,18 +354,34 @@ private: newent.cleanup(); } } else if (const AstStructDType* const adtypep = VN_CAST(dtypep, StructDType)) { - // For now it's packed, so similar to array - for (AstMemberDType* itemp = adtypep->membersp(); itemp; - itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp(); - const int index_code = itemp->lsb(); - ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(), - new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true), - index_code, subtypep->width()}, - new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true), - index_code, subtypep->width()}}; - toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp); - newent.cleanup(); + if (adtypep->packed()) { + for (AstMemberDType* itemp = adtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp(); + const int index_code = itemp->lsb(); + ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(), + new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true), + index_code, subtypep->width()}, + new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true), + index_code, subtypep->width()}}; + toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp); + newent.cleanup(); + } + } else { + for (AstMemberDType* itemp = adtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp(); + AstNodeExpr* const varRefp = new AstStructSel{ + varp->fileline(), above.m_varRefp->cloneTree(true), itemp->name()}; + AstNodeExpr* const chgRefp = new AstStructSel{ + varp->fileline(), above.m_varRefp->cloneTree(true), itemp->name()}; + varRefp->dtypep(subtypep); + chgRefp->dtypep(subtypep); + ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(), varRefp, + chgRefp}; + toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp); + newent.cleanup(); + } } } else if (const AstUnionDType* const adtypep = VN_CAST(dtypep, UnionDType)) { // Arbitrarily handle only the first member of the union diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 32dedc071..ed0a1452f 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -72,6 +72,7 @@ private: std::vector m_scopesp; std::vector m_cellsp; std::vector m_classesp; + std::vector m_typedefsp; AssignMap m_assignMap; // List of all simple assignments for each variable const bool m_elimUserVars; // Allow removal of user's vars @@ -234,6 +235,11 @@ private: if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // classref checkAll(nodep); } + void visit(AstStructSel* nodep) override { + iterateChildren(nodep); + if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // structdtype + checkAll(nodep); + } void visit(AstModport* nodep) override { iterateChildren(nodep); if (m_elimCells) { @@ -253,11 +259,8 @@ private: } void visit(AstTypedef* nodep) override { iterateChildren(nodep); - if (m_elimCells && !nodep->attrPublic()) { - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } - checkAll(nodep); + m_typedefsp.push_back(nodep); + // Don't let packages with only public variables disappear // Normal modules may disappear, e.g. if they are parameterized then removed if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc(); @@ -306,6 +309,22 @@ private: } // METHODS + void deadCheckTypedefs() { + for (AstTypedef* typedefp : m_typedefsp) { + if (shouldDeleteTypedef(typedefp)) { + VL_DO_DANGLING(pushDeletep(typedefp->unlinkFrBack()), typedefp); + continue; + } + checkAll(typedefp); + } + } + bool shouldDeleteTypedef(AstTypedef* typedefp) { + if (auto* structp = VN_CAST(typedefp->subDTypep(), StructDType)) { + if (structp->user1() && !structp->packed()) return false; + } + return m_elimCells && !typedefp->attrPublic(); + } + void deadCheckMod() { // Kill any unused modules // V3LinkCells has a graph that is capable of this too, but we need to do it @@ -490,6 +509,7 @@ public: vscp->varp()->user1Inc(); } + deadCheckTypedefs(); deadCheckVar(); // We only eliminate scopes when in a flattened structure // Otherwise we have no easy way to know if a scope is used diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 214d8d9b1..42b4675d5 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -670,6 +670,17 @@ 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); + string literal; + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + const std::string line + = emitVarResetRecurse(varp, varNameProtected + suffix + "." + itemp->nameProtect(), + itemp->dtypep(), depth + 1, ""); + if (!line.empty()) literal += line; + } + return literal; } else if (basicp && basicp->keyword() == VBasicDTypeKwd::STRING) { // String's constructor deals with it return ""; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e19a42995..eddbff382 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1066,6 +1066,11 @@ public: putbs("->"); puts(nodep->varp()->nameProtect()); } + void visit(AstStructSel* nodep) override { + iterateAndNextNull(nodep->fromp()); + putbs("."); + puts(nodep->nameProtect()); + } void visit(AstNullCheck* nodep) override { puts("VL_NULL_CHECK("); iterateAndNextNull(nodep->lhsp()); diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index a20c622ab..2caf40342 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -208,6 +208,44 @@ class EmitCHeader final : public EmitCConstInit { } } } + void emitStructDecl(const AstNodeModule* modp, AstStructDType* 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); + if (subp && !subp->packed()) { + // Recurse if it belongs to the current module + if (subp->classOrPackagep() == modp) { + emitStructDecl(modp, subp, emitted); + puts("\n"); + } + } + } + puts("struct " + 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)); + puts(";\n"); + } + puts("};\n"); + } + void emitStructs(const AstNodeModule* modp) { + bool first = true; + // Track structs that've been emitted already + 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); + if (!sdtypep) continue; + if (sdtypep->packed()) continue; + decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n"); + emitStructDecl(modp, sdtypep, emitted); + } + } void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) { std::vector funcsp; @@ -252,6 +290,8 @@ class EmitCHeader final : public EmitCConstInit { // From `systemc_header emitTextSection(modp, VNType::atScHdr); + emitStructs(modp); + // Open class body {{{ puts("\nclass "); puts(prefixNameProtect(modp)); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index bcdeb9618..321247cfe 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -50,6 +50,11 @@ 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)) { + if (!dtypep->packed()) { + m_dependencies.insert( + EmitCBaseVisitor::prefixNameProtect(dtypep->classOrPackagep())); + } } } void addSelfDependency(const string& selfPointer, AstNode* nodep) { @@ -92,6 +97,10 @@ class EmitCGatherDependencies final : VNVisitor { addDTypeDependency(nodep->fromp()->dtypep()); iterateChildrenConst(nodep); } + void visit(AstStructSel* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } void visit(AstNodeVarRef* nodep) override { addSelfDependency(nodep->selfPointer(), nodep->varp()); iterateChildrenConst(nodep); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 3d920dfca..1ea5da943 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1406,7 +1406,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char m_statsVars = flag; m_stats |= flag; }); - DECL_OPTION("-structs-unpacked", OnOff, &m_structsPacked); + DECL_OPTION("-structs-packed", OnOff, &m_structsPacked); DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2017; }); DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug diff --git a/src/V3Options.h b/src/V3Options.h index 4d3a14280..1665c6167 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -267,7 +267,7 @@ private: bool m_relativeIncludes = false; // main switch: --relative-includes bool m_reportUnoptflat = false; // main switch: --report-unoptflat bool m_savable = false; // main switch: --savable - bool m_structsPacked = true; // main switch: --structs-packed + bool m_structsPacked = false; // main switch: --structs-packed bool m_systemC = false; // main switch: --sc: System C instead of simple C++ bool m_stats = false; // main switch: --stats bool m_statsVars = false; // main switch: --stats-vars diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 449abf60f..5c24680e7 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -421,7 +421,28 @@ private: // a much faster way to trace addTraceDecl(VNumRange{}, nodep->width()); } else if (!nodep->packed()) { - addIgnore("Unsupported: Unpacked struct/union"); + if (VN_IS(nodep, UnionDType)) { + addIgnore("Unsupported: Unpacked union"); + } else { + FileLine* const flp = nodep->fileline(); + VL_RESTORER(m_traName); + string prefix{m_traName}; + prefix += getScopeChar(VLT_TRACE_SCOPE_STRUCT); + addToSubFunc(new AstTracePushNamePrefix{flp, prefix + ' '}); + for (const AstMemberDType* itemp = nodep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump(); + m_traName = itemp->prettyName(); + VL_RESTORER(m_traValuep); + m_traValuep = m_traValuep->cloneTree(false); + m_traName = itemp->prettyName(); + m_traValuep = new AstStructSel{flp, m_traValuep, itemp->name()}; + m_traValuep->dtypep(subtypep); + iterate(subtypep); + VL_DO_CLEAR(m_traValuep->deleteTree(), m_traValuep = nullptr); + } + addToSubFunc(new AstTracePopNamePrefix{flp, 1}); + } } else { FileLine* const flp = nodep->fileline(); const bool isStruct = VN_IS(nodep, StructDType); // Otherwise union diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a420d2a04..86f8f8f03 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -222,6 +222,7 @@ private: const AstWith* m_withp = nullptr; // Current 'with' statement const AstFunc* m_funcp = nullptr; // Current function const AstAttrOf* m_attrp = nullptr; // Current attribute + AstNodeModule* m_modp = nullptr; // Current module const bool m_paramsOnly; // Computing parameter value; limit operation const bool m_doGenerate; // Do errors later inside generate statement int m_dtTables = 0; // Number of created data type tables @@ -2478,34 +2479,42 @@ private: UINFO(5, " NODECLASS " << nodep << endl); // if (debug() >= 9) nodep->dumpTree("- class-in: "); if (!nodep->packed()) { - nodep->v3warn(UNPACKED, "Unsupported: Unpacked struct/union"); - if (!v3Global.opt.structsPacked()) { - nodep->v3warn(UNPACKED, "Unsupported: --no-structs-packed"); + if (VN_IS(nodep, UnionDType)) { + nodep->v3warn(UNPACKED, "Unsupported: Unpacked union"); + } else if (v3Global.opt.structsPacked()) { + nodep->packed(true); } } userIterateChildren(nodep, nullptr); // First size all members nodep->repairMemberCache(); - // Determine bit assignments and width nodep->dtypep(nodep); - int lsb = 0; - int width = 0; nodep->isFourstate(false); - // MSB is first, so go backwards - AstMemberDType* itemp; - for (itemp = nodep->membersp(); itemp && itemp->nextp(); - itemp = VN_AS(itemp->nextp(), MemberDType)) {} - for (AstMemberDType* backip; itemp; itemp = backip) { - if (itemp->isFourstate()) nodep->isFourstate(true); - backip = VN_CAST(itemp->backp(), MemberDType); - itemp->lsb(lsb); - if (VN_IS(nodep, UnionDType)) { - width = std::max(width, itemp->width()); - } else { - lsb += itemp->width(); - width += itemp->width(); + // Determine bit assignments and width + if (VN_IS(nodep, UnionDType) || nodep->packed()) { + int lsb = 0; + int width = 0; + // MSB is first, so go backwards + AstMemberDType* itemp; + for (itemp = nodep->membersp(); itemp && itemp->nextp(); + itemp = VN_AS(itemp->nextp(), MemberDType)) {} + for (AstMemberDType* backip; itemp; itemp = backip) { + if (itemp->skipRefp()->isCompound()) + itemp->v3error( + "Unpacked data type in packed struct/union (IEEE 1800-2017 7.2.1)"); + if (itemp->isFourstate()) nodep->isFourstate(true); + backip = VN_CAST(itemp->backp(), MemberDType); + itemp->lsb(lsb); + if (VN_IS(nodep, UnionDType)) { + width = std::max(width, itemp->width()); + } else { + lsb += itemp->width(); + width += itemp->width(); + } } + nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration + } else { + nodep->widthForce(1, 1); } - nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration // if (debug() >= 9) nodep->dumpTree("- class-out: "); } void visit(AstClass* nodep) override { @@ -2526,6 +2535,11 @@ private: // though causes problems with t_class_forward.v, so for now avoided // userIterateChildren(nodep->classp(), nullptr); } + void visit(AstNodeModule* nodep) override { + VL_RESTORER(m_modp); + m_modp = nodep; + userIterateChildren(nodep, nullptr); + } void visit(AstClassOrPackageRef* nodep) override { if (nodep->didWidthAndSet()) return; userIterateChildren(nodep, nullptr); @@ -2550,6 +2564,9 @@ private: nodep->dtypep(nodep); // The member itself, not subDtype nodep->widthFromSub(nodep->subDTypep()); } + void visit(AstStructSel* nodep) override { + userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); + } void visit(AstMemberSel* nodep) override { UINFO(5, " MEMBERSEL " << nodep << endl); if (debug() >= 9) nodep->dumpTree("- mbs-in: "); @@ -2668,7 +2685,7 @@ private: nodep->dtypep(memberp); UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl); UINFO(9, " dt-> " << nodep->dtypep() << endl); - } else { + } else if (adtypep->packed()) { AstSel* const newp = new AstSel{nodep->fileline(), nodep->fromp()->unlinkFrBack(), memberp->lsb(), memberp->width()}; // Must skip over the member to find the union; as the member may disappear later @@ -2680,6 +2697,18 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); // Should be able to treat it as a normal-ish nodesel - maybe. // The lhsp() will be strange until this stage; create the number here? + } else { + AstStructSel* const newp = new AstStructSel{ + nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()}; + // Must skip over the member to find the union; as the member may disappear later + newp->dtypep(memberp->subDTypep()->skipRefToEnump()); + newp->didWidth(true); // Don't replace dtype with basic type + UINFO(9, " MEMBERSEL -> " << newp << endl); + UINFO(9, " dt-> " << newp->dtypep() << endl); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + // Should be able to treat it as a normal-ish nodesel - maybe. + // The lhsp() will be strange until this stage; create the number here? } return true; } @@ -4561,7 +4590,9 @@ private: || VN_IS(dtypep, WildcardArrayDType) // || VN_IS(dtypep, ClassRefDType) // || VN_IS(dtypep, DynArrayDType) // - || VN_IS(dtypep, QueueDType)) { + || VN_IS(dtypep, QueueDType) + || (VN_IS(dtypep, StructDType) + && !VN_AS(dtypep, StructDType)->packed())) { added = true; newFormat += "%@"; VNRelinker handle; diff --git a/src/verilog.y b/src/verilog.y index 311e68a4c..d6757a093 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2104,10 +2104,8 @@ member_decl_assignment: // Derived from IEEE: variable_decl_assi // // At present we allow only packed structures/unions. // // So this is different from variable_decl_assignment id variable_dimensionListE - { if ($2) $2->v3warn(UNPACKED, "Unsupported: Unpacked array in packed struct/union" - " (struct/union converted to unpacked)"); - $$ = new AstMemberDType{$1, *$1, VFlagChildDType{}, - AstNodeDType::cloneTreeNull(GRAMMARP->m_memDTypep, true)}; + { $$ = new AstMemberDType{$1, *$1, VFlagChildDType{}, + GRAMMARP->createArray(AstNodeDType::cloneTreeNull(GRAMMARP->m_memDTypep, true), $2, false)}; PARSEP->tagNodep($$); } | id variable_dimensionListE '=' variable_declExpr diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index dc2725f00..7ef0ebebd 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -32,7 +32,7 @@ module Vt_debug_emitv_t; ???? // UNSIZEDARRAYDTYPE ???? // DYNARRAYDTYPE - signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] e_t; + signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] e_t; typedef struct packed { ???? // REFDTYPE 'e_t' @@ -56,7 +56,7 @@ module Vt_debug_emitv_t; ???? // UNSIZEDARRAYDTYPE ???? // DYNARRAYDTYPE - signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] ps_t; + signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] ps_t; typedef struct {signed logic [2:0] a}logicunion {logic a} @@ -77,7 +77,7 @@ module Vt_debug_emitv_t; ???? // UNSIZEDARRAYDTYPE ???? // DYNARRAYDTYPE - signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] us_t; + signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] us_t; typedef union {logic a} ???? // REFDTYPE 'ps_t' @@ -97,7 +97,7 @@ module Vt_debug_emitv_t; ???? // UNSIZEDARRAYDTYPE ???? // DYNARRAYDTYPE - signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] union_t; + signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] union_t; struct packed { ???? // REFDTYPE 'e_t' diff --git a/test_regress/t/t_flag_structs_packed.pl b/test_regress/t/t_flag_structs_packed.pl new file mode 100755 index 000000000..4f0a79a43 --- /dev/null +++ b/test_regress/t/t_flag_structs_packed.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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); + +compile( + verilator_flags2 => ['--structs-packed'], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_struct_unpacked_bad.v b/test_regress/t/t_flag_structs_packed.v similarity index 100% rename from test_regress/t/t_struct_unpacked_bad.v rename to test_regress/t/t_flag_structs_packed.v diff --git a/test_regress/t/t_flag_structs_packed_bad.out b/test_regress/t/t_flag_structs_packed_bad.out new file mode 100644 index 000000000..3ca15d47c --- /dev/null +++ b/test_regress/t/t_flag_structs_packed_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_flag_structs_packed.v:14:19: Unpacked data type in packed struct/union (IEEE 1800-2017 7.2.1) + : ... In instance x + 14 | notpacked_t b; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_struct_unpacked_bad.pl b/test_regress/t/t_flag_structs_packed_bad.pl similarity index 93% rename from test_regress/t/t_struct_unpacked_bad.pl rename to test_regress/t/t_flag_structs_packed_bad.pl index 59ba0d6c6..2ac9f69e8 100755 --- a/test_regress/t/t_struct_unpacked_bad.pl +++ b/test_regress/t/t_flag_structs_packed_bad.pl @@ -8,6 +8,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +top_filename("t/t_flag_structs_packed.v"); + scenarios(linter => 1); lint( diff --git a/test_regress/t/t_package_struct.out b/test_regress/t/t_package_struct.out new file mode 100644 index 000000000..9bc7bd537 --- /dev/null +++ b/test_regress/t/t_package_struct.out @@ -0,0 +1,2 @@ +hello, world (0, 0) +*-* All Finished *-* diff --git a/test_regress/t/t_package_struct.pl b/test_regress/t/t_package_struct.pl new file mode 100755 index 000000000..f2e251072 --- /dev/null +++ b/test_regress/t/t_package_struct.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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( + expect_filename => $Self->{golden_filename}, + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_package_struct.v b/test_regress/t/t_package_struct.v new file mode 100644 index 000000000..445bfd541 --- /dev/null +++ b/test_regress/t/t_package_struct.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +package pkg; + typedef struct { + string a, b; + struct { + bit a, b; + } has; + } strings; +endpackage + +module t (/*AUTOARG*/); + initial begin + pkg::strings stct; + stct.a = "hello"; + stct.b = "world"; + $display("%s, %s (%1b, %1b)", stct.a, stct.b, stct.has.a, stct.has.b); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_protect_ids.v b/test_regress/t/t_protect_ids.v index ef6bb188a..dcc0fc41d 100644 --- a/test_regress/t/t_protect_ids.v +++ b/test_regress/t/t_protect_ids.v @@ -25,10 +25,16 @@ module secret_sub // verilator no_inline_module - integer secret_cyc; - real secret_cyc_r; - integer secret_o; - real secret_r; + typedef struct { + integer secret_field; + integer secret_field_r; + } secret_st; + + integer secret_cyc; + real secret_cyc_r; + integer secret_o; + real secret_r; + secret_st secret_pair; export "DPI-C" task dpix_a_task; task dpix_a_task(input int i, output int o); o = i + 1; endtask @@ -40,6 +46,8 @@ module secret_sub // Test loop always @ (posedge clk) begin + secret_pair.secret_field += 1; + secret_pair.secret_field_r += 2; secret_cyc_r = $itor(secret_cyc)/10.0 - 5.0; secret_cyc <= dpii_a_func(secret_cyc); secret_r += 1.0 + $cos(secret_cyc_r); diff --git a/test_regress/t/t_struct_assign.out b/test_regress/t/t_struct_assign.out new file mode 100644 index 000000000..d897b7de2 --- /dev/null +++ b/test_regress/t/t_struct_assign.out @@ -0,0 +1,3 @@ +( 3, 4) ( 3, 4) +%p='{fst:'h3, snd:'h4} +*-* All Finished *-* diff --git a/test_regress/t/t_struct_assign.pl b/test_regress/t/t_struct_assign.pl new file mode 100755 index 000000000..f2e251072 --- /dev/null +++ b/test_regress/t/t_struct_assign.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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( + expect_filename => $Self->{golden_filename}, + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_struct_assign.v b/test_regress/t/t_struct_assign.v new file mode 100644 index 000000000..c0dc33341 --- /dev/null +++ b/test_regress/t/t_struct_assign.v @@ -0,0 +1,27 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + typedef struct { + int fst, snd; + } pair_t; + + pair_t a, b; + + initial begin + a.fst = 1; + a.snd = 2; + b.fst = 3; + b.snd = 4; + + a = b; + + $display("(%d, %d) (%d, %d)", a.fst, a.snd, b.fst, b.snd); + $display("%%p=%p", a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_struct_unpacked.v b/test_regress/t/t_struct_unpacked.v index 7014a9b08..29a1e69b9 100644 --- a/test_regress/t/t_struct_unpacked.v +++ b/test_regress/t/t_struct_unpacked.v @@ -5,22 +5,24 @@ // SPDX-License-Identifier: CC0-1.0 module x; - - // verilator lint_off UNPACKED typedef struct { - int a; - } notpacked_t; - // verilator lint_on UNPACKED + int a, b; + logic [3:0] c; + } embedded_t; - typedef struct packed { - notpacked_t b; - } ispacked_t; + typedef struct { + embedded_t b; + embedded_t tab [3:0]; + } notembedded_t; - ispacked_t p; + notembedded_t p; + embedded_t t [1:0]; initial begin - p.b = 1; - if (p.b != 1) $stop; + t[1].a = 2; + p.b.a = 1; + if (t[1].a != 2) $stop; + if (p.b.a != 1) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_struct_unpacked2.out b/test_regress/t/t_struct_unpacked2.out deleted file mode 100644 index d31ee7fcd..000000000 --- a/test_regress/t/t_struct_unpacked2.out +++ /dev/null @@ -1,10 +0,0 @@ -%Warning-UNPACKED: t/t_struct_unpacked2.v:10:13: Unsupported: Unpacked array in packed struct/union (struct/union converted to unpacked) - 10 | int b [2]; - | ^ - ... 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. -%Warning-UNPACKED: t/t_struct_unpacked2.v:9:12: Unsupported: Unpacked struct/union - : ... In instance x - 9 | typedef struct { - | ^~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_struct_unused.pl b/test_regress/t/t_struct_unused.pl new file mode 100755 index 000000000..efbd21e12 --- /dev/null +++ b/test_regress/t/t_struct_unused.pl @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +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 +# 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_all => 1); + +# Use --debug-protect to assist debug + +compile( + ); + +execute( + check_finished => 1, + ); + +if ($Self->{vlt_all}) { + # Check for unused structs in any outputs + my $any; + foreach my $filename (glob $Self->{obj_dir} . "/*.[ch]*") { + file_grep_not($filename, qr/useless/i); + $any = 1; + } + $any or $Self->error("No outputs found"); +} + +ok(1); +1; diff --git a/test_regress/t/t_struct_unused.v b/test_regress/t/t_struct_unused.v new file mode 100644 index 000000000..07181488a --- /dev/null +++ b/test_regress/t/t_struct_unused.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module x; + typedef struct { + int fst, snd; + } uselessA_t; + + typedef struct { + bit [3:0] n; + uselessA_t b; + } uselessB_t; + + uselessA_t useless; + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_structu_dataType_assignment.pl b/test_regress/t/t_structu_dataType_assignment.pl index 2cb5eeaff..45d42e7f6 100755 --- a/test_regress/t/t_structu_dataType_assignment.pl +++ b/test_regress/t/t_structu_dataType_assignment.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( + verilator_flags2 => ['--structs-packed'], ); execute( diff --git a/test_regress/t/t_structu_dataType_assignment.v b/test_regress/t/t_structu_dataType_assignment.v index 209dc34b6..5839b375a 100644 --- a/test_regress/t/t_structu_dataType_assignment.v +++ b/test_regress/t/t_structu_dataType_assignment.v @@ -38,9 +38,9 @@ module top(); int A; struct { int B, C; - struct{ + struct { int D, E; - struct{ + struct { int F; shortint G; } FG1; diff --git a/test_regress/t/t_structu_dataType_assignment_bad.pl b/test_regress/t/t_structu_dataType_assignment_bad.pl index bec0388e9..b9230bc2a 100755 --- a/test_regress/t/t_structu_dataType_assignment_bad.pl +++ b/test_regress/t/t_structu_dataType_assignment_bad.pl @@ -12,6 +12,7 @@ scenarios(simulator => 1); compile( + verilator_flags2 => ['--structs-packed'], fails => 1, expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_struct_unpacked_bad.out b/test_regress/t/t_union_unpacked_bad.out similarity index 52% rename from test_regress/t/t_struct_unpacked_bad.out rename to test_regress/t/t_union_unpacked_bad.out index 6b83b545a..783f81c13 100644 --- a/test_regress/t/t_struct_unpacked_bad.out +++ b/test_regress/t/t_union_unpacked_bad.out @@ -1,7 +1,7 @@ -%Warning-UNPACKED: t/t_struct_unpacked_bad.v:9:12: Unsupported: Unpacked struct/union - : ... In instance x - 9 | typedef struct { - | ^~~~~~ +%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 diff --git a/test_regress/t/t_struct_unpacked2.pl b/test_regress/t/t_union_unpacked_bad.pl similarity index 100% rename from test_regress/t/t_struct_unpacked2.pl rename to test_regress/t/t_union_unpacked_bad.pl diff --git a/test_regress/t/t_struct_unpacked2.v b/test_regress/t/t_union_unpacked_bad.v similarity index 72% rename from test_regress/t/t_struct_unpacked2.v rename to test_regress/t/t_union_unpacked_bad.v index e7c6ff9ff..d93b5ec5b 100644 --- a/test_regress/t/t_struct_unpacked2.v +++ b/test_regress/t/t_union_unpacked_bad.v @@ -6,15 +6,15 @@ module x; - typedef struct { - int b [2]; - } notpacked_t; + typedef union { + int a; + } union_t; - notpacked_t n; + union_t b; initial begin - n.b[0] = 1; - if (n.b[0] != 1) $stop; + b = 1; + if (b != 1) $stop; $write("*-* All Finished *-*\n"); $finish; end