diff --git a/Changes b/Changes index 466afe302..c2e34da47 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Add optimization of operators between concats, msg1447. [Jie Xu] +*** Add public enums, bug833. [Jonathon Donaldson] + **** Fix public parameters in unused packages, bug804. [Jonathon Donaldson] **** Fix generate unrolling with function call, bug830. [Steven Slatter] diff --git a/bin/verilator b/bin/verilator index 7d18045d4..d46b6e20c 100755 --- a/bin/verilator +++ b/bin/verilator @@ -2251,6 +2251,14 @@ reduce the size of the final executable when a task is used a very large number of times. For this flag to work, the task and tasks below it must be pure; they cannot reference any variables outside the task itself. +=item /*verilator public*/ (typedef enum) + +Used after an enum typedef declaration to indicate the emitted C code +should have the enum values visible. Due to C++ language restrictions, this +may only be used on 64-bit or narrower integral enumerations. + + typedef enum logic [2:0] { ZERO = 3'b0 } pub_t /*verilator public*/; + =item /*verilator public*/ (variable) Used after an input, output, register, or wire declaration to indicate the diff --git a/src/V3Ast.h b/src/V3Ast.h index 1daafcd8d..f14284ab8 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -236,6 +236,8 @@ public: DIM_SIZE, // V3Width processes DIM_UNPK_DIMENSIONS, // V3Width converts to constant // + DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic + // MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes // VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes @@ -255,6 +257,7 @@ public: "%E-AT", "DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT", "DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS", + "DT_PUBLIC", "MEMBER_BASE", "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW", @@ -1565,8 +1568,9 @@ public: virtual void dumpSmall(ostream& str); virtual bool hasDType() const { return true; } virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type - virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs to next non-typeref type + virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type virtual AstNodeDType* skipRefToConstp() const = 0; // recurses over typedefs to next non-typeref-or-const type + virtual AstNodeDType* skipRefToEnump() const = 0; // recurses over typedefs/const to next non-typeref-or-enum/struct type virtual int widthAlignBytes() const = 0; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const = 0; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... virtual bool maybePointedTo() const { return true; } @@ -1617,6 +1621,7 @@ public: virtual AstBasicDType* basicp() const { return findLogicDType(width(),width(),numeric())->castBasicDType(); } virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... // op1 = members @@ -1673,6 +1678,7 @@ public: virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return elementsConst() * subDTypep()->widthTotalBytes(); } int msb() const; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c1fac9d39..afd2ce341 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -819,6 +819,10 @@ void AstPin::dump(ostream& str) { else { str<<" ->UNLINKED"; } if (svImplicit()) str<<" [.SV]"; } +void AstTypedef::dump(ostream& str) { + this->AstNode::dump(str); + if (attrPublic()) str<<" [PUBLIC]"; +} void AstRange::dump(ostream& str) { this->AstNode::dump(str); if (littleEndian()) str<<" [LITTLE]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index a11a5cc03..62e6249be 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -183,22 +183,30 @@ public: class AstTypedef : public AstNode { private: string m_name; + bool m_attrPublic; public: - AstTypedef(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) + AstTypedef(FileLine* fl, const string& name, AstNode* attrsp, VFlagChildDType, AstNodeDType* dtp) : AstNode(fl), m_name(name) { childDTypep(dtp); // Only for parser + addAttrsp(attrsp); dtypep(NULL); // V3Width will resolve + m_attrPublic = false; } ASTNODE_NODE_FUNCS(Typedef, TYPEDEF) + virtual void dump(ostream& str); AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } + void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } + AstNode* attrsp() const { return op4p()->castNode(); } // op4 = Attributes during early parse // METHODS virtual string name() const { return m_name; } virtual bool maybePointedTo() const { return true; } virtual bool hasDType() const { return true; } void name(const string& flag) { m_name = flag; } + bool attrPublic() const { return m_attrPublic; } + void attrPublic(bool flag) { m_attrPublic = flag; } }; class AstTypedefFwd : public AstNode { @@ -242,6 +250,7 @@ public: virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } virtual string name() const { return m_name; } @@ -384,6 +393,7 @@ public: virtual AstBasicDType* basicp() const { return (AstBasicDType*)this; } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... AstBasicDTypeKwd keyword() const { return m.m_keyword; } // Avoid using - use isSomething accessors instead @@ -445,6 +455,7 @@ public: virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } }; @@ -474,6 +485,7 @@ public: virtual AstBasicDType* basicp() const { return NULL; } virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return 1; } virtual int widthTotalBytes() const { return 1; } string cellName() const { return m_cellName; } @@ -528,6 +540,10 @@ public: if (defp()) return defp()->skipRefToConstp(); else { v3fatalSrc("Typedef not linked"); return NULL; } } + virtual AstNodeDType* skipRefToEnump() const { + if (defp()) return defp()->skipRefToEnump(); + else { v3fatalSrc("Typedef not linked"); return NULL; } + } virtual int widthAlignBytes() const { return dtypeSkipRefp()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypeSkipRefp()->widthTotalBytes(); } void name(const string& flag) { m_name = flag; } @@ -599,6 +615,7 @@ public: AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); } virtual AstNodeDType* skipRefToConstp() const { return subDTypep()->skipRefToConstp(); } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... // METHODS @@ -688,6 +705,7 @@ public: virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); } virtual AstNodeDType* skipRefToConstp() const { return subDTypep()->skipRefToConstp(); } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } }; diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 4091e37b5..18dd12eed 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -238,6 +238,10 @@ private: nodep->iterateChildren(*this); insureCleanAndNext (nodep->valuep()); } + virtual void visit(AstTypedef* nodep, AstNUser*) { + // No cleaning, or would loose pointer to enum + nodep->iterateChildren(*this); + } // Control flow operators virtual void visit(AstNodeCond* nodep, AstNUser*) { diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 9cb4cab68..0e9d01090 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -165,6 +165,13 @@ private: nodep->packagep()->user1Inc(); } } + virtual void visit(AstTypedef* nodep, AstNUser*) { + nodep->iterateChildren(*this); + checkAll(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 && m_modp->castPackage()) m_modp->user1Inc(); + } virtual void visit(AstVarScope* nodep, AstNUser*) { nodep->iterateChildren(*this); checkAll(nodep); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 829a91179..3d3d69a89 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -96,6 +96,38 @@ public: } } + void emitTypedefs(AstNode* firstp) { + bool first = true; + for (AstNode* loopp=firstp; loopp; loopp = loopp->nextp()) { + if (AstTypedef* nodep = loopp->castTypedef()) { + if (nodep->attrPublic()) { + if (first) { + first = false; + puts("\n// TYPEDEFS\n"); + puts("// That were declared public\n"); + } else { + puts("\n"); + } + if (AstEnumDType* adtypep = nodep->dtypep()->skipRefToEnump()->castEnumDType()) { + if (adtypep->width()>64) { + puts("// enum "+nodep->name()+" // Ignored: Too wide for C++\n"); + } else { + puts("enum "+nodep->name()+" {\n"); + for (AstEnumItem* itemp = adtypep->itemsp(); itemp; itemp=itemp->nextp()->castEnumItem()) { + puts(itemp->name()); + puts(" = "); + itemp->valuep()->iterateAndNext(*this); + if (nodep->nextp()) puts(","); + puts("\n"); + } + puts("};\n"); + } + } + } + } + } + } + // VISITORS virtual void visit(AstNodeAssign* nodep, AstNUser*) { bool paren = true; bool decind = false; @@ -663,6 +695,7 @@ public: nodep->iterateChildren(*this); } // NOPs + virtual void visit(AstTypedef*, AstNUser*) {} virtual void visit(AstPragma*, AstNUser*) {} virtual void visit(AstCell*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstVar*, AstNUser*) {} // Handled outside the Visit class @@ -670,7 +703,6 @@ public: virtual void visit(AstTraceDecl*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstTraceInc*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstCFile*, AstNUser*) {} // Handled outside the Visit class - virtual void visit(AstTypedef*, AstNUser*) {} // Nothing needed presently // Default virtual void visit(AstNode* nodep, AstNUser*) { puts((string)"\n???? // "+nodep->prettyTypeName()+"\n"); @@ -1845,6 +1877,8 @@ void EmitCImp::emitInt(AstNodeModule* modp) { } } + emitTypedefs(modp->stmtsp()); + puts("\n// PORTS\n"); if (modp->isTop()) puts("// The application code writes and reads these signals to\n"); if (modp->isTop()) puts("// propagate new values into/out from the Verilated model.\n"); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index f33d42c6c..34667daed 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -62,6 +62,7 @@ private: AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module AstNodeModule* m_modp; // Current module AstNodeFTask* m_ftaskp; // Current task + AstNodeDType* m_dtypep; // Current data type // METHODS static int debug() { @@ -104,6 +105,15 @@ private: m_valueModp = upperValueModp; } } + virtual void visit(AstNodeDType* nodep, AstNUser*) { + if (!nodep->user1SetOnce()) { // Process only once. + cleanFileline(nodep); + AstNodeDType* upperDtypep = m_dtypep; + m_dtypep = nodep; + nodep->iterateChildren(*this); + m_dtypep = upperDtypep; + } + } virtual void visit(AstEnumItem* nodep, AstNUser*) { // Expand ranges cleanFileline(nodep); @@ -176,7 +186,13 @@ private: virtual void visit(AstAttrOf* nodep, AstNUser*) { cleanFileline(nodep); nodep->iterateChildren(*this); - if (nodep->attrType() == AstAttrType::VAR_CLOCK) { + if (nodep->attrType() == AstAttrType::DT_PUBLIC) { + AstTypedef* typep = nodep->backp()->castTypedef(); + if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef"); + typep->attrPublic(true); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + else if (nodep->attrType() == AstAttrType::VAR_CLOCK) { if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); m_varp->attrScClocked(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; @@ -265,7 +281,7 @@ private: nodep->deleteTree(); nodep=NULL; return; } else { - defp = new AstTypedef(nodep->fileline(), nodep->name(), VFlagChildDType(), dtypep); + defp = new AstTypedef(nodep->fileline(), nodep->name(), NULL, VFlagChildDType(), dtypep); m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp)); backp->addNextHere(defp); } @@ -327,6 +343,7 @@ public: m_varp = NULL; m_modp = NULL; m_ftaskp = NULL; + m_dtypep = NULL; m_inAlways = false; m_inGenerate = false; m_needStart = false; diff --git a/src/verilog.y b/src/verilog.y index f5ec10686..2f6bb09a6 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1600,8 +1600,9 @@ implicit_typeE: // IEEE: part of *data_type_or_implicit type_declaration: // ==IEEE: type_declaration // // Use idAny, as we can redeclare a typedef on an existing typedef - yTYPEDEF data_type idAny variable_dimensionListE ';' { $$ = new AstTypedef($1, *$3, VFlagChildDType(), GRAMMARP->createArray($2,$4,false)); - SYMP->reinsert($$); } + yTYPEDEF data_type idAny variable_dimensionListE dtypeAttrListE ';' + /**/ { $$ = new AstTypedef($1, *$3, $5, VFlagChildDType(), GRAMMARP->createArray($2,$4,false)); + SYMP->reinsert($$); } //UNSUP yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ ';' { $$ = NULL; $1->v3error("Unsupported: SystemVerilog 2005 typedef in this context"); } //UNSUP // // Combines into above "data_type id" rule // // Verilator: Not important what it is in the AST, just need to make sure the yaID__aTYPE gets returned @@ -1612,6 +1613,20 @@ type_declaration: // ==IEEE: type_declaration //UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } ; +dtypeAttrListE: + /* empty */ { $$ = NULL; } + | dtypeAttrList { $$ = $1; } + ; + +dtypeAttrList: + dtypeAttr { $$ = $1; } + | dtypeAttrList dtypeAttr { $$ = $1->addNextNull($2); } + ; + +dtypeAttr: + yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::DT_PUBLIC); } + ; + //************************************************ // Module Items diff --git a/test_regress/t/t_enum_public.cpp b/test_regress/t/t_enum_public.cpp new file mode 100644 index 000000000..8beeb64b5 --- /dev/null +++ b/test_regress/t/t_enum_public.cpp @@ -0,0 +1,26 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +#include +#include "Vt_enum_public.h" + +#include "Vt_enum_public_p3.h" +#include "Vt_enum_public_p62.h" + +int main (int argc, char *argv[]) { + Vt_enum_public *topp = new Vt_enum_public; + + Verilated::debug(0); + + // Make sure public tag worked + if (Vt_enum_public_p3::ZERO || Vt_enum_public_p3::ONE) {} + if (Vt_enum_public_p62::ZERO || Vt_enum_public_p62::ALLONE) {} + + for (int i = 0; i < 10; i++) { + topp->eval(); + } +} diff --git a/test_regress/t/t_enum_public.pl b/test_regress/t/t_enum_public.pl new file mode 100755 index 000000000..4408cbf72 --- /dev/null +++ b/test_regress/t/t_enum_public.pl @@ -0,0 +1,26 @@ +#!/usr/bin/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. + +if ($Self->{vlt}) { + compile ( + verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], + make_top_shell => 0, + make_main => 0, + ); +} else { + compile ( + ); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_enum_public.v b/test_regress/t/t_enum_public.v new file mode 100644 index 000000000..f3b1a93e2 --- /dev/null +++ b/test_regress/t/t_enum_public.v @@ -0,0 +1,33 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +package p3; + typedef enum logic [2:0] { + ZERO = 3'b0, + ONE = 3'b1 } e3_t /*verilator public*/; +endpackage + +package p62; + typedef enum logic [62:0] { + ZERO = '0, + ALLONE = '1 } e62_t /*verilator public*/; +endpackage + +module t (/*AUTOARG*/); + + enum integer { + EI_A, + EI_B, + EI_C + } m_state; + + initial begin + m_state = EI_A; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule +