From 229d854607be76b173d660f49f96ca9c24eb8aeb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 31 Dec 2012 17:05:13 -0500 Subject: [PATCH] Fix package resolution of parameters, bug586. --- Changes | 2 + src/V3Ast.h | 7 +- src/V3AstNodes.cpp | 9 +- src/V3AstNodes.h | 45 +++++--- src/V3LinkDot.cpp | 170 +++++++++++++++++++++++------- src/V3LinkParse.cpp | 121 +-------------------- src/V3ParseImp.h | 1 + src/verilog.y | 93 ++++++++-------- test_regress/t/t_param_package.pl | 18 ++++ test_regress/t/t_param_package.v | 23 ++++ 10 files changed, 264 insertions(+), 225 deletions(-) create mode 100755 test_regress/t/t_param_package.pl create mode 100644 test_regress/t/t_param_package.v diff --git a/Changes b/Changes index 5a124ea3c..8750422af 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Support "unsigned int" DPI import functions, msg966. [Alex Lee] +*** Fix package resolution of parameters, bug586. [Jeremy Bennett] + **** Fix non-integer vpi_get_value, bug587. [Rich Porter] **** Fix task inlining under $display, bug589. [Holger Waechtler] diff --git a/src/V3Ast.h b/src/V3Ast.h index ae0ba869f..895fbe52a 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -505,10 +505,7 @@ class AstParseRefExp { public: enum en { PX_NONE, // Used in V3LinkParse only - PX_TEXT, // Unknown ID component - PX_PREDOT, // Module name or misc component above var/task/func/member - PX_VAR_ANY, // Variable/structure member - PX_FTASK // Task/Function (AstParse::ftaskrefp() will be set) + PX_TEXT // Unknown ID component }; enum en m_e; inline AstParseRefExp() : m_e(PX_NONE) {} @@ -517,7 +514,7 @@ public: operator en () const { return m_e; } const char* ascii() const { static const char* names[] = { - "","TEXT","PREDOT","VAR_MEM","VAR_ANY","FTASK"}; + "","TEXT","PREDOT"}; return names[m_e]; } }; inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a3e37f36f..a0a930286 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -859,11 +859,16 @@ void AstSenItem::dump(ostream& str) { void AstParseRef::dump(ostream& str) { this->AstNode::dump(str); str<<" ["<AstNode::dump(str); + if (packagep()) { str<<" pkg="<<(void*)packagep(); } + str<<" -> "; + if (packagep()) { packagep()->dump(str); } + else { str<<"UNLINKED"; } } void AstDot::dump(ostream& str) { this->AstNode::dump(str); - if (start()) str<<" [START]"; } void AstActive::dump(ostream& str) { this->AstNode::dump(str); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 844a87587..982c9509d 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1367,15 +1367,16 @@ struct AstParseRef : public AstNode { private: AstParseRefExp m_expect; // Type we think it should resolve to string m_name; - bool m_start; // Start of parseref stack public: AstParseRef(FileLine* fl, AstParseRefExp expect, const string& name, AstNode* lhsp, AstNodeFTaskRef* ftaskrefp) - :AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); m_start=false; } + :AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); } ASTNODE_NODE_FUNCS(ParseRef, PARSEREF) virtual void dump(ostream& str); virtual string name() const { return m_name; } // * = Var name virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); } - virtual bool same(AstNode* samep) const { return expect() == samep->castParseRef()->expect() && m_name==samep->castParseRef()->m_name; } + virtual bool same(AstNode* samep) const { + return (expect() == samep->castParseRef()->expect() + && m_name==samep->castParseRef()->m_name); } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } virtual void name(const string& name) { m_name = name; } @@ -1385,26 +1386,44 @@ public: AstNode* lhsp() const { return op1p(); } // op1 = List of statements AstNode* ftaskrefp() const { return op2p(); } // op2 = Function/task reference void ftaskrefp(AstNodeFTaskRef* nodep) { setNOp2p(nodep); } // op2 = Function/task reference - bool start() const { return m_start; } - void start(bool flag) { m_start = flag; } +}; + +struct AstPackageRef : public AstNode { +private: + AstPackage* m_packagep; // Package hierarchy +public: + AstPackageRef(FileLine* fl, AstPackage* packagep) + : AstNode(fl), m_packagep(packagep) {} + ASTNODE_NODE_FUNCS(PackageRef, PACKAGEREF) + // METHODS + virtual bool broken() const { return !m_packagep || !m_packagep->brokeExists(); } + virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) { + m_packagep = m_packagep->clonep()->castPackage(); + }} + virtual bool same(AstNode* samep) const { + return (m_packagep==samep->castPackageRef()->m_packagep); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_packagep)); } + virtual void dump(ostream& str=cout); + AstPackage* packagep() const { return m_packagep; } + void packagep(AstPackage* nodep) { m_packagep=nodep; } }; struct AstDot : public AstNode { // A dot separating paths in an AstXRef, AstFuncRef or AstTaskRef // These are eliminated in the link stage -private: - bool m_start; // Start of parseref stack public: AstDot(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - :AstNode(fl) { setOp1p(lhsp); setOp2p(rhsp); m_start=false; } + :AstNode(fl) { setOp1p(lhsp); setOp2p(rhsp); } ASTNODE_NODE_FUNCS(Dot, DOT) + static AstNode* newIfPkg(FileLine*fl, AstPackage* packagep, AstNode* rhsp) { // For parser, make only if non-null package + if (!packagep) return rhsp; + return new AstDot(fl, new AstPackageRef(fl, packagep), rhsp); + } virtual void dump(ostream& str); virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } AstNode* lhsp() const { return op1p(); } AstNode* rhsp() const { return op2p(); } - bool start() const { return m_start; } - void start(bool flag) { m_start = flag; } }; //###################################################################### @@ -1474,11 +1493,7 @@ public: class Initial {}; // for creator type-overload selection class Settle {}; // for creator type-overload selection class Never {}; // for creator type-overload selection - AstSenItem(FileLine* fl, AstEdgeType edgeType, AstNodeVarRef* varrefp) - : AstNodeSenItem(fl), m_edgeType(edgeType) { - setOp1p(varrefp); - } - AstSenItem(FileLine* fl, AstEdgeType edgeType, AstParseRef* varrefp) + AstSenItem(FileLine* fl, AstEdgeType edgeType, AstNode* varrefp) : AstNodeSenItem(fl), m_edgeType(edgeType) { setOp1p(varrefp); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index d859f126a..e55c29483 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -809,7 +809,7 @@ private: if (nodep->castDot()) { // Not creating a simple implied type, // and implying something else would just confuse later errors } - else if (nodep->castVarRef() || (nodep->castParseRef() && nodep->castParseRef()->start())) { + else if (nodep->castVarRef() || nodep->castParseRef()) { // To prevent user errors, we should only do single bit // implicit vars, however some netlists (MIPS) expect single // bit implicit wires to get created with range 0:0 etc. @@ -1018,7 +1018,11 @@ private: AstUser5InUse m_inuser5; // TYPES - enum DotPosition { DP_SCOPE, DP_VAR_ETC, DP_MEMBER }; + enum DotPosition { DP_NONE=0, // Not under a DOT + DP_PACKAGE, // {package}:: DOT + DP_SCOPE, // [DOT...] {scope-or-var} DOT + DP_FINAL, // [DOT...] {var-or-func-or-dtype} with no following dots + DP_MEMBER }; // DOT {member-name} [DOT...] // STATE LinkDotState* m_statep; // State, including dotted symbol table @@ -1030,16 +1034,24 @@ private: AstNodeFTask* m_ftaskp; // Current function/task struct DotStates { + DotPosition m_dotPos; // Scope part of dotted resolution VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup AstDot* m_dotp; // Current dot - DotPosition m_dotPos; // Scope part of dotted resolution bool m_dotErr; // Error found in dotted resolution, ignore upwards string m_dotText; // String of dotted names found in below parseref - void init(VSymEnt* curSymp) { - m_dotSymp = curSymp; m_dotp = NULL; m_dotPos = DP_VAR_ETC; m_dotErr = false; m_dotText = ""; - } DotStates() { init(NULL); } ~DotStates() {} + void init(VSymEnt* curSymp) { + m_dotPos = DP_NONE; m_dotSymp = curSymp; m_dotp = NULL; m_dotErr = false; m_dotText = ""; + } + string ascii() const { + static const char* names[] = { "NONE","PACKAGE","SCOPE","FINAL","MEMBER" }; + ostringstream sstr; + sstr<<"ds="<prettyName()); } + inline void checkNoDot(AstNode* nodep) { + if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) { + m_statep->preErrorDump(); + UINFO(1,"ds="<v3error("Syntax Error: Not expecting "<type()<<" under a "<backp()->type()<<" in dotted expression"); + m_ds.m_dotErr = true; + } + } // VISITs virtual void visit(AstNetlist* nodep, AstNUser* vup) { @@ -1077,6 +1097,7 @@ private: virtual void visit(AstTypeTable* nodep, AstNUser*) {} virtual void visit(AstNodeModule* nodep, AstNUser*) { if (nodep->dead()) return; + checkNoDot(nodep); UINFO(8," "<getNodeSym(nodep); // Until overridden by a SCOPE @@ -1088,17 +1109,20 @@ private: } virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," "<getScopeSym(nodep); nodep->iterateChildren(*this); m_ds.m_dotSymp = m_curSymp = NULL; } virtual void visit(AstCellInline* nodep, AstNUser*) { + checkNoDot(nodep); if (m_statep->forScopeCreation()) { nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } } virtual void visit(AstCell* nodep, AstNUser*) { // Cell: Resolve its filename. If necessary, parse it. + checkNoDot(nodep); m_cellp = nodep; AstNode::user5ClearTree(); if (!nodep->modp()) { @@ -1127,6 +1151,7 @@ private: } virtual void visit(AstPin* nodep, AstNUser*) { // Pin: Link to submodule's port + checkNoDot(nodep); if (!nodep->modVarp()) { if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?\n"); VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name()); @@ -1155,10 +1180,14 @@ private: // Early return() above when deleted } virtual void visit(AstDot* nodep, AstNUser*) { + // Legal under a DOT: AstDot, AstParseRef, AstPackageRef, AstNodeSel + // also a DOT can be part of an expression, but only above plus AstFTaskRef are legal children + // DOT(PACKAGEREF, PARSEREF(text)) + // DOT(DOT(DOT(PARSEREF(text), ... if (nodep->user3SetOnce()) return; UINFO(8," "<start(); // Save, as nodep may go NULL + bool start = !m_ds.m_dotp; // Save, as m_dotp will be changed { if (start) { // Starting dot sequence if (debug()>=9) nodep->dumpTree("-dot-in: "); @@ -1166,10 +1195,17 @@ private: } m_ds.m_dotp = nodep; // Always, not just at start m_ds.m_dotPos = DP_SCOPE; + // m_ds.m_dotText communicates the cell prefix between stages - nodep->lhsp()->iterateAndNext(*this); + if (nodep->lhsp()->castPackageRef()) { + if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in dotted reference"); m_ds.m_dotErr=true; } + m_ds.m_dotPos = DP_PACKAGE; + } else { + m_ds.m_dotPos = DP_SCOPE; + nodep->lhsp()->iterateAndNext(*this); + } if (!m_ds.m_dotErr) { // Once something wrong, give up - if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_VAR_ETC; // Top dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS + if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS nodep->rhsp()->iterateAndNext(*this); } if (start) { @@ -1197,15 +1233,16 @@ private: } virtual void visit(AstParseRef* nodep, AstNUser*) { if (nodep->user3SetOnce()) return; - UINFO(9," linkPARSEREF se"<<(void*)m_ds.m_dotSymp<<" pos="<v3fatalSrc("NULL lookup symbol table"); if (!m_statep->forPrimary()) nodep->v3fatalSrc("ParseRefs should no longer exist"); DotStates lastStates = m_ds; - bool start = nodep->start(); // Save, as nodep may go NULL + bool start = !m_ds.m_dotp; if (start) { m_ds.init(m_curSymp); + // Note m_ds.m_dot remains NULL; this is a reference not under a dot } if (m_ds.m_dotPos == DP_MEMBER) { // Found a Var, everything following is membership. {scope}.{var}.HERE {member} @@ -1219,20 +1256,29 @@ private: string expectWhat; bool allowScope = false; bool allowVar = false; - bool onlyVar = false; - if (nodep->expect() == AstParseRefExp::PX_PREDOT) { + AstPackage* packagep = NULL; + if (m_ds.m_dotPos == DP_PACKAGE) { + // {package}::{a} + expectWhat = "scope/variable"; + allowScope = true; + allowVar = true; + if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + packagep = m_ds.m_dotp->lhsp()->castPackageRef()->packagep(); + if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + m_ds.m_dotSymp = m_statep->getNodeSym(packagep); + m_ds.m_dotPos = DP_SCOPE; + } else if (m_ds.m_dotPos == DP_SCOPE) { // {a}.{b}, where {a} maybe a module name // or variable, where dotting into structure member expectWhat = "scope/variable"; allowScope = true; allowVar = true; - } else if (nodep->expect() == AstParseRefExp::PX_VAR_ANY) { + } else if (m_ds.m_dotPos == DP_NONE + || m_ds.m_dotPos == DP_FINAL) { expectWhat = "variable"; - onlyVar = true; allowVar = true; - } else if (nodep->expect() == AstParseRefExp::PX_FTASK) { - expectWhat = "task/function"; } else { + UINFO(1,"ds="<v3fatalSrc("Unhandled AstParseRefExp"); } // Lookup @@ -1283,23 +1329,9 @@ private: m_ds.m_dotText = ""; } } - else if (AstNodeFTask* ftaskp = foundp->nodep()->castNodeFTask()) { - if (nodep->expect() == AstParseRefExp::PX_FTASK) { - AstNodeFTaskRef* refp = nodep->ftaskrefp()->castNodeFTaskRef(); - if (!refp) nodep->v3fatalSrc("Parseref indicates FTASKref but none found"); - refp->name(nodep->name()); - refp->dotted(m_ds.m_dotText); // Maybe "" - refp->taskp(ftaskp); - refp->packagep(foundp->packagep()); // Generally set by parse, but might be an import - taskFuncSwapCheck(refp); - refp->unlinkFrBack(); - nodep->replaceWith(refp); pushDeletep(nodep); nodep = NULL; - ok = true; - } - } // if (!ok) { - bool checkImplicit = (onlyVar && m_ds.m_dotText==""); + bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText==""); bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); if (err) { m_statep->preErrorDump(); @@ -1308,7 +1340,7 @@ private: <<"'"<<" as a "<nodep()->typeName() <<" but expected a "<prettyName()); } else { @@ -1335,6 +1367,7 @@ private: // VarRef: Resolve its reference // ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find // errors here now that we have a VarRef. + // No checkNoDot; created and iterated from a parseRef nodep->iterateChildren(*this); if (!nodep->varp()) { UINFO(9," linkVarRef se"<<(void*)m_curSymp<<" n="<user3SetOnce()) return; UINFO(8," "<varp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. @@ -1408,6 +1442,7 @@ private: } } virtual void visit(AstVar* nodep, AstNUser*) { + checkNoDot(nodep); nodep->iterateChildren(*this); if (m_statep->forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) { nodep->v3error("Input/output/inout does not appear in port list: "<prettyName()); @@ -1416,6 +1451,17 @@ private: virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { if (nodep->user3SetOnce()) return; UINFO(8," "<lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep()); + m_ds.m_dotPos = DP_SCOPE; + m_ds.m_dotp = NULL; + } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) { + nodep->dotted(m_ds.m_dotText); // Maybe "" + } else { + checkNoDot(nodep); + } if (nodep->packagep() && nodep->taskp()) { // References into packages don't care about cell hierarchy. } else if (!m_modSymp) { @@ -1457,7 +1503,11 @@ private: // Note ParseRef has similar error handling/message output m_statep->preErrorDump(); UINFO(7," ErrFtask curSymp=se"<<(void*)m_curSymp<<" dotSymp=se"<<(void*)dotSymp<dotted() == "") { + if (foundp) { + nodep->v3error("Found definition of '"<prettyName() + <<"'"<<" as a "<nodep()->typeName() + <<" but expected a task/function"); + } else if (nodep->dotted() == "") { nodep->v3error("Can't find definition of task/function: "<prettyName()); } else { nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); @@ -1466,7 +1516,12 @@ private: } taskFuncSwapCheck(nodep); } - nodep->iterateChildren(*this); + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); + nodep->iterateChildren(*this); + } + m_ds = lastStates; } virtual void visit(AstSelBit* nodep, AstNUser*) { if (nodep->user3SetOnce()) return; @@ -1480,12 +1535,40 @@ private: } // And pass up m_ds.m_dotText } + // Pass dot state down to fromp() nodep->fromp()->iterateAndNext(*this); - nodep->bitp()->iterateAndNext(*this); - nodep->attrp()->iterateAndNext(*this); + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); + nodep->bitp()->iterateAndNext(*this); + nodep->attrp()->iterateAndNext(*this); + } + m_ds = lastStates; + } + virtual void visit(AstNodePreSel* nodep, AstNUser*) { + // Excludes simple AstSelBit, see above + if (nodep->user3SetOnce()) return; + if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} + nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference"); + m_ds.m_dotErr = true; + return; + } + nodep->lhsp()->iterateAndNext(*this); + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); + nodep->rhsp()->iterateAndNext(*this); + nodep->thsp()->iterateAndNext(*this); + } + m_ds = lastStates; + } + virtual void visit(AstMemberSel* nodep, AstNUser*) { + // checkNoDot not appropriate, can be under a dot + nodep->iterateChildren(*this); } virtual void visit(AstBegin* nodep, AstNUser*) { UINFO(5," "<getNodeSym(nodep); @@ -1497,6 +1580,7 @@ private: } virtual void visit(AstNodeFTask* nodep, AstNUser*) { UINFO(5," "<user3SetOnce()) return; + if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) { + if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep()); + m_ds.m_dotPos = DP_SCOPE; + m_ds.m_dotp = NULL; + } else { + checkNoDot(nodep); + } if (!nodep->defp()) { VSymEnt* foundp; if (nodep->packagep()) { @@ -1528,6 +1621,7 @@ private: virtual void visit(AstDpiExport* nodep, AstNUser*) { // AstDpiExport: Make sure the function referenced exists, then dump it nodep->iterateChildren(*this); + checkNoDot(nodep); VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); AstNodeFTask* taskp = foundp->nodep()->castNodeFTask(); if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "<prettyName()); } @@ -1541,10 +1635,12 @@ private: } virtual void visit(AstPackageImport* nodep, AstNUser*) { // No longer needed + checkNoDot(nodep); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate + checkNoDot(nodep); nodep->iterateChildren(*this); } public: diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 5a8aa9858..943535e55 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -19,11 +19,7 @@ //************************************************************************* // LinkParse TRANSFORMATIONS: // Top-down traversal -// Replace ParseRef with lower parseref -// TASKREF(PARSEREF(DOT(TEXTa,TEXTb))) -> DOT(PARSEREF(a),TASKREF(b)) -// PARSEREF(TEXTa) -> PARSEREF(a) (no change) -// PARSEREF(DOT(TEXTa,TEXTb)) -> DOT(PARSEREF(A),PARSEREF(b) -// PARSEREF(DOT(DOT(TEXTa,TEXTb),TEXTc)) -> DOT(DOT(PARSEREF(a),PARSEREF(b)),PARSEREF(c)) +// Move some attributes around //************************************************************************* #include "config_build.h" @@ -57,7 +53,6 @@ private: typedef set FileLineSet; // STATE - AstParseRefExp m_exp; // Type of data we're looking for AstVar* m_varp; // Variable we're under ImplTypedefMap m_implTypedef; // Created typedefs for each FileLineSet m_filelines; // Filelines that have been seen @@ -66,7 +61,6 @@ private: bool m_needStart; // Need start marker on lower AstParse AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module AstNodeModule* m_modp; // Current module - AstParseRef* m_rightParsep; // RHS()-most parseref // METHODS static int debug() { @@ -90,125 +84,17 @@ private: } } - void checkExpected(AstNode* nodep) { - if (m_exp != AstParseRefExp::PX_NONE) { - nodep->v3fatalSrc("Tree syntax error: Not expecting "<type()<<" under a "<backp()->type()); - m_exp = AstParseRefExp::PX_NONE; - } - } - // VISITs virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { if (!nodep->user1SetOnce()) { // Process only once. cleanFileline(nodep); UINFO(5," "<iterateChildren(*this); m_valueModp = upperValueModp; } } - virtual void visit(AstParseRef* nodep, AstNUser*) { - if (nodep->user1SetOnce()) return; // Process only once. - // VarRef: Parse its reference - UINFO(5," "<ftaskrefp()) nodep->ftaskrefp()->iterateAndNext(*this); - } - m_exp = lastExp; - } - if (nodep->start()) { // Start of new parseref stack - // The start parseref indicates the type of element to be created. - // Push the start marking down to the lowest non-start parseref under any DOTs. - // May be a varref inside a select, etc, so save state and recurse - AstParseRefExp lastExp = m_exp; - AstParseRef* lastRightp = m_rightParsep; - { - m_exp = nodep->expect(); - m_rightParsep = NULL; - m_needStart = true; - nodep->lhsp()->accept(*this); - if (!m_rightParsep) nodep->v3fatalSrc("No child ParseRef found"); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - if (nodep->expect() == AstParseRefExp::PX_FTASK) { - // Put the FTaskRef under the final DOT - AstNodeFTaskRef* ftaskrefp = nodep->ftaskrefp()->castNodeFTaskRef(); - if (!ftaskrefp) nodep->v3fatalSrc("Parseref indicates FTASKref but none found"); - ftaskrefp->name(m_rightParsep->name()); - ftaskrefp->unlinkFrBack(); - if (ftaskrefp->packagep()) { - // Can remove the parse stack entirely and V3LinkDot will deal with it natively - lhsp = ftaskrefp; - } else { - m_rightParsep->ftaskrefp(ftaskrefp); - } - } - nodep->replaceWith(lhsp); - nodep->deleteTree(); nodep=NULL; - } - m_exp = lastExp; - m_rightParsep = lastRightp; - } - else { // inside existing stack - nodep->expect(m_exp); - m_rightParsep = nodep; - // Move the start marker down to a standalone parse reference - if (m_needStart) { nodep->start(true); m_needStart = false; } - } - } - virtual void visit(AstDot* nodep, AstNUser*) { - UINFO(5," "<user1SetOnce()) { // Process only once. - cleanFileline(nodep); - if (m_exp == AstParseRefExp::PX_NONE) nodep->v3fatalSrc("Tree syntax error: Not expecting "<type()<<" under a "<backp()->type()); - AstParseRefExp lastExp = m_exp; - m_exp = AstParseRefExp::PX_PREDOT; - if (m_needStart) { nodep->start(true); m_needStart = false; } - nodep->lhsp()->iterateAndNext(*this); - m_exp = lastExp; - nodep->rhsp()->iterateAndNext(*this); - } - } - virtual void visit(AstSelBit* nodep, AstNUser*) { - if (!nodep->user1SetOnce()) { // Process only once. - cleanFileline(nodep); - if (m_exp==AstParseRefExp::PX_FTASK) { - nodep->v3error("Syntax Error: Range selection '[]' is not allowed as part of function/task names"); - } else { - nodep->lhsp()->iterateAndNext(*this); - AstParseRefExp lastExp = m_exp; - { - m_exp = AstParseRefExp::PX_NONE; - nodep->rhsp()->iterateAndNext(*this); - } - m_exp = lastExp; - } - } - } - virtual void visit(AstNodePreSel* nodep, AstNUser*) { - // Excludes simple AstSel, see above - if (!nodep->user1SetOnce()) { // Process only once. - cleanFileline(nodep); - if (m_exp == AstParseRefExp::PX_PREDOT) { // Already under dot, so this is {modulepart} DOT {modulepart} - nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference"); - } else if (m_exp==AstParseRefExp::PX_FTASK) { - nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed as part of function/task names"); - } else { - nodep->lhsp()->iterateAndNext(*this); - AstParseRefExp lastExp = m_exp; - { - m_exp = AstParseRefExp::PX_NONE; - nodep->rhsp()->iterateAndNext(*this); - nodep->thsp()->iterateAndNext(*this); - } - m_exp = lastExp; - } - } - } virtual void visit(AstEnumItem* nodep, AstNUser*) { // Expand ranges cleanFileline(nodep); @@ -379,7 +265,6 @@ private: virtual void visit(AstNodeModule* nodep, AstNUser*) { // Module: Create sim table for entire module and iterate cleanFileline(nodep); - checkExpected(nodep); // So we detect node types we forgot to list here // m_modp = nodep; m_valueModp = nodep; @@ -390,7 +275,6 @@ private: void visitIterateNoValueMod(AstNode* nodep) { // Iterate a node which shouldn't have any local variables moved to an Initial cleanFileline(nodep); - checkExpected(nodep); // So we detect node types we forgot to list here // AstNodeModule* upperValueModp = m_valueModp; m_valueModp = NULL; @@ -415,7 +299,6 @@ private: virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate cleanFileline(nodep); - checkExpected(nodep); // So we detect node types we forgot to list here nodep->iterateChildren(*this); } @@ -423,13 +306,11 @@ public: // CONSTUCTORS LinkParseVisitor(AstNetlist* rootp) { m_varp = NULL; - m_exp = AstParseRefExp::PX_NONE; m_modp = NULL; m_inAlways = false; m_inGenerate = false; m_needStart = false; m_valueModp = NULL; - m_rightParsep = NULL; rootp->accept(*this); } virtual ~LinkParseVisitor() {} diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 43dcd3a12..3e305ea49 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -74,6 +74,7 @@ struct V3ParseBisonYYSType { AstNodeSenItem* senitemp; AstNodeVarRef* varnodep; AstPackage* packagep; + AstPackageRef* packagerefp; AstParseRef* parserefp; AstPatMember* patmemberp; AstPin* pinp; diff --git a/src/verilog.y b/src/verilog.y index ee81f6cd1..f0cea2d95 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -499,6 +499,7 @@ class AstSenTree; %token yP_WILDNOTEQUAL "!=?" %token yP_GTE ">=" %token yP_LTE "<=" +%token yP_LTE__IGNORE "<=-ignored" // Used when expr:<= means assignment %token yP_SLEFT "<<" %token yP_SRIGHT ">>" %token yP_SSRIGHT ">>>" @@ -560,7 +561,7 @@ class AstSenTree; %left '^' yP_XNOR %left '&' yP_NAND %left yP_EQUAL yP_NOTEQUAL yP_CASEEQUAL yP_CASENOTEQUAL yP_WILDEQUAL yP_WILDNOTEQUAL -%left '>' '<' yP_GTE yP_LTE +%left '>' '<' yP_GTE yP_LTE yP_LTE__IGNORE %left yP_SLEFT yP_SRIGHT yP_SSRIGHT %left '+' '-' %left '*' '/' '%' @@ -2023,8 +2024,7 @@ statement_item: // IEEE: statement_item //UNSUP fexprLvalue '=' dynamic_array_new ';' { UNSUP } // // // IEEE: nonblocking_assignment - | idClassSel yP_LTE delayE expr ';' { $$ = new AstAssignDly($2,$1,$4); } - | '{' variable_lvalueConcList '}' yP_LTE delayE expr ';' { $$ = new AstAssignDly($4,$2,$6); } + | fexprLvalue yP_LTE delayE expr ';' { $$ = new AstAssignDly($2,$1,$4); } //UNSUP fexprLvalue yP_LTE delay_or_event_controlE expr ';' { UNSUP } // // // IEEE: procedural_continuous_assignment @@ -2065,7 +2065,7 @@ statement_item: // IEEE: statement_item // // Expr here must result in a subroutine_call | task_subroutine_callNoMethod ';' { $$ = $1; } //UNSUP fexpr '.' array_methodNoRoot ';' { UNSUP } - //UNSUP fexpr '.' task_subroutine_callNoMethod ';' { UNSUP } + | fexpr '.' task_subroutine_callNoMethod ';' { $$ = new AstDot($2,$1,$3); } //UNSUP fexprScope ';' { UNSUP } // // Not here in IEEE; from class_constructor_declaration // // Because we've joined class_constructor_declaration into generic functions @@ -2140,35 +2140,22 @@ statementVerilatorPragmas: ; foperator_assignment: // IEEE: operator_assignment (for first part of expression) - idClassSel '=' delayE expr { $$ = new AstAssign($2,$1,$4); } - | idClassSel '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen($3,$1,$5,$7); } - | '{' variable_lvalueConcList '}' '=' delayE expr { $$ = new AstAssign($4,$2,$6); } + fexprLvalue '=' delayE expr { $$ = new AstAssign($2,$1,$4); } + | fexprLvalue '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen($3,$1,$5,$7); } // //UNSUP ~f~exprLvalue '=' delay_or_event_controlE expr { UNSUP } //UNSUP ~f~exprLvalue yP_PLUS(etc) expr { UNSUP } - | idClassSel yP_PLUSEQ expr { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_MINUSEQ expr { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_TIMESEQ expr { $$ = new AstAssign($2,$1,new AstMul ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_DIVEQ expr { $$ = new AstAssign($2,$1,new AstDiv ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_MODEQ expr { $$ = new AstAssign($2,$1,new AstModDiv ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_ANDEQ expr { $$ = new AstAssign($2,$1,new AstAnd ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_OREQ expr { $$ = new AstAssign($2,$1,new AstOr ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_XOREQ expr { $$ = new AstAssign($2,$1,new AstXor ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_SLEFTEQ expr { $$ = new AstAssign($2,$1,new AstShiftL ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); } - | idClassSel yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); } - // - | '{' variable_lvalueConcList '}' yP_PLUSEQ expr { $$ = new AstAssign($4,$2,new AstAdd ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_MINUSEQ expr { $$ = new AstAssign($4,$2,new AstSub ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_TIMESEQ expr { $$ = new AstAssign($4,$2,new AstMul ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_DIVEQ expr { $$ = new AstAssign($4,$2,new AstDiv ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_MODEQ expr { $$ = new AstAssign($4,$2,new AstModDiv ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_ANDEQ expr { $$ = new AstAssign($4,$2,new AstAnd ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_OREQ expr { $$ = new AstAssign($4,$2,new AstOr ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_XOREQ expr { $$ = new AstAssign($4,$2,new AstXor ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_SLEFTEQ expr { $$ = new AstAssign($4,$2,new AstShiftL ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_SRIGHTEQ expr { $$ = new AstAssign($4,$2,new AstShiftR ($4,$2->cloneTree(true),$5)); } - | '{' variable_lvalueConcList '}' yP_SSRIGHTEQ expr { $$ = new AstAssign($4,$2,new AstShiftRS($4,$2->cloneTree(true),$5)); } + | fexprLvalue yP_PLUSEQ expr { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_MINUSEQ expr { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_TIMESEQ expr { $$ = new AstAssign($2,$1,new AstMul ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_DIVEQ expr { $$ = new AstAssign($2,$1,new AstDiv ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_MODEQ expr { $$ = new AstAssign($2,$1,new AstModDiv ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_ANDEQ expr { $$ = new AstAssign($2,$1,new AstAnd ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_OREQ expr { $$ = new AstAssign($2,$1,new AstOr ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_XOREQ expr { $$ = new AstAssign($2,$1,new AstXor ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_SLEFTEQ expr { $$ = new AstAssign($2,$1,new AstShiftL ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); } + | fexprLvalue yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); } ; finc_or_dec_expression: // ==IEEE: inc_or_dec_expression @@ -2309,15 +2296,13 @@ for_step: // IEEE: for_step //************************************************ // Functions/tasks -taskRef: // IEEE: part of tf_call - idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstTaskRef($1->fileline(),"",NULL)); $$->start(true); } - | idDotted '(' list_of_argumentsE ')' { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstTaskRef($1->fileline(),"",$3)); $$->start(true); } - //UNSUP: package_scopeIdFollows idDotted { } - //UNSUP: package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { } - //UNSUP: idDotted is really just id to allow dotted method calls +taskRef: // IEEE: part of tf_call + id { $$ = new AstTaskRef($1,*$1,NULL); } + | id '(' list_of_argumentsE ')' { $$ = new AstTaskRef($1,*$1,$3); } + | package_scopeIdFollows id '(' list_of_argumentsE ')' { $$ = AstDot::newIfPkg($2, $1, new AstTaskRef($2,*$2,$4)); } ; -funcRef: // IEEE: part of tf_call +funcRef: // IEEE: part of tf_call // // package_scope/hierarchical_... is part of expr, so just need ID // // making-a id-is-a // // ----------------- ------------------ @@ -2326,8 +2311,8 @@ funcRef: // IEEE: part of tf_call // // property_instance property_identifier property_actual_arg // // sequence_instance sequence_identifier sequence_actual_arg // // let_expression let_identifier let_actual_arg - idDotted '(' list_of_argumentsE ')' { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstFuncRef($2, "", $3)); $$->start(true); } - | package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { AstFuncRef* f=new AstFuncRef($3,"",$4); f->packagep($1); $$ = new AstParseRef($2->fileline(), AstParseRefExp::PX_FTASK, "", $2, f); $$->start(true); } + id '(' list_of_argumentsE ')' { $$ = new AstFuncRef($2, *$1, $3); } + | package_scopeIdFollows id '(' list_of_argumentsE ')' { $$ = AstDot::newIfPkg($2, $1, new AstFuncRef($2,*$2,$4)); } //UNSUP: idDotted is really just id to allow dotted method calls ; @@ -2745,7 +2730,7 @@ expr: // IEEE: part of expression/constant_expression/primary // | function_subroutine_callNoMethod { $$ = $1; } // // method_call - //UNSUP ~l~expr '.' function_subroutine_callNoMethod { UNSUP } + | ~l~expr '.' function_subroutine_callNoMethod { $$ = new AstDot($2,$1,$3); } // // method_call:array_method requires a '.' //UNSUP ~l~expr '.' array_methodNoRoot { UNSUP } // @@ -2781,6 +2766,7 @@ expr: // IEEE: part of expression/constant_expression/primary // //---------------------- // + // // Part of expr that may also be used as lvalue | ~l~exprOkLvalue { $$ = $1; } // //---------------------- @@ -2800,6 +2786,10 @@ expr: // IEEE: part of expression/constant_expression/primary //UNSUP ~l~expr yDIST '{' dist_list '}' { UNSUP } ; +fexpr: // For use as first part of statement (disambiguates <=) + BISONPRE_COPY(expr,{s/~l~/f/g; s/~r~/f/g; s/~f__IGNORE~/__IGNORE/g;}) // {copied} + ; + exprNoStr: // expression with string removed BISONPRE_COPY(expr,{s/~noStr__IGNORE~/Ignore/g;}) // {copied} ; @@ -2818,6 +2808,14 @@ exprOkLvalue: // expression that's also OK to use as a variable_lvalue //UNSUP streaming_concatenation { UNSUP } ; +fexprOkLvalue: // exprOkLValue, For use as first part of statement (disambiguates <=) + BISONPRE_COPY(exprOkLvalue,{s/~l~/f/g}) // {copied} + ; + +fexprLvalue: // For use as first part of statement (disambiguates <=) + fexprOkLvalue { $$=$1; $$ = $1; } + ; + exprScope: // scope and variable for use to inside an expression // // Here we've split method_call_root | implicit_class_handle | class_scope | package_scope // // from the object being called and let expr's "." deal with resolving it. @@ -2827,17 +2825,20 @@ exprScope: // scope and variable for use to inside an expression // // Or method_call_body without parenthesis // // See also varRefClassBit, which is the non-expr version of most of this //UNSUP yTHIS { UNSUP } - idClassSel { $$ = $1; } - //UNSUP: idArrayed instead of idClassSel - //UNSUP package_scopeIdFollows idArrayed { UNSUP } + idArrayed { $$ = $1; } + | package_scopeIdFollows idArrayed { $$ = AstDot::newIfPkg($2->fileline(), $1, $2); } //UNSUP class_scopeIdFollows idArrayed { UNSUP } - //UNSUP ~l~expr '.' idArrayed { UNSUP } + | ~l~expr '.' idArrayed { $$ = new AstDot($2,$1,$3); } // // expr below must be a "yTHIS" //UNSUP ~l~expr '.' ySUPER { UNSUP } // // Part of implicit_class_handle //UNSUP ySUPER { UNSUP } ; +fexprScope: // exprScope, For use as first part of statement (disambiguates <=) + BISONPRE_COPY(exprScope,{s/~l~/f/g}) // {copied} + ; + // Psl excludes {}'s by lexer converting to different token exprPsl: expr { $$ = $1; } @@ -3163,8 +3164,8 @@ variable_lvalueConcList: // IEEE: part of variable_lvalue: '{' variable_l ; // VarRef to dotted, and/or arrayed, and/or bit-ranged variable -idClassSel: // Misc Ref to dotted, and/or arrayed, and/or bit-ranged variable - idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_VAR_ANY, "", $1, NULL); $$->start(true); } +idClassSel: // Misc Ref to dotted, and/or arrayed, and/or bit-ranged variable + idDotted { $$ = $1; } // // IEEE: [ implicit_class_handle . | package_scope ] hierarchical_variable_identifier select //UNSUP yTHIS '.' idDotted { UNSUP } //UNSUP ySUPER '.' idDotted { UNSUP } diff --git a/test_regress/t/t_param_package.pl b/test_regress/t/t_param_package.pl new file mode 100755 index 000000000..f91289753 --- /dev/null +++ b/test_regress/t/t_param_package.pl @@ -0,0 +1,18 @@ +#!/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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_package.v b/test_regress/t/t_param_package.v new file mode 100644 index 000000000..b7ef97b43 --- /dev/null +++ b/test_regress/t/t_param_package.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module + +module t; + Test0 t0 (.val0('0)); + Test1 t1 (.val1('0)); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +package params; + parameter P = 7; +endpackage + +module Test0 (val0); + parameter Z = 1; + input [Z : 0] val0; +endmodule + +module Test1 (val1); + input logic [params::P : 0] val1; // Fully qualified parameter +endmodule