diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 2d4fcfae9..b46299235 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -198,7 +198,6 @@ RAW_OBJS = \ V3Inst.o \ V3Life.o \ V3LifePost.o \ - V3Link.o \ V3LinkCells.o \ V3LinkDot.o \ V3LinkJump.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index 72a22d058..69482608f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1646,13 +1646,14 @@ private: bool m_modPublic:1; // Module has public references bool m_modTrace:1; // Tracing this module bool m_inLibrary:1; // From a library, no error if not used, never top level + bool m_dead:1; // LinkDot believes is dead; will remove in Dead visitors int m_level; // 1=top module, 2=cell off top module, ... int m_varNum; // Incrementing variable number public: AstNodeModule(FileLine* fl, const string& name) : AstNode (fl) ,m_name(name), m_origName(name) - ,m_modPublic(false), m_modTrace(false), m_inLibrary(false) + ,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false) ,m_level(0), m_varNum(0) { } ASTNODE_BASE_FUNCS(NodeModule) virtual void dump(ostream& str); @@ -1677,6 +1678,8 @@ public: bool modPublic() const { return m_modPublic; } void modTrace(bool flag) { m_modTrace = flag; } bool modTrace() const { return m_modTrace; } + void dead(bool flag) { m_dead = flag; } + bool dead() const { return m_dead; } }; //###################################################################### diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c92e6e7db..c673c95e8 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -702,6 +702,7 @@ void AstNodeModule::dump(ostream& str) { str<<" L"<AstNode::dump(str); @@ -831,8 +832,8 @@ void AstNodeFTask::dump(ostream& str) { void AstBegin::dump(ostream& str) { this->AstNode::dump(str); if (unnamed()) str<<" [UNNAMED]"; - if (hidden()) str<<" [HIDDEN]"; if (generate()) str<<" [GEN]"; + if (genforp()) str<<" [GENFOR]"; } void AstCoverDecl::dump(ostream& str) { this->AstNode::dump(str); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index c2bc2db07..673a17df0 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2344,7 +2344,6 @@ struct AstBegin : public AstNode { private: string m_name; // Name of block bool m_unnamed; // Originally unnamed - bool m_hidden; // Inserted by verilator, not user bool m_generate; // Underneath a generate public: // Node that simply puts name into the output stream @@ -2353,7 +2352,6 @@ public: , m_name(name) { addNOp1p(stmtsp); m_unnamed = (name==""); - m_hidden = false; m_generate = generate; } ASTNODE_NODE_FUNCS(Begin, BEGIN) @@ -2361,13 +2359,12 @@ public: virtual string name() const { return m_name; } // * = Block name virtual void name(const string& name) { m_name = name; } // op1 = Statements - AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements + AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements void addStmtsp(AstNode* nodep) { addNOp1p(nodep); } - AstNode* flatsp() const { return op2p()->castNode(); } // op2 = Statements that don't appear under new scope - void addFlatsp(AstNode* nodep) { addNOp2p(nodep); } + AstNode* genforp() const { return op2p(); } // op2 = GENFOR, if applicable, + // might NOT be a GenFor, as loop unrolling replaces with Begin + void addGenforp(AstGenFor* nodep) { addOp2p(nodep); } bool unnamed() const { return m_unnamed; } - void hidden(bool flag) { m_hidden = flag; } - bool hidden() const { return m_hidden; } void generate(bool flag) { m_generate = flag; } bool generate() const { return m_generate; } }; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index d3f754507..ce2e0658b 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -142,24 +142,17 @@ private: // Remap var names and replace lower Begins nodep->stmtsp()->iterateAndNext(*this); - + if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier"); } m_namedScope = oldScope; m_unnamedScope = oldUnnamed; - // Don't change var names of generate FOR() variables, but do recurse into a child FOR - nodep->flatsp()->iterateAndNext(*this); - // Cleanup AstNode* addsp = NULL; if (AstNode* stmtsp = nodep->stmtsp()) { stmtsp->unlinkFrBackWithNext(); addsp = addsp->addNextNull(stmtsp); } - if (AstNode* stmtsp = nodep->flatsp()) { - stmtsp->unlinkFrBackWithNext(); - addsp = addsp->addNextNull(stmtsp); - } if (addsp) { nodep->replaceWith(addsp); } else { diff --git a/src/V3Link.cpp b/src/V3Link.cpp deleted file mode 100644 index d2c9e1e0c..000000000 --- a/src/V3Link.cpp +++ /dev/null @@ -1,725 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Resolve module/signal name references -// -// Code available from: http://www.veripool.org/verilator -// -//************************************************************************* -// -// Copyright 2003-2012 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. -// -// Verilator is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -//************************************************************************* -// Link TRANSFORMATIONS: -// Top-down traversal -// Cells: -// Connect pins -// VarRef's: -// Link to var they reference -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "V3Global.h" -#include "V3Link.h" -#include "V3SymTable.h" -#include "V3Ast.h" - -//###################################################################### -// Link state, as a visitor of each AstNode - -class LinkVisitor : public AstNVisitor { -private: - // NODE STATE - // Entire netlist: - // AstVar/Module/Task::user1() // AstPackage* Set if inside a package - // AstVar::user2p() // bool True if port set for this variable - // AstVar/Module::user3p() // VSymEnt* Table used to create this variable - // AstNodeModule::user4p() // VSymEnt* Module's Symbol table - // AstNodeFTask::user4p() // VSymEnt* Local Symbol table - // AstBegin::user4p() // VSymEnt* Local Symbol table - // AstVar::user5p() // AstPin* True if port attached to a pin - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - AstUser4InUse m_inuser4; - AstUser5InUse m_inuser5; - - // ENUMS - enum IdState { // Which loop through the tree - ID_FIND, // Find all identifiers and add to lists - ID_PARAM, // Move parameters to cell definitions - ID_RESOLVE}; // Resolve all identifiers and report errors - - // STATE - // Below state needs to be preserved between each module call. - VSymGraph m_syms; // Symbol table graph - AstPackage* m_packagep; // Current package - AstCell* m_cellp; // Current cell - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Current function/task - IdState m_idState; // Id linking mode (find or resolve) - int m_paramNum; // Parameter number, for position based connection - VSymEnt* m_curVarsp; // Symbol table of variables and tasks under table we're inserting into - VSymEnt* m_cellVarsp; // Symbol table of variables under cell's module - int m_beginNum; // Begin block number, 0=none seen - int m_modBeginNum; // Begin block number in module, 0=none seen - bool m_inGenerate; // Inside a generate - - static int debug() { - static int level = -1; - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); - return level; - } - - // METHODS - VSymEnt* symsFindNew(AstNode* nodep) { - // Find or create symbol table for this node - VSymEnt* symsp = nodep->user4p()->castSymEnt(); - if (symsp) { - return symsp; - } else { - symsp = new VSymEnt(&m_syms, nodep); - nodep->user4p(symsp); - return symsp; - } - } - VSymEnt* symsFind(AstNode* nodep) { - // Find or create symbol table for this node - if (VSymEnt* symsp = nodep->user4p()->castSymEnt()) { - return symsp; - } else { - nodep->v3fatalSrc("Symbol table not found looking up symbol"); - return NULL; - } - } - - void symsInsert(const string& name, AstNode* nodep) { - // Insert into symbol table, and remember what table the node is in - m_curVarsp->insert(name, symsFindNew(nodep)); - nodep->user3p(m_curVarsp); - nodep->user1p(m_packagep); - } - - AstPackage* packageFor(AstNode* nodep) { - if (nodep) return nodep->user1p()->castNode()->castPackage(); // Loaded by symsInsert - else return NULL; - } - - bool linkVarName (AstVarRef* nodep) { - // Return true if changed, and caller should end processing - if (!nodep->varp()) { - AstNode* foundp = m_curVarsp->findIdFallback(nodep->name())->nodep(); - if (AstVar* varp = foundp->castVar()) { - nodep->varp(varp); - nodep->packagep(packageFor(varp)); - } - else if (AstEnumItem* valuep = foundp->castEnumItem()) { - AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, packageFor(valuep)); - nodep->replaceWith(newp); - nodep->deleteTree(); nodep=NULL; - return true; // Edited - } - } - return false; - } - - string nodeTextType(AstNode* nodep) { - if (nodep->castVar()) return "variable"; - else if (nodep->castCell()) return "cell"; - else if (nodep->castTask()) return "task"; - else if (nodep->castFunc()) return "function"; - else if (nodep->castBegin()) return "block"; - else return nodep->prettyTypeName(); - } - - string ucfirst(const string& text) { - string out = text; - out[0] = toupper(out[0]); - return out; - } - - void findAndInsertAndCheck(AstNode* nodep, const string& name) { - // Lookup the given name under current symbol table - // Insert if not found - // Report error if there's a duplicate - // - // Note we only check for conflicts at the same level; it's ok if one block hides another - // We also wouldn't want to not insert it even though it's lower down - AstNode* foundp = m_curVarsp->findIdFlat(name)->nodep(); - if (!foundp) { - symsInsert(nodep->name(), nodep); - foundp = nodep; - } else if (nodep==foundp) { // Already inserted. - // Good. - } else if ((nodep->castBegin() && foundp->castBegin()) - && m_inGenerate) { - // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks - // See t_gen_forif.v for an example. - } else if (nodep->type() == foundp->type()) { - nodep->v3error("Duplicate declaration of "<prettyName()<warnMore()<<"... Location of original declaration"); - } else { - nodep->v3error("Unsupported in C: "<prettyName()<warnMore()<<"... Location of original declaration"); - } - } - - void createImplicitVar (AstVarRef* forrefp, bool noWarn) { - // Create implicit after warning - if (linkVarName(forrefp)) { forrefp=NULL; return; } - if (!forrefp->varp()) { - if (!noWarn) { - if (forrefp->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) { - forrefp->v3error("Signal definition not found, and implicit disabled with `default_nettype: "<prettyName()); - } else { - forrefp->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<prettyName()); - } - } - AstVar* newp = new AstVar (forrefp->fileline(), AstVarType::WIRE, - forrefp->name(), VFlagLogicPacked(), 1); - - newp->trace(m_modp->modTrace()); - m_modp->addStmtp(newp); - // Link it to signal list - IdState old_id = m_idState; - VSymEnt* old_varsp = m_curVarsp; - m_idState = ID_FIND; - m_curVarsp = symsFind(m_modp); // Must add the variable under the module; curVarsp might be lower now - newp->accept(*this); - m_idState = old_id; - m_curVarsp = old_varsp; - } - } - - // VISITs - virtual void visit(AstNetlist* nodep, AstNUser* vup) { - // Top scope - m_curVarsp = symsFindNew(nodep); - // Recurse..., backward as must do packages before using packages - m_idState = ID_FIND; - nodep->iterateChildrenBackwards(*this); - if (debug()==9) m_curVarsp->dump(cout,"-curvars: ",true/*user4p_is_table*/); - m_idState = ID_PARAM; - nodep->iterateChildrenBackwards(*this); - m_idState = ID_RESOLVE; - nodep->iterateChildrenBackwards(*this); - nodep->checkTree(); - } - - virtual void visit(AstNodeModule* nodep, AstNUser*) { - // Module: Create sim table for entire module and iterate - UINFO(2,"Link Module: "<v3fatalSrc("NULL"); - if (nodep->castPackage()) m_packagep = nodep->castPackage(); - if (m_packagep && m_packagep->isDollarUnit()) { // $unit goes on "top" - nodep->user4p(m_curVarsp); - // Don't insert dunit itself, or symtable->dump will loop-recurse - } else { - findAndInsertAndCheck(nodep, nodep->name()); - m_curVarsp = symsFindNew(nodep); - m_curVarsp->fallbackp(upperVarsp); - UINFO(9, "New module scope "<iterateChildren(*this); - // Prep for next - m_modp = NULL; - m_packagep = NULL; - } - m_curVarsp = upperVarsp; - m_cellp = upperCellp; - } - - virtual void visit(AstGenerate* nodep, AstNUser*) { - // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks - // See t_gen_forif.v for an example. - bool lastInGen = m_inGenerate; - { - m_inGenerate = true; - nodep->iterateChildren(*this); - } - m_inGenerate = lastInGen; - } - - virtual void visit(AstVar* nodep, AstNUser*) { - // Var: Remember its name for later resolution - if (!m_curVarsp) nodep->v3fatalSrc("Var not under module??\n"); - nodep->iterateChildren(*this); - if (m_idState==ID_FIND) { - // We used modTrace before leveling, and we may now - // want to turn it off now that we know the levelizations - if (v3Global.opt.traceDepth() - && (m_modp->level()-1) > v3Global.opt.traceDepth()) { - m_modp->modTrace(false); - nodep->trace(false); - } - // Find under either a task or the module's vars - AstNode* foundp = m_curVarsp->findIdFallback(nodep->name())->nodep(); - AstVar* findvarp = foundp->castVar(); - bool ins=false; - if (!foundp) { - ins=true; - } else if (!findvarp && m_curVarsp->findIdFlat(nodep->name())) { - nodep->v3error("Unsupported in C: Variable has same name as " - <prettyName()); - } else if (findvarp != nodep) { - UINFO(4,"DupVar: "<user3p()->castSymEnt() == m_curVarsp) { // Only when on same level - if ((findvarp->isIO() && nodep->isSignal()) - || (findvarp->isSignal() && nodep->isIO())) { - findvarp->combineType(nodep); - nodep->fileline()->modifyStateInherit(nodep->fileline()); - AstBasicDType* bdtypep = findvarp->childDTypep()->castBasicDType(); - if (bdtypep && bdtypep->implicit()) { - // Then have "input foo" and "real foo" so the dtype comes from the other side. - AstNodeDType* newdtypep = nodep->subDTypep(); - if (!newdtypep || !nodep->childDTypep()) findvarp->v3fatalSrc("No child type?"); - bdtypep->unlinkFrBack()->deleteTree(); - newdtypep->unlinkFrBack(); - findvarp->childDTypep(newdtypep); - } - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - } else { - nodep->v3error("Duplicate declaration of signal: "<prettyName()<warnMore()<<"... Location of original declaration"); - } - } else { - // User can disable the message at either point - if (!(m_ftaskp && m_ftaskp->dpiImport()) - && !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) - && !foundp->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { - nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<name()<warnMore()<<"... Location of original declaration"); - } - ins = true; - } - } - if (ins) { - symsInsert(nodep->name(), nodep); - if (nodep->isGParam()) { - m_paramNum++; - symsInsert("__paramNumber"+cvtToStr(m_paramNum), nodep); - } - } - } - if (m_idState==ID_RESOLVE) { - if (nodep->isIO() && !m_ftaskp && !nodep->user2()) { - nodep->v3error("Input/output/inout does not appear in port list: "<prettyName()); - } - } - } - virtual void visit(AstVarRef* nodep, AstNUser*) { - // VarRef: Resolve its reference - nodep->iterateChildren(*this); - if (m_idState==ID_RESOLVE && !nodep->varp()) { - if (linkVarName(nodep)) { nodep=NULL; return; } - if (!nodep->varp()) { - nodep->v3error("Can't find definition of signal: "<prettyName()); - createImplicitVar (nodep, true); // So only complain once - } - } - } - //virtual void visit(AstVarXRef* nodep, AstNUser*) { - // See LinkDotVisitor - //} - - virtual void visit(AstEnumItem* nodep, AstNUser*) { - // EnumItem: Remember its name for later resolution - nodep->iterateChildren(*this); - if (m_idState==ID_FIND) { - // Find under either a task or the module's vars - AstNode* foundp = m_curVarsp->findIdFallback(nodep->name())->nodep(); - AstEnumItem* findvarp = foundp->castEnumItem(); - bool ins=false; - if (!foundp) { - ins=true; - } else if (findvarp != nodep) { - UINFO(4,"DupVar: "<user3p()->castSymEnt() == m_curVarsp) { // Only when on same level - nodep->v3error("Duplicate declaration of enum value: "<prettyName()<warnMore()<<"... Location of original declaration"); - } else { - // User can disable the message at either point - if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) - && !foundp->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { - nodep->v3warn(VARHIDDEN,"Declaration of enum value hides declaration in upper scope: "<name()<warnMore()<<"... Location of original declaration"); - } - ins = true; - } - } - if (ins) { - symsInsert(nodep->name(), nodep); - } - } - } - - virtual void visit(AstNodeFTask* nodep, AstNUser*) { - // NodeTask: Remember its name for later resolution - if (!m_curVarsp) nodep->v3fatalSrc("Function/Task not under module??\n"); - // Remember the existing symbol table scope - VSymEnt* upperVarsp = m_curVarsp; - { - // Create symbol table for the task's vars - m_curVarsp = symsFindNew(nodep); - m_curVarsp->fallbackp(upperVarsp); - - // Convert the func's range to the output variable - // This should probably be done in the Parser instead, as then we could - // just attact normal signal attributes to it. - if (nodep->fvarp() - && !nodep->fvarp()->castVar()) { - AstNodeDType* dtypep = nodep->fvarp()->castNodeDType(); - // If unspecified, function returns one bit; however when we support NEW() it could - // also return the class reference. - if (dtypep) dtypep->unlinkFrBack(); - else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); - AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), - VFlagChildDType(), dtypep); // Not dtype resolved yet - newvarp->funcReturn(true); - newvarp->trace(false); // Not user visible - newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); - nodep->addFvarp(newvarp); - // Explicit insert required, as the var name shadows the upper level's task name - symsInsert(newvarp->name(), newvarp); - } - m_ftaskp = nodep; - nodep->iterateChildren(*this); - m_ftaskp = NULL; - } - m_curVarsp = upperVarsp; - if (m_idState==ID_FIND) { - findAndInsertAndCheck(nodep, nodep->name()); - } - } - virtual void visit(AstBegin* nodep, AstNUser*) { - // Link variables underneath blocks - // Remember the existing symbol table scope - VSymEnt* upperVarsp = m_curVarsp; - // Rename "genblk"s to include a number - // All blocks are numbered in the standard, IE we start with "genblk1" even if only one. - UINFO(8," "<name() == "genblk") { - ++m_beginNum; - nodep->name(nodep->name()+cvtToStr(m_beginNum)); - } - if (m_idState==ID_FIND && nodep->name()=="" && nodep->unnamed()) { - // Unnamed blocks are only important when they contain var - // decls, so search for them. (Otherwise adding all the - // unnamed#'s would just confuse tracing variables in - // places such as tasks, where "task ...; begin ... end" - // are common. - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { - if (stmtp->castVar()) { - ++m_modBeginNum; - nodep->name("unnamedblk"+cvtToStr(m_modBeginNum)); - break; - } - } - } - - // Check naming (we don't really care, but some tools do, so better to warn) - if (m_idState==ID_FIND && nodep->name()!="") { - findAndInsertAndCheck(nodep, nodep->name()); - } - // Recurse - int oldNum = m_beginNum; - if (!nodep->hidden()) m_beginNum = 0; - { - // Create symbol table for the task's vars - m_curVarsp = symsFindNew(nodep); - m_curVarsp->fallbackp(upperVarsp); - nodep->iterateChildren(*this); - } - m_curVarsp = upperVarsp; - m_beginNum = oldNum; - } - virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { - // NodeFTaskRef: Resolve its reference - if (m_idState==ID_RESOLVE && !nodep->taskp()) { - if (nodep->dotted() == "") { - AstNodeFTask* taskp; - if (nodep->packagep()) { - taskp = symsFind(nodep->packagep())->findIdFallback(nodep->name())->nodep()->castNodeFTask(); - } else { - taskp = m_curVarsp->findIdFallback(nodep->name())->nodep()->castNodeFTask(); - } - if (!taskp) { nodep->v3error("Can't find definition of task/function: "<prettyName()); } - nodep->taskp(taskp); - nodep->packagep(packageFor(taskp)); - if (taskp->castTask() && nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<prettyName()); - } - } - nodep->iterateChildren(*this); - } - virtual void visit(AstDpiExport* nodep, AstNUser*) { - // AstDpiExport: Make sure the function referenced exists, then dump it - nodep->iterateChildren(*this); - if (m_idState==ID_RESOLVE) { - AstNodeFTask* taskp; - taskp = m_curVarsp->findIdFallback(nodep->name())->nodep()->castNodeFTask(); - if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "<prettyName()); } - else if (taskp->dpiExport()) { - nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<prettyName()); - } else { - taskp->dpiExport(true); - if (nodep->cname()!="") taskp->cname(nodep->cname()); - } - nodep->unlinkFrBack()->deleteTree(); - } - } - - virtual void visit(AstTypedef* nodep, AstNUser*) { - // Remember its name for later resolution - if (!m_curVarsp) nodep->v3fatalSrc("Typedef not under module??\n"); - nodep->iterateChildren(*this); - if (m_idState==ID_FIND) { - findAndInsertAndCheck(nodep, nodep->name()); - } - } - virtual void visit(AstRefDType* nodep, AstNUser*) { - // Resolve its reference - if (m_idState==ID_RESOLVE && !nodep->defp()) { - AstTypedef* defp; - if (nodep->packagep()) { - defp = symsFind(nodep->packagep())->findIdFlat(nodep->name())->nodep()->castTypedef(); - } else { - defp = m_curVarsp->findIdFallback(nodep->name())->nodep()->castTypedef(); - } - if (!defp) { nodep->v3error("Can't find typedef: "<prettyName()); } - nodep->refDTypep(defp->subDTypep()); - nodep->packagep(packageFor(defp)); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstCell* nodep, AstNUser*) { - // Cell: Resolve its filename. If necessary, parse it. - m_cellp = nodep; - AstNode::user5ClearTree(); - if (m_idState==ID_FIND) { - // Add to list of all cells, for error checking and defparam's - findAndInsertAndCheck(nodep, nodep->name()); - } - if (!nodep->modp()) { - nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out - } - else { - if (nodep->modp()->castNotFoundModule()) { - // Prevent warnings about missing pin connects - if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree(); - if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); - } - // Need to pass the module info to this cell, so we can link up the pin names - else if (m_idState==ID_RESOLVE) { - m_cellVarsp = nodep->modp()->user4p()->castSymEnt(); - UINFO(4,"(Backto) Link Cell: "<dumpTree(cout,"linkcell:"); } - //if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); } - nodep->iterateChildren(*this); - m_cellVarsp = NULL; - } - else if (m_idState==ID_PARAM) { - nodep->iterateChildren(*this); - } - } - m_cellp = NULL; - // Parent module inherits child's publicity - // This is done bottom up in the LinkBotupVisitor stage - } - - virtual void visit(AstPort* nodep, AstNUser*) { - // Port: Stash the pin number - if (!m_curVarsp) nodep->v3fatalSrc("Port not under module??\n"); - if (m_idState==ID_PARAM) { - // Need to set pin numbers after varnames are created - // But before we do the final resolution based on names - AstVar* refp = m_curVarsp->findIdFlat(nodep->name())->nodep()->castVar(); - if (!refp) { - nodep->v3error("Input/output/inout declaration not found for port: "<prettyName()); - } else if (!refp->isIO()) { - nodep->v3error("Pin is not an in/out/inout: "<prettyName()); - } else { - symsInsert("__pinNumber"+cvtToStr(nodep->pinNum()), refp); - refp->user2(true); - } - // Ports not needed any more - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - } - } - - void pinImplicitExprRecurse(AstNode* nodep) { - // Under a pin, Check interconnect expression for a pin reference or a concat. - // Create implicit variable as needed - if (AstVarRef* varrefp = nodep->castVarRef()) { - // 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. - //if (!nodep->modVarp()->rangep()) ... - createImplicitVar(varrefp, false); - } - // These are perhaps a little too generous, as a SELect of siga[sigb] - // perhaps shouldn't create an implicit variable. But, we've warned... - else { - if (nodep->op1p()) pinImplicitExprRecurse(nodep->op1p()); - if (nodep->op2p()) pinImplicitExprRecurse(nodep->op2p()); - if (nodep->op3p()) pinImplicitExprRecurse(nodep->op3p()); - if (nodep->op4p()) pinImplicitExprRecurse(nodep->op4p()); - if (nodep->nextp()) pinImplicitExprRecurse(nodep->nextp()); - } - } - - virtual void visit(AstPin* nodep, AstNUser*) { - // Pin: Link to submodule's port - // ONLY CALLED by visit(AstCell) during ID_RESOLVE and ID_PARAM state - if (m_idState==ID_RESOLVE && !nodep->modVarp()) { - if (!m_cellVarsp) nodep->v3fatalSrc("Pin not under cell?\n"); - AstVar* refp = m_cellVarsp->findIdFlat(nodep->name())->nodep()->castVar(); - if (!refp) { - if (nodep->name() == "__paramNumber1" && m_cellp->modp()->castPrimitive()) { - // Primitive parameter is really a delay we can just ignore - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - return; - } - nodep->v3error("Pin not found: "<prettyName()); - } else if (!refp->isIO() && !refp->isParam()) { - nodep->v3error("Pin is not an in/out/inout/param: "<prettyName()); - } else { - nodep->modVarp(refp); - if (refp->user5p() && refp->user5p()->castNode()!=nodep) { - nodep->v3error("Duplicate pin connection: "<prettyName()<user5p()->castNode()->warnMore() - <<"... Location of original pin connection"); - } else { - refp->user5p(nodep); - } - } - nodep->iterateChildren(*this); - } - // Deal with implicit definitions - do before ID_RESOLVE stage as may be referenced above declaration - if (m_idState==ID_PARAM - && nodep->exprp() // Else specifically unconnected pin - && !nodep->svImplicit()) { // SV 19.11.3: .name pins don't allow implicit decls - pinImplicitExprRecurse(nodep->exprp()); - } - // Early return() above when deleted - } - - virtual void visit(AstAssignW* nodep, AstNUser*) { - // Deal with implicit definitions - // We used to nodep->allowImplicit() here, but it turns out - // normal "assigns" can also make implicit wires. Yuk. - pinImplicitExprRecurse(nodep->lhsp()); - nodep->iterateChildren(*this); - } - virtual void visit(AstAssignAlias* nodep, AstNUser*) { - // tran gates need implicit creation - if (AstVarRef* forrefp = nodep->lhsp()->castVarRef()) { - createImplicitVar(forrefp, false); - } - if (AstVarRef* forrefp = nodep->rhsp()->castVarRef()) { - createImplicitVar(forrefp, false); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstImplicit* nodep, AstNUser*) { - // Unsupported gates need implicit creation - pinImplicitExprRecurse(nodep); - // We're done with implicit gates - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - } - - virtual void visit(AstDefParam* nodep, AstNUser*) { - nodep->iterateChildren(*this); - if (m_idState==ID_PARAM) { - nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<name()<<"(...etc...))"); - AstNode* foundp = m_curVarsp->findIdFallback(nodep->path())->nodep(); - AstCell* cellp = foundp->castCell(); - if (!cellp) { - nodep->v3error("In defparam, cell "<path()<<" never declared"); - } else { - AstNode* exprp = nodep->rhsp()->unlinkFrBack(); - UINFO(9,"Defparam cell "<path()<<"."<name() - <<" <= "<fileline(), - -1, // Pin# not relevant - nodep->name(), - exprp); - cellp->addParamsp(pinp); - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - } - } - } - - virtual void visit(AstPackageImport* nodep, AstNUser*) { - UINFO(2," Link: "<packagep()); - if (nodep->name()!="*") { - VSymEnt* impp = srcp->findIdFlat(nodep->name()); - if (!impp) { - nodep->v3error("Import object not found: "<packagep()->prettyName()<<"::"<prettyName()); - } - } - m_curVarsp->import(srcp, nodep->name()); - // No longer needed - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - } - - virtual void visit(AstNode* nodep, AstNUser*) { - // Default: Just iterate - nodep->iterateChildren(*this); - } - -public: - // CONSTUCTORS - LinkVisitor(AstNetlist* rootp) - : m_syms(rootp) { - m_curVarsp = NULL; - m_cellVarsp = NULL; - m_cellp = NULL; - m_modp = NULL; - m_ftaskp = NULL; - m_packagep = NULL; - m_paramNum = 0; - m_beginNum = 0; - m_modBeginNum = 0; - m_inGenerate = false; - // - rootp->accept(*this); - } - virtual ~LinkVisitor() {} -}; - -//###################################################################### -// Link class functions - -void V3Link::link(AstNetlist* rootp) { - UINFO(4,__FUNCTION__<<": "< VSymEnt*. Last cell that uses this module - // AstVarScope::user2p() -> AstVarScope*. Base alias for this signal + // AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node + // ... Note maybe more than one, as can be multiple hierarchy places + // AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal + // AstVar::user2p() // AstFTask*. If a function variable, the task that links to the variable + // AstVar::user4() // bool. True if port set for this variable + // AstBegin::user4() // bool. Did name processing + // AstNodeModule::user4() // bool. Live module AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; + AstUser4InUse m_inuser4; // TYPES typedef std::multimap NameScopeMap; // MEMBERS VSymGraph m_syms; // Symbol table + VSymEnt* m_dunitEntp; // $unit entry NameScopeMap m_nameScopeMap; // Hash of scope referenced by non-pretty textual name + bool m_forPrimary; // First link bool m_forPrearray; // Compress cell__[array] refs bool m_forScopeCreation; // Remove VarXRefs for V3Scope public: @@ -95,42 +103,101 @@ public: LinkDotState(AstNetlist* rootp, VLinkDotStep step) : m_syms(rootp) { UINFO(4,__FUNCTION__<<": "<findIdFallback(name); - if (findp) return findp; - findp = symp->findIdFallback(altname); - return findp; + static string nodeTextType(AstNode* nodep) { + if (nodep->castVar()) return "variable"; + else if (nodep->castCell()) return "cell"; + else if (nodep->castTask()) return "task"; + else if (nodep->castFunc()) return "function"; + else if (nodep->castBegin()) return "block"; + else return nodep->prettyTypeName(); + } + static string ucfirst(const string& text) { + string out = text; + out[0] = toupper(out[0]); + return out; + } + + VSymEnt* rootEntp() const { return m_syms.rootp(); } + VSymEnt* dunitEntp() const { return m_dunitEntp; } + void checkDuplicate(VSymEnt* lookupSymp, AstNode* nodep, const string& name) { + // Lookup the given name under current symbol table + // Insert if not found + // Report error if there's a duplicate + // + // Note we only check for conflicts at the same level; it's ok if one block hides another + // We also wouldn't want to not insert it even though it's lower down + VSymEnt* foundp = lookupSymp->findIdFlat(name); + AstNode* fnodep = foundp->nodep(); + if (!fnodep) { + // Not found, will add in a moment. + } else if (nodep==fnodep) { // Already inserted. + // Good. + } else if (nodep->castBegin() && fnodep->castBegin() + && nodep->castBegin()->generate()) { + // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks + // See t_gen_forif.v for an example. + } else { + preErrorDump(); + UINFO(4,"Var1 "<type() == fnodep->type()) { + nodep->v3error("Duplicate declaration of "<prettyName()<warnMore()<<"... Location of original declaration"); + } else { + nodep->v3error("Unsupported in C: "<prettyName()<warnMore()<<"... Location of original declaration"); + } + } + } + void insertDUnit(AstNetlist* nodep) { + // $unit on top scope + VSymEnt* symp = new VSymEnt(&m_syms, nodep); + UINFO(9," INSERTdunit se"<<(void*)symp<parentp(rootEntp()); // Needed so backward search can find name of top module + symp->fallbackp(NULL); + rootEntp()->insert("$unit ",symp); // Space so can never name conflict with user code + // + if (m_dunitEntp) nodep->v3fatalSrc("Call insertDUnit only once"); + m_dunitEntp = symp; } -public: VSymEnt* insertTopCell(AstNodeModule* nodep, const string& scopename) { // Only called on the module at the very top of the hierarchy VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTtop se"<<(void*)symp<<" "<parentp(m_syms.rootp()); // Needed so backward search can find name of top module + symp->parentp(rootEntp()); // Needed so backward search can find name of top module + symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff nodep->user1p(symp); - m_syms.rootp()->insert(nodep->origName(),symp); + checkDuplicate(rootEntp(), nodep, nodep->origName()); + rootEntp()->insert(nodep->origName(),symp); if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, symp)); return symp; } VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp, AstCell* nodep, const string& scopename) { + if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTcel se"<<(void*)symp<<" "<user1p(symp); + checkDuplicate(abovep, nodep, nodep->name()); abovep->reinsert(basename, symp); - if (abovep != modSymp) { + if (abovep != modSymp && !modSymp->findIdFlat(nodep->name())) { // If it's foo_DOT_bar, we need to be able to find it under that too. modSymp->reinsert(nodep->name(), symp); } return symp; } - VSymEnt* insertBegin(VSymEnt* abovep, VSymEnt* modSymp, - AstBegin* nodep) { - // A fake point in the hierarchy, corresponding to a begin block + VSymEnt* insertBlock(VSymEnt* abovep, const string& name, AstNode* nodep, AstPackage* packagep) { + // A fake point in the hierarchy, corresponding to a begin or function/task block // After we remove begins these will go away // Note we fallback to the symbol table of the parent, as we want to find variables there // However, cells walk the graph, so cells will appear under the begin/ftask itself @@ -165,24 +233,33 @@ public: VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTblk se"<<(void*)symp<<" above=se"<<(void*)abovep<<" node="<parentp(abovep); - symp->symPrefix(nodep->name()+"__DOT__"); - if (nodep->name() != "") { - // Duplicates are possible, as until resolve generates might have 2 same cells under an if - abovep->reinsert(nodep->name(), symp); + symp->packagep(packagep); + symp->fallbackp(abovep); // Needed so can find $unit stuff + nodep->user1p(symp); + if (name != "") { + checkDuplicate(abovep, nodep, name); } + // Duplicates are possible, as until resolve generates might have 2 same cells under an if + abovep->reinsert(name, symp); return symp; } - void insertSym(VSymEnt* abovep, const string& name, AstNode* nodep) { + void insertSym(VSymEnt* abovep, const string& name, AstNode* nodep, AstPackage* packagep) { if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); - // We don't remember the ent associated with each node, because we need a unique scope entry for each instantiation VSymEnt* symp = new VSymEnt(&m_syms, nodep); - UINFO(9," INSERTsym name='"<second; } +private: + VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) { + VSymEnt* findp = symp->findIdFallback(name); + if (findp) return findp; + if (altname != "") { + UINFO(8," alt fallback\n"); + findp = symp->findIdFallback(altname); + } + return findp; + } +public: VSymEnt* findDotted(VSymEnt* lookupSymp, const string& dotname, string& baddot, VSymEnt*& okSymp) { // Given a dotted hierarchy name, return where in scope it is @@ -216,8 +304,10 @@ public: okSymp = lookupSymp; string altIdent = ""; if (m_forPrearray) { - // Cell foo__[array] before we've expanded arrays is just foo. - if ((pos = ident.find("__BRA__")) != string::npos) { + // GENFOR Begin is foo__BRA__##__KET__ after we've genloop unrolled, + // but presently should be just "foo". + // Likewise cell foo__[array] before we've expanded arrays is just foo + if ((pos = ident.rfind("__BRA__")) != string::npos) { altIdent = ident.substr(0,pos); } } @@ -266,18 +356,59 @@ public: return lookupSymp; } - AstNode* findSymPrefixed(VSymEnt* lookupSymp, const string& dotname, string& baddot) { + VSymEnt* findSymPrefixed(VSymEnt* lookupSymp, const string& dotname, string& baddot) { // Find symbol in given point in hierarchy // For simplicity lookupSymp may be passed NULL result from findDotted if (!lookupSymp) return NULL; UINFO(8,"\t\tfindSymPrefixed "<symPrefix()=="") ? "" : " as ") <<((lookupSymp->symPrefix()=="") ? "" : lookupSymp->symPrefix()+dotname) <<" at se"<findIdFallback(lookupSymp->symPrefix() + dotname)->nodep(); // Might be NULL - if (!nodep) baddot = dotname; - return nodep; + VSymEnt* foundp = lookupSymp->findIdFallback(lookupSymp->symPrefix() + dotname); // Might be NULL + if (!foundp) baddot = dotname; + return foundp; + } + + // METHODS - Variables + bool linkVarName (VSymEnt* lookupSymp, AstVarRef* nodep) { + // Return true if changed, and caller should end processing + if (!nodep->varp()) { + UINFO(9," linkVarName se"<<(void*)lookupSymp<<" n="<v3fatalSrc("NULL lookup symbol table"); + VSymEnt* foundp = lookupSymp->findIdFallback(nodep->name()); + if (AstVar* varp = foundp->nodep()->castVar()) { + nodep->varp(varp); + nodep->packagep(foundp->packagep()); + } + else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) { + AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep()); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + return true; // Edited + } + } + return false; + } + void createImplicitVar (VSymEnt* lookupSymp, AstVarRef* nodep, AstNodeModule* modp, VSymEnt* moduleSymp, bool noWarn) { + // Create implicit after warning + if (linkVarName(lookupSymp, nodep)) { nodep=NULL; return; } + if (!nodep->varp()) { + if (!noWarn) { + if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) { + nodep->v3error("Signal definition not found, and implicit disabled with `default_nettype: "<prettyName()); + } else { + nodep->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<prettyName()); + } + } + AstVar* newp = new AstVar (nodep->fileline(), AstVarType::WIRE, + nodep->name(), VFlagLogicPacked(), 1); + newp->trace(modp->modTrace()); + modp->addStmtp(newp); + // Link it to signal list, must add the variable under the module; current scope might be lower now + insertSym(moduleSymp, newp->name(), newp, NULL/*packagep*/); + } } }; @@ -287,11 +418,18 @@ class LinkDotFindVisitor : public AstNVisitor { private: // STATE LinkDotState* m_statep; // State to pass between visitors, including symbol table + AstPackage* m_packagep; // Current package VSymEnt* m_modSymp; // Symbol Entry for current module - VSymEnt* m_curSymp; // Symbol Entry for current module, possibly a fake inlined one + VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert string m_scope; // Scope text AstBegin* m_beginp; // Current Begin/end block + AstNodeFTask* m_ftaskp; // Current function/task + bool m_inGenerate; // Inside a generate + int m_paramNum; // Parameter number, for position based connection + int m_beginNum; // Begin block number, 0=none seen + int m_modBeginNum; // Begin block number in module, 0=none seen + // METHODS int debug() { return LinkDotState::debug(); } // VISITs @@ -301,6 +439,11 @@ private: //for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) { // if (nodep->castPackage()) {}} + m_statep->insertDUnit(nodep); + + // First back iterate, to find all packages. Backward as must do base packages before using packages + nodep->iterateChildrenBackwards(*this); + // The first module in the list is always the top module (sorted before this is called). // This may not be the module with isTop() set, as early in the steps, // wrapTop may have not been created yet. @@ -318,16 +461,51 @@ private: m_curSymp = m_modSymp = NULL; } } + virtual void visit(AstTypeTable* nodep, AstNUser*) {} virtual void visit(AstNodeModule* nodep, AstNUser*) { // Called on top module from Netlist, other modules from the cell creating them, // and packages UINFO(8," "<forPrearray() && nodep->castPackage()); + bool doit = (m_modSymp || standalonePkg); + string oldscope = m_scope; + VSymEnt* oldModSymp = m_modSymp; + VSymEnt* oldCurSymp = m_curSymp; + if (doit) { + UINFO(2," Link Module: "<dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?"); + VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp(); + m_packagep = nodep->castPackage(); + if (standalonePkg) { + if (m_packagep->isDollarUnit()) { + m_curSymp = m_modSymp = m_statep->dunitEntp(); + nodep->user1p(m_curSymp); + } else { + m_scope = nodep->name(); + m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name()+"::", nodep, m_packagep); + UINFO(9, "New module scope "<iterateChildren(*this); + nodep->user4(true); + } else { //!doit + // Will be optimized away later + // Can't remove now, as our backwards iterator will throw up + UINFO(5, "Module not under any CELL or top - dead module: "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); @@ -389,43 +567,208 @@ private: m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name()); } } + virtual void visit(AstGenerate* nodep, AstNUser*) { + // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks + // See t_gen_forif.v for an example. + bool lastInGen = m_inGenerate; + { + m_inGenerate = true; + nodep->iterateChildren(*this); + } + m_inGenerate = lastInGen; + } virtual void visit(AstBegin* nodep, AstNUser*) { UINFO(5," "<forPrimary() && !nodep->user4SetOnce()) { + if (nodep->name() == "genblk") { + ++m_beginNum; + nodep->name(nodep->name()+cvtToStr(m_beginNum)); + } + // Just for loop index, make special name. The [00] is so it will "dearray" to same + // name as after we expand the GENFOR + if (nodep->genforp()) nodep->name(nodep->name()); + } + // All blocks are numbered in the standard, IE we start with "genblk1" even if only one. + if (nodep->name()=="" && nodep->unnamed()) { + // Unnamed blocks are only important when they contain var + // decls, so search for them. (Otherwise adding all the + // unnamed#'s would just confuse tracing variables in + // places such as tasks, where "task ...; begin ... end" + // are common. + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + if (stmtp->castVar()) { + ++m_modBeginNum; + nodep->name("unnamedblk"+cvtToStr(m_modBeginNum)); + break; + } + } + } + int oldNum = m_beginNum; AstBegin* oldbegin = m_beginp; VSymEnt* oldCurSymp = m_curSymp; { + m_beginNum = 0; m_beginp = nodep; - // We don't pickup variables (as not supported yet), but do need to find cells - m_curSymp = m_statep->insertBegin(m_curSymp, m_modSymp, nodep); - nodep->stmtsp()->iterateAndNext(*this); + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); + m_curSymp->fallbackp(oldCurSymp); + // Iterate + nodep->iterateChildren(*this); } m_curSymp = oldCurSymp; m_beginp = oldbegin; - // - nodep->flatsp()->iterateAndNext(*this); + m_beginNum = oldNum; } virtual void visit(AstNodeFTask* nodep, AstNUser*) { - if (!m_beginp) { // For now, we don't support xrefs into functions inside begin blocks - m_statep->insertSym(m_modSymp, nodep->name(), nodep); + // NodeTask: Remember its name for later resolution + UINFO(5," "<v3fatalSrc("Function/Task not under module??\n"); + // Remember the existing symbol table scope + VSymEnt* oldCurSymp = m_curSymp; + { + // Create symbol table for the task's vars + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); + m_curSymp->fallbackp(oldCurSymp); + // Convert the func's range to the output variable + // This should probably be done in the Parser instead, as then we could + // just attact normal signal attributes to it. + if (nodep->fvarp() + && !nodep->fvarp()->castVar()) { + AstNodeDType* dtypep = nodep->fvarp()->castNodeDType(); + // If unspecified, function returns one bit; however when we support NEW() it could + // also return the class reference. + if (dtypep) dtypep->unlinkFrBack(); + else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); + AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), + VFlagChildDType(), dtypep); // Not dtype resolved yet + newvarp->funcReturn(true); + newvarp->trace(false); // Not user visible + newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); + nodep->addFvarp(newvarp); + // Explicit insert required, as the var name shadows the upper level's task name + m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, NULL/*packagep*/); + } + m_ftaskp = nodep; + nodep->iterateChildren(*this); + m_ftaskp = NULL; } - // No recursion, we don't want to pick up variables + m_curSymp = oldCurSymp; + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + nodep->user1p(m_curSymp); // Remember so Param visitor can easily find the reference point } virtual void visit(AstVar* nodep, AstNUser*) { - if (!m_statep->forScopeCreation() - && !m_beginp // For now, we don't support xrefs into begin blocks - && !nodep->isFuncLocal()) { - m_statep->insertSym(m_modSymp, nodep->name(), nodep); - } else { - UINFO(9," Not allowing dot refs to: "<v3fatalSrc("Var not under module??\n"); + nodep->iterateChildren(*this); + if (!m_statep->forScopeCreation()) { + // Find under either a task or the module's vars + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname? + AstVar* findvarp = foundp->nodep()->castVar(); + bool ins=false; + if (!foundp) { + ins=true; + } else if (!findvarp && foundp && m_curSymp->findIdFlat(nodep->name())) { + m_statep->preErrorDump(); + nodep->v3error("Unsupported in C: Variable has same name as " + <nodep())<<": "<prettyName()); + } else if (findvarp != nodep) { + UINFO(4,"DupVar: "<nodep()<parentp()<parentp() == m_curSymp) { // Only when on same level + if ((findvarp->isIO() && nodep->isSignal()) + || (findvarp->isSignal() && nodep->isIO())) { + findvarp->combineType(nodep); + nodep->fileline()->modifyStateInherit(nodep->fileline()); + AstBasicDType* bdtypep = findvarp->childDTypep()->castBasicDType(); + if (bdtypep && bdtypep->implicit()) { + // Then have "input foo" and "real foo" so the dtype comes from the other side. + AstNodeDType* newdtypep = nodep->subDTypep(); + if (!newdtypep || !nodep->childDTypep()) findvarp->v3fatalSrc("No child type?"); + bdtypep->unlinkFrBack()->deleteTree(); + newdtypep->unlinkFrBack(); + findvarp->childDTypep(newdtypep); + } + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } else { + nodep->v3error("Duplicate declaration of signal: "<prettyName()<warnMore()<<"... Location of original declaration"); + } + } else { + // User can disable the message at either point + if (!(m_ftaskp && m_ftaskp->dpiImport()) + && (!m_ftaskp || m_ftaskp != foundp->nodep()) // Not the function's variable hiding function + && !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) + && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { + nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<name()<nodep()->warnMore()<<"... Location of original declaration"); + } + ins = true; + } + } + if (ins) { + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + if (m_statep->forPrimary() && nodep->isGParam()) { + m_paramNum++; + m_statep->insertSym(m_curSymp, "__paramNumber"+cvtToStr(m_paramNum), nodep, m_packagep); + } + } } } + virtual void visit(AstTypedef* nodep, AstNUser*) { + // Remember its name for later resolution + if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module??\n"); + nodep->iterateChildren(*this); + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + } virtual void visit(AstCFunc* nodep, AstNUser*) { - // Ignore all AstVars under functions + // For dotted resolution, ignore all AstVars under functions, otherwise shouldn't exist + if (m_statep->forScopeCreation()) nodep->v3fatalSrc("No CFuncs expected in tree yet"); + } + virtual void visit(AstEnumItem* nodep, AstNUser*) { + // EnumItem: Remember its name for later resolution + nodep->iterateChildren(*this); + // Find under either a task or the module's vars + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname? + AstEnumItem* findvarp = foundp->nodep()->castEnumItem(); + bool ins=false; + if (!foundp) { + ins=true; + } else if (findvarp != nodep) { + UINFO(4,"DupVar: "<parentp() == m_curSymp) { // Only when on same level + nodep->v3error("Duplicate declaration of enum value: "<prettyName()<warnMore()<<"... Location of original declaration"); + } else { + // User can disable the message at either point + if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) + && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { + nodep->v3warn(VARHIDDEN,"Declaration of enum value hides declaration in upper scope: "<name()<nodep()->warnMore()<<"... Location of original declaration"); + } + ins = true; + } + } + if (ins) { + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + } + } + virtual void visit(AstPackageImport* nodep, AstNUser*) { + UINFO(2," Link: "<getNodeSym(nodep->packagep()); + if (nodep->name()!="*") { + VSymEnt* impp = srcp->findIdFlat(nodep->name()); + if (!impp) { + nodep->v3error("Import object not found: "<packagep()->prettyName()<<"::"<prettyName()); + } + } + m_curSymp->import(srcp, nodep->name()); + // No longer needed + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } - // For speed, don't recurse things that can't have cells - // Note we allow AstNodeStmt's as generates may be under them - virtual void visit(AstNodeMath*, AstNUser*) {} virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); @@ -435,15 +778,172 @@ public: // CONSTUCTORS LinkDotFindVisitor(AstNetlist* rootp, LinkDotState* statep) { UINFO(4,__FUNCTION__<<": "<accept(*this); } virtual ~LinkDotFindVisitor() {} }; +//====================================================================== + +class LinkDotParamVisitor : public AstNVisitor { +private: + // NODE STATE + // Cleared on global + // *:user1p() -> See LinkDotState + // *:user2p() -> See LinkDotState + // *:user4() -> See LinkDotState + + // STATE + LinkDotState* m_statep; // State to pass between visitors, including symbol table + AstNodeModule* m_modp; // Current module + + int debug() { return LinkDotState::debug(); } + + void pinImplicitExprRecurse(AstNode* nodep) { + // Under a pin, Check interconnect expression for a pin reference or a concat. + // Create implicit variable as needed + if (AstVarRef* varrefp = nodep->castVarRef()) { + // 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. + //if (!nodep->modVarp()->rangep()) ... + // Note FindVisitor::visit AstVarRef stashes user1p specifically for this getNodeSym + m_statep->createImplicitVar(m_statep->getNodeSym(nodep), varrefp, m_modp, m_statep->getNodeSym(m_modp), false); + } + // These are perhaps a little too generous, as a SELect of siga[sigb] + // perhaps shouldn't create an implicit variable. But, we've warned... + else { + if (nodep->op1p()) pinImplicitExprRecurse(nodep->op1p()); + if (nodep->op2p()) pinImplicitExprRecurse(nodep->op2p()); + if (nodep->op3p()) pinImplicitExprRecurse(nodep->op3p()); + if (nodep->op4p()) pinImplicitExprRecurse(nodep->op4p()); + if (nodep->nextp()) pinImplicitExprRecurse(nodep->nextp()); + } + } + + // VISITs + virtual void visit(AstTypeTable* nodep, AstNUser*) {} + virtual void visit(AstNodeModule* nodep, AstNUser*) { + UINFO(5," "<dead() || !nodep->user4()) { + UINFO(4,"Mark dead module "<forPrearray()) nodep->v3fatalSrc("Dead module persisted past where should have removed"); + // Don't remove now, because we may have a tree of parameterized modules with VARXREFs into the deleted module region + // V3Dead should cleanup. + // Downstream visitors up until V3Dead need to check for nodep->dead. + nodep->dead(true); + } else { + m_modp = nodep; + nodep->iterateChildren(*this); + m_modp = NULL; + } + } + virtual void visit(AstVar* nodep, AstNUser*) { + // We used modTrace before leveling, and we may now + // want to turn it off now that we know the levelizations + if (v3Global.opt.traceDepth() + && (m_modp->level()-1) > v3Global.opt.traceDepth()) { + m_modp->modTrace(false); + nodep->trace(false); + } + nodep->iterateChildren(*this); + } + virtual void visit(AstPin* nodep, AstNUser*) { + // Pin: Link to submodule's port + // Deal with implicit definitions - do before Resolve visitor as may be referenced above declaration + if (nodep->exprp() // Else specifically unconnected pin + && !nodep->svImplicit()) { // SV 19.11.3: .name pins don't allow implicit decls + pinImplicitExprRecurse(nodep->exprp()); + } + } + virtual void visit(AstDefParam* nodep, AstNUser*) { + nodep->iterateChildren(*this); + nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<name()<<"(...etc...))"); + VSymEnt* foundp = m_statep->getNodeSym(m_modp)->findIdFallback(nodep->path()); + AstCell* cellp = foundp->nodep()->castCell(); + if (!cellp) { + nodep->v3error("In defparam, cell "<path()<<" never declared"); + } else { + AstNode* exprp = nodep->rhsp()->unlinkFrBack(); + UINFO(9,"Defparam cell "<path()<<"."<name() + <<" <= "<fileline(), + -1, // Pin# not relevant + nodep->name(), + exprp); + cellp->addParamsp(pinp); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + } + virtual void visit(AstPort* nodep, AstNUser*) { + // Port: Stash the pin number + // Need to set pin numbers after varnames are created + // But before we do the final resolution based on names + VSymEnt* foundp = m_statep->getNodeSym(m_modp)->findIdFlat(nodep->name()); + AstVar* refp = foundp->nodep()->castVar(); + if (!refp) { + nodep->v3error("Input/output/inout declaration not found for port: "<prettyName()); + } else if (!refp->isIO()) { + nodep->v3error("Pin is not an in/out/inout: "<prettyName()); + } else { + refp->user4(true); + m_statep->insertSym(m_statep->getNodeSym(m_modp), "__pinNumber"+cvtToStr(nodep->pinNum()), refp, NULL/*packagep*/); + } + // Ports not needed any more + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + // Deal with implicit definitions + // We used to nodep->allowImplicit() here, but it turns out + // normal "assigns" can also make implicit wires. Yuk. + pinImplicitExprRecurse(nodep->lhsp()); + nodep->iterateChildren(*this); + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + // tran gates need implicit creation + if (AstVarRef* forrefp = nodep->lhsp()->castVarRef()) { + pinImplicitExprRecurse(forrefp); + } + if (AstVarRef* forrefp = nodep->rhsp()->castVarRef()) { + pinImplicitExprRecurse(forrefp); + } + nodep->iterateChildren(*this); + } + virtual void visit(AstImplicit* nodep, AstNUser*) { + // Unsupported gates need implicit creation + pinImplicitExprRecurse(nodep); + // We're done with implicit gates + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkDotParamVisitor(AstNetlist* rootp, LinkDotState* statep) { + UINFO(4,__FUNCTION__<<": "<accept(*this); + } + virtual ~LinkDotParamVisitor() {} +}; + + //====================================================================== class LinkDotScopeVisitor : public AstNVisitor { @@ -455,6 +955,10 @@ private: int debug() { return LinkDotState::debug(); } // VISITs + virtual void visit(AstNetlist* nodep, AstNUser* vup) { + // Recurse..., backward as must do packages before using packages + nodep->iterateChildrenBackwards(*this); + } virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," SCOPE "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); @@ -466,11 +970,12 @@ private: } virtual void visit(AstVarScope* nodep, AstNUser*) { if (!nodep->varp()->isFuncLocal()) { - m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep); + m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL); } } virtual void visit(AstNodeFTask* nodep, AstNUser*) { - m_statep->insertSym(m_modSymp, nodep->name(), nodep); + VSymEnt* symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, NULL); + symp->fallbackp(m_modSymp); // No recursion, we don't want to pick up variables } virtual void visit(AstAssignAlias* nodep, AstNUser*) { @@ -511,37 +1016,122 @@ public: class LinkDotResolveVisitor : public AstNVisitor { private: + // NODE STATE + // Cleared on global + // *:user1p() -> See LinkDotState + // *:user2p() -> See LinkDotState + // *:user4() -> See LinkDotState + // Cleared on Cell + // AstVar::user5() // bool True if pin used in this cell + AstUser5InUse m_inuser5; + // STATE LinkDotState* m_statep; // State, including dotted symbol table + VSymEnt* m_curSymp; // SymEnt for current lookup point VSymEnt* m_modSymp; // SymEnt for current module + VSymEnt* m_pinSymp; // SymEnt for pin lookups + AstCell* m_cellp; // Current cell + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Current function/task int debug() { return LinkDotState::debug(); } - // METHODS - // VISITs + virtual void visit(AstNetlist* nodep, AstNUser* vup) { + // Recurse..., backward as must do packages before using packages + nodep->iterateChildrenBackwards(*this); + } + virtual void visit(AstTypeTable* nodep, AstNUser*) {} virtual void visit(AstNodeModule* nodep, AstNUser*) { + if (nodep->dead()) return; UINFO(8," "<existsModScope(nodep)) { - UINFO(5,"Dead module for "<getNodeSym(nodep); - } + m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); // Until overridden by a SCOPE + m_cellp = NULL; + m_modp = nodep; nodep->iterateChildren(*this); - m_modSymp = NULL; + m_modp = NULL; + m_curSymp = m_modSymp = NULL; } virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," "<getScopeSym(nodep); + m_curSymp = m_statep->getScopeSym(nodep); nodep->iterateChildren(*this); - m_modSymp = NULL; + m_curSymp = NULL; } virtual void visit(AstCellInline* nodep, AstNUser*) { if (m_statep->forScopeCreation()) { nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } } + virtual void visit(AstCell* nodep, AstNUser*) { + // Cell: Resolve its filename. If necessary, parse it. + m_cellp = nodep; + AstNode::user5ClearTree(); + if (!nodep->modp()) { + nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out + } + else { + if (nodep->modp()->castNotFoundModule()) { + // Prevent warnings about missing pin connects + if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree(); + if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); + } + // Need to pass the module info to this cell, so we can link up the pin names + // However can't use m_curSymp as pin connections need to use the instantiator's symbols + else { + m_pinSymp = m_statep->getNodeSym(nodep->modp()); + UINFO(4,"(Backto) Link Cell: "<dumpTree(cout,"linkcell:"); } + //if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); } + nodep->iterateChildren(*this); + m_pinSymp = NULL; + } + } + m_cellp = NULL; + // Parent module inherits child's publicity + // This is done bottom up in the LinkBotupVisitor stage + } + virtual void visit(AstPin* nodep, AstNUser*) { + // Pin: Link to submodule's port + if (!nodep->modVarp()) { + if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?\n"); + VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name()); + AstVar* refp = foundp->nodep()->castVar(); + if (!refp) { + if (nodep->name() == "__paramNumber1" && m_cellp->modp()->castPrimitive()) { + // Primitive parameter is really a delay we can just ignore + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + return; + } + nodep->v3error("Pin not found: "<prettyName()); + } else if (!refp->isIO() && !refp->isParam()) { + nodep->v3error("Pin is not an in/out/inout/param: "<prettyName()); + } else { + nodep->modVarp(refp); + if (refp->user5p() && refp->user5p()->castNode()!=nodep) { + nodep->v3error("Duplicate pin connection: "<prettyName()<user5p()->castNode()->warnMore() + <<"... Location of original pin connection"); + } else { + refp->user5p(nodep); + } + } + nodep->iterateChildren(*this); + } + // Early return() above when deleted + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + // VarRef: Resolve its reference + nodep->iterateChildren(*this); + if (!nodep->varp()) { + if (m_statep->linkVarName(m_curSymp, nodep)) { nodep=NULL; return; } + if (!nodep->varp()) { + m_statep->preErrorDump(); + nodep->v3error("Can't find definition of signal: "<prettyName()); + m_statep->createImplicitVar (m_curSymp, nodep, m_modp, m_modSymp, true); // So only complain once + } + } + } virtual void visit(AstVarXRef* nodep, AstNUser*) { // VarRef: Resolve its reference // We always link even if varp() is set, because the module we choose may change @@ -553,7 +1143,7 @@ private: } else { string baddot; VSymEnt* okSymp; - VSymEnt* dotSymp = m_modSymp; // Start search at current scope + VSymEnt* dotSymp = m_curSymp; // Start search at current scope if (nodep->inlinedDots()!="") { // Correct for current scope string inl = AstNode::dedotName(nodep->inlinedDots()); dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp); @@ -564,8 +1154,8 @@ private: } dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL if (!m_statep->forScopeCreation()) { - AstVar* varp = (m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot) - ->castVar()); // maybe NULL + VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); + AstVar* varp = foundp->nodep()->castVar(); // maybe NULL nodep->varp(varp); UINFO(7," Resolved "<varp()) { @@ -575,8 +1165,8 @@ private: } } else { string baddot; - AstVarScope* vscp = (m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot) - ->castVarScope()); // maybe NULL + VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); + AstVarScope* vscp = foundp->nodep()->castVarScope(); // maybe NULL if (!vscp) { m_statep->preErrorDump(); nodep->v3error("Can't find varpin scope of '"<dotted()+"."+nodep->prettyName()); @@ -597,9 +1187,15 @@ private: } } } + virtual void visit(AstVar* nodep, AstNUser*) { + 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()); + } + } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { UINFO(8," "<packagep()) { + if (nodep->packagep() && nodep->taskp()) { // References into packages don't care about cell hierarchy. } else if (!m_modSymp) { UINFO(9,"Dead module for "<inlinedDots()!="") { // Correct for current scope - string inl = AstNode::dedotName(nodep->inlinedDots()); - UINFO(8,"\t\tInlined "<findDotted(dotSymp, inl, baddot, okSymp); - if (!dotSymp) { - m_statep->preErrorDump(); + VSymEnt* okSymp = NULL; + VSymEnt* dotSymp = m_curSymp; // Start search at module, as a variable of same name under a subtask isn't a relevant hit + // // however a function under a begin/end is. So we want begins, but not the function + if (nodep->packagep()) { // Look only in specified package + dotSymp = m_statep->getNodeSym(nodep->packagep()); + } else { + if (nodep->inlinedDots()!="") { // Correct for current scope + dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw) + string inl = AstNode::dedotName(nodep->inlinedDots()); + UINFO(8,"\t\tInlined "<findDotted(dotSymp, inl, baddot, okSymp); + if (!dotSymp) { + m_statep->preErrorDump(); + okSymp->cellErrorScopes(nodep); + nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); + } + } + dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL + } + VSymEnt* foundp = NULL; + AstNodeFTask* taskp = NULL; + foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); + taskp = foundp->nodep()->castNodeFTask(); // Maybe NULL + if (taskp) { + nodep->taskp(taskp); + nodep->packagep(foundp->packagep()); + UINFO(7," Resolved "<preErrorDump(); + if (nodep->dotted() == "") { + nodep->v3error("Can't find definition of task/function: "<prettyName()); + } else { + nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); okSymp->cellErrorScopes(nodep); - nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); } } - dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL - - AstNodeFTask* taskp = (m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot) - ->castNode()->castNodeFTask()); // maybe NULL - nodep->taskp(taskp); - UINFO(7," Resolved "<taskp()) { - m_statep->preErrorDump(); - nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); - okSymp->cellErrorScopes(nodep); + if (taskp->castTask() && nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<prettyName()); + } + nodep->iterateChildren(*this); + } + virtual void visit(AstBegin* nodep, AstNUser*) { + UINFO(5," "<getNodeSym(nodep); + UINFO(5," cur=se"<<(void*)m_curSymp<iterateChildren(*this); + } + m_curSymp = oldCurSymp; + UINFO(5," cur=se"<<(void*)m_curSymp<getNodeSym(nodep); + nodep->iterateChildren(*this); + } + m_curSymp = oldCurSymp; + m_ftaskp = NULL; + } + virtual void visit(AstRefDType* nodep, AstNUser*) { + // Resolve its reference + if (!nodep->defp()) { + VSymEnt* foundp; + if (nodep->packagep()) { + foundp = m_statep->getNodeSym(nodep->packagep())->findIdFlat(nodep->name()); + } else { + foundp = m_curSymp->findIdFallback(nodep->name()); + } + if (AstTypedef* defp = foundp->nodep()->castTypedef()) { + nodep->refDTypep(defp->subDTypep()); + nodep->packagep(foundp->packagep()); + } else { + nodep->v3error("Can't find typedef: "<prettyName()); } } nodep->iterateChildren(*this); } + virtual void visit(AstDpiExport* nodep, AstNUser*) { + // AstDpiExport: Make sure the function referenced exists, then dump it + nodep->iterateChildren(*this); + 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()); } + else if (taskp->dpiExport()) { + nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<prettyName()); + } else { + taskp->dpiExport(true); + if (nodep->cname()!="") taskp->cname(nodep->cname()); + } + nodep->unlinkFrBack()->deleteTree(); + } virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); @@ -645,6 +1309,11 @@ public: UINFO(4,__FUNCTION__<<": "<accept(*this); } @@ -657,12 +1326,16 @@ public: int V3LinkDot::debug() { return LinkDotState::debug(); } void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { - UINFO(2,__FUNCTION__<<": "<=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); LinkDotState state (rootp, step); LinkDotFindVisitor visitor(rootp,&state); if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); - if (step == LDS_SCOPED) { + if (step == LDS_PRIMARY || step == LDS_PARAMED) { + // Initial link stage, resolve parameters + LinkDotParamVisitor visitors(rootp,&state); + if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); + } + else if (step == LDS_SCOPED) { // Well after the initial link when we're ready to operate on the flat design, // process AstScope's. This needs to be separate pass after whole hierarchy graph created. LinkDotScopeVisitor visitors(rootp,&state); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 8858fdc29..5577fff2e 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -114,6 +114,7 @@ private: // VISITORS virtual void visit(AstNodeModule* nodep, AstNUser*) { + if (nodep->dead()) return; m_modp = nodep; m_repeatNum = 0; nodep->iterateChildren(*this); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index c1758d17c..cab43bd23 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -66,9 +66,13 @@ private: } // VISITs + // TODO: Most of these visitors are here for historical reasons. + // TODO: ExpectDecriptor can move to data type resolution, and the rest + // TODO: could move to V3LinkParse to get them out of the way of elaboration virtual void visit(AstNodeModule* nodep, AstNUser*) { // Module: Create sim table for entire module and iterate UINFO(8,"MODULE "<dead()) return; m_modp = nodep; m_senitemCvtNum = 0; nodep->iterateChildren(*this); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index a6a7dffd7..f2b2562d7 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -54,6 +54,7 @@ class ParamVisitor : public AstNVisitor { private: // NODE STATE // AstNodeModule::user5() // bool True if processed + // AstGenFor::user5() // bool True if processed // AstVar::user5() // bool True if constant propagated // AstVar::user4() // int Global parameter number (for naming new module) // // (0=not processed, 1=iterated, but no number, 65+ parameter numbered) @@ -142,7 +143,9 @@ private: nodep->iterateChildren(*this); } virtual void visit(AstNodeModule* nodep, AstNUser*) { - if (nodep->level() <= 2) { // Haven't added top yet, so level 2 is the top + if (nodep->dead()) { + UINFO(4," MOD-dead. "<level() <= 2) { // Haven't added top yet, so level 2 is the top // Add request to END of modules left to process m_todoModps.push_back(nodep); visitModules(); @@ -214,13 +217,36 @@ private: //! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional //! expression, since this is currently restricted to simple comparisons. If we ever do //! move to more generic constant expressions, such code will be neede here. + virtual void visit(AstBegin* nodep, AstNUser*) { + if (nodep->genforp()) { + AstGenFor* forp = nodep->genforp()->castGenFor(); + if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN"); + // We should have a GENFOR under here. We will be replacing the begin, + // so process here rather than at the generate to avoid iteration problems + UINFO(9," BEGIN "<name(); + // Leave the original Begin, as need a container for the (possible) GENVAR + // Note V3Unroll will replace some AstVarRef's to the loop variable with constants + V3Unroll::unrollGen(forp, beginName); forp=NULL; + // Blocks were constructed under the special begin, move them up + // Note forp is null, so grab statements again + if (AstNode* stmtsp = nodep->genforp()) { + stmtsp->unlinkFrBackWithNext(); + nodep->addNextHere(stmtsp); + // Note this clears nodep->genforp(), so begin is no longer special + } + } else { + nodep->iterateChildren(*this); + } + } virtual void visit(AstGenFor* nodep, AstNUser*) { - // We parse a very limited form of FOR, so we don't need to do a full - // simulation to unroll the loop - UINFO(9," GENFOR "<v3fatalSrc("GENFOR should have been wrapped in BEGIN"); } virtual void visit(AstGenCase* nodep, AstNUser*) { UINFO(9," GENCASE "<stmtsp()->iterateAndNext(*this); m_varModeReplace = false; - tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); tempp=NULL; + oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); tempp=NULL; + } + if (m_generate) { + string index = AstNode::encodeNumber(m_varValuep->toSInt()); + string nname = m_beginName + "__BRA__" + index + "__KET__"; + oneloopp = new AstBegin(oneloopp->fileline(),nname,oneloopp,true); } if (newbodysp) newbodysp->addNext(oneloopp); @@ -379,29 +384,6 @@ private: } } - virtual void visit(AstBegin* nodep, AstNUser*) { - // Naming inside loop body; must have been a generate for. - // We need to only rename the 'upper' begin, - // anything lower will be renamed "uppernewname.lowerbegin" - bool lastBegin = m_inBegin; - m_inBegin = true; - nodep->stmtsp()->iterateAndNext(*this); - m_inBegin = lastBegin; - nodep->flatsp()->iterateAndNext(*this); - - if (m_varModeReplace && !m_inBegin // no upper begin, excluding this one - ) { - // Rename it, as otherwise we may get a conflict - // V3Begin sees these DOTs and makes CellInlines for us. - string index = AstNode::encodeNumber(m_varValuep->toSInt()); - string nname = (string)"genfor"+index+"__DOT__"+nodep->name(); - // Verilog seems to drop the for loop name and tack on [#] - nname = nodep->name() + "__BRA__" + index + "__KET__"; - //UINFO(8," Rename begin "<name(nname); - } - } - virtual void visit(AstVarRef* nodep, AstNUser*) { if (m_varModeCheck && nodep->varp() == m_forVarp @@ -433,14 +415,14 @@ private: public: // CONSTUCTORS - UnrollVisitor(AstNode* nodep, bool generate) { + UnrollVisitor(AstNode* nodep, bool generate, string beginName) { m_forVarp = NULL; m_forVscp = NULL; m_ignoreIncp = NULL; m_varModeCheck = false; m_varModeReplace = false; - m_inBegin = false; m_generate = generate; + m_beginName = beginName; // nodep->accept(*this); } @@ -455,10 +437,10 @@ public: void V3Unroll::unrollAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<: // ==IEEE: generate_region generate_block_or_null: // IEEE: generate_block_or_null // ';' // is included in // // IEEE: generate_block + // // Must always return a BEGIN node, or NULL - see GenFor construction generate_item { $$ = $1 ? (new AstBegin($1->fileline(),"genblk",$1,true)) : NULL; } | genItemBegin { $$ = $1; } ; @@ -1500,17 +1501,27 @@ conditional_generate_construct: // ==IEEE: conditional_generate_construct loop_generate_construct: // ==IEEE: loop_generate_construct yFOR '(' genvar_initialization ';' expr ';' genvar_iteration ')' generate_block_or_null - { AstBegin* blkp = new AstBegin($1,"",NULL,true); blkp->hidden(true); + { // Convert BEGIN(...) to BEGIN(GENFOR(...)), as we need the BEGIN to hide the local genvar + AstBegin* lowerBegp = $9->castBegin(); + if ($9 && !lowerBegp) $9->v3fatalSrc("Child of GENFOR should have been begin"); + if (!lowerBegp) lowerBegp = new AstBegin($1,"genblk",NULL,true); // Empty body + AstNode* lowerNoBegp = lowerBegp->stmtsp(); + if (lowerNoBegp) lowerNoBegp->unlinkFrBackWithNext(); + // + AstBegin* blkp = new AstBegin($1,lowerBegp->name(),NULL,true); + // V3LinkDot detects BEGIN(GENFOR(...)) as a special case AstNode* initp = $3; AstNode* varp = $3; if (varp->castVar()) { // Genvar initp = varp->nextp(); initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init blkp->addStmtsp(varp); } - // Statements are under 'flatsp' so that cells under this + // Statements are under 'genforp' as cells under this // for loop won't get an extra layer of hierarchy tacked on - blkp->addFlatsp(new AstGenFor($1,initp,$5,$7,$9)); - $$ = blkp; } + blkp->addGenforp(new AstGenFor($1,initp,$5,$7,lowerNoBegp)); + $$ = blkp; + lowerBegp->deleteTree(); lowerBegp=NULL; + } ; genvar_initialization: // ==IEEE: genvar_initalization