mirror of
https://github.com/verilator/verilator.git
synced 2025-05-04 14:36:55 +00:00
Internals: MAJOR CHANGE. Combine V3Link and V3LinkDot stages for structures.
Functionality should be similar, but may introduce instability in resolving variables/cells. Final merge from dot.
This commit is contained in:
parent
a2f49063e2
commit
8b9b7178a2
@ -198,7 +198,6 @@ RAW_OBJS = \
|
|||||||
V3Inst.o \
|
V3Inst.o \
|
||||||
V3Life.o \
|
V3Life.o \
|
||||||
V3LifePost.o \
|
V3LifePost.o \
|
||||||
V3Link.o \
|
|
||||||
V3LinkCells.o \
|
V3LinkCells.o \
|
||||||
V3LinkDot.o \
|
V3LinkDot.o \
|
||||||
V3LinkJump.o \
|
V3LinkJump.o \
|
||||||
|
@ -1646,13 +1646,14 @@ private:
|
|||||||
bool m_modPublic:1; // Module has public references
|
bool m_modPublic:1; // Module has public references
|
||||||
bool m_modTrace:1; // Tracing this module
|
bool m_modTrace:1; // Tracing this module
|
||||||
bool m_inLibrary:1; // From a library, no error if not used, never top level
|
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_level; // 1=top module, 2=cell off top module, ...
|
||||||
int m_varNum; // Incrementing variable number
|
int m_varNum; // Incrementing variable number
|
||||||
public:
|
public:
|
||||||
AstNodeModule(FileLine* fl, const string& name)
|
AstNodeModule(FileLine* fl, const string& name)
|
||||||
: AstNode (fl)
|
: AstNode (fl)
|
||||||
,m_name(name), m_origName(name)
|
,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) { }
|
,m_level(0), m_varNum(0) { }
|
||||||
ASTNODE_BASE_FUNCS(NodeModule)
|
ASTNODE_BASE_FUNCS(NodeModule)
|
||||||
virtual void dump(ostream& str);
|
virtual void dump(ostream& str);
|
||||||
@ -1677,6 +1678,8 @@ public:
|
|||||||
bool modPublic() const { return m_modPublic; }
|
bool modPublic() const { return m_modPublic; }
|
||||||
void modTrace(bool flag) { m_modTrace = flag; }
|
void modTrace(bool flag) { m_modTrace = flag; }
|
||||||
bool modTrace() const { return m_modTrace; }
|
bool modTrace() const { return m_modTrace; }
|
||||||
|
void dead(bool flag) { m_dead = flag; }
|
||||||
|
bool dead() const { return m_dead; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
@ -702,6 +702,7 @@ void AstNodeModule::dump(ostream& str) {
|
|||||||
str<<" L"<<level();
|
str<<" L"<<level();
|
||||||
if (modPublic()) str<<" [P]";
|
if (modPublic()) str<<" [P]";
|
||||||
if (inLibrary()) str<<" [LIB]";
|
if (inLibrary()) str<<" [LIB]";
|
||||||
|
if (dead()) str<<" [DEAD]";
|
||||||
}
|
}
|
||||||
void AstPackageImport::dump(ostream& str) {
|
void AstPackageImport::dump(ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
@ -831,8 +832,8 @@ void AstNodeFTask::dump(ostream& str) {
|
|||||||
void AstBegin::dump(ostream& str) {
|
void AstBegin::dump(ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (unnamed()) str<<" [UNNAMED]";
|
if (unnamed()) str<<" [UNNAMED]";
|
||||||
if (hidden()) str<<" [HIDDEN]";
|
|
||||||
if (generate()) str<<" [GEN]";
|
if (generate()) str<<" [GEN]";
|
||||||
|
if (genforp()) str<<" [GENFOR]";
|
||||||
}
|
}
|
||||||
void AstCoverDecl::dump(ostream& str) {
|
void AstCoverDecl::dump(ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
|
@ -2344,7 +2344,6 @@ struct AstBegin : public AstNode {
|
|||||||
private:
|
private:
|
||||||
string m_name; // Name of block
|
string m_name; // Name of block
|
||||||
bool m_unnamed; // Originally unnamed
|
bool m_unnamed; // Originally unnamed
|
||||||
bool m_hidden; // Inserted by verilator, not user
|
|
||||||
bool m_generate; // Underneath a generate
|
bool m_generate; // Underneath a generate
|
||||||
public:
|
public:
|
||||||
// Node that simply puts name into the output stream
|
// Node that simply puts name into the output stream
|
||||||
@ -2353,7 +2352,6 @@ public:
|
|||||||
, m_name(name) {
|
, m_name(name) {
|
||||||
addNOp1p(stmtsp);
|
addNOp1p(stmtsp);
|
||||||
m_unnamed = (name=="");
|
m_unnamed = (name=="");
|
||||||
m_hidden = false;
|
|
||||||
m_generate = generate;
|
m_generate = generate;
|
||||||
}
|
}
|
||||||
ASTNODE_NODE_FUNCS(Begin, BEGIN)
|
ASTNODE_NODE_FUNCS(Begin, BEGIN)
|
||||||
@ -2363,11 +2361,10 @@ public:
|
|||||||
// op1 = Statements
|
// 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); }
|
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
|
||||||
AstNode* flatsp() const { return op2p()->castNode(); } // op2 = Statements that don't appear under new scope
|
AstNode* genforp() const { return op2p(); } // op2 = GENFOR, if applicable,
|
||||||
void addFlatsp(AstNode* nodep) { addNOp2p(nodep); }
|
// might NOT be a GenFor, as loop unrolling replaces with Begin
|
||||||
|
void addGenforp(AstGenFor* nodep) { addOp2p(nodep); }
|
||||||
bool unnamed() const { return m_unnamed; }
|
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; }
|
void generate(bool flag) { m_generate = flag; }
|
||||||
bool generate() const { return m_generate; }
|
bool generate() const { return m_generate; }
|
||||||
};
|
};
|
||||||
|
@ -142,24 +142,17 @@ private:
|
|||||||
|
|
||||||
// Remap var names and replace lower Begins
|
// Remap var names and replace lower Begins
|
||||||
nodep->stmtsp()->iterateAndNext(*this);
|
nodep->stmtsp()->iterateAndNext(*this);
|
||||||
|
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||||
}
|
}
|
||||||
m_namedScope = oldScope;
|
m_namedScope = oldScope;
|
||||||
m_unnamedScope = oldUnnamed;
|
m_unnamedScope = oldUnnamed;
|
||||||
|
|
||||||
// Don't change var names of generate FOR() variables, but do recurse into a child FOR
|
|
||||||
nodep->flatsp()->iterateAndNext(*this);
|
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
AstNode* addsp = NULL;
|
AstNode* addsp = NULL;
|
||||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||||
stmtsp->unlinkFrBackWithNext();
|
stmtsp->unlinkFrBackWithNext();
|
||||||
addsp = addsp->addNextNull(stmtsp);
|
addsp = addsp->addNextNull(stmtsp);
|
||||||
}
|
}
|
||||||
if (AstNode* stmtsp = nodep->flatsp()) {
|
|
||||||
stmtsp->unlinkFrBackWithNext();
|
|
||||||
addsp = addsp->addNextNull(stmtsp);
|
|
||||||
}
|
|
||||||
if (addsp) {
|
if (addsp) {
|
||||||
nodep->replaceWith(addsp);
|
nodep->replaceWith(addsp);
|
||||||
} else {
|
} else {
|
||||||
|
725
src/V3Link.cpp
725
src/V3Link.cpp
@ -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 <cstdio>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cctype>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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 "<<nodeTextType(foundp)<<": "<<nodep->prettyName()<<endl
|
|
||||||
<<foundp->warnMore()<<"... Location of original declaration");
|
|
||||||
} else {
|
|
||||||
nodep->v3error("Unsupported in C: "<<ucfirst(nodeTextType(nodep))<<" has the same name as "
|
|
||||||
<<nodeTextType(foundp)<<": "<<nodep->prettyName()<<endl
|
|
||||||
<<foundp->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: "<<forrefp->prettyName());
|
|
||||||
} else {
|
|
||||||
forrefp->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<<forrefp->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: "<<nodep<<endl);
|
|
||||||
AstCell* upperCellp = m_cellp;
|
|
||||||
VSymEnt* upperVarsp = m_curVarsp;
|
|
||||||
{
|
|
||||||
m_modp = nodep;
|
|
||||||
if (!m_curVarsp) nodep->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 "<<m_curVarsp<<endl);
|
|
||||||
}
|
|
||||||
// This state must be save/restored in the cell visitor function
|
|
||||||
m_cellp = NULL;
|
|
||||||
m_cellVarsp = NULL;
|
|
||||||
m_paramNum = 0;
|
|
||||||
m_beginNum = 0;
|
|
||||||
m_modBeginNum = 0;
|
|
||||||
nodep->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 "
|
|
||||||
<<nodeTextType(foundp)<<": "<<nodep->prettyName());
|
|
||||||
} else if (findvarp != nodep) {
|
|
||||||
UINFO(4,"DupVar: "<<nodep<<" ;; "<<foundp<<endl);
|
|
||||||
if (findvarp && findvarp->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: "<<nodep->prettyName()<<endl
|
|
||||||
<<findvarp->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: "<<nodep->name()<<endl
|
|
||||||
<<foundp->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: "<<nodep->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: "<<nodep->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: "<<nodep<<" ;; "<<foundp<<endl);
|
|
||||||
if (findvarp && findvarp->user3p()->castSymEnt() == m_curVarsp) { // Only when on same level
|
|
||||||
nodep->v3error("Duplicate declaration of enum value: "<<nodep->prettyName()<<endl
|
|
||||||
<<findvarp->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: "<<nodep->name()<<endl
|
|
||||||
<<foundp->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," "<<nodep<<endl);
|
|
||||||
if (m_idState==ID_FIND && nodep->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: "<<nodep->prettyName()); }
|
|
||||||
nodep->taskp(taskp);
|
|
||||||
nodep->packagep(packageFor(taskp));
|
|
||||||
if (taskp->castTask() && nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<<nodep->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: "<<nodep->prettyName()); }
|
|
||||||
else if (taskp->dpiExport()) {
|
|
||||||
nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<<nodep->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: "<<nodep->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: "<<nodep<<endl);
|
|
||||||
//if (debug()) { nodep->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: "<<nodep->prettyName());
|
|
||||||
} else if (!refp->isIO()) {
|
|
||||||
nodep->v3error("Pin is not an in/out/inout: "<<nodep->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: "<<nodep->prettyName());
|
|
||||||
} else if (!refp->isIO() && !refp->isParam()) {
|
|
||||||
nodep->v3error("Pin is not an in/out/inout/param: "<<nodep->prettyName());
|
|
||||||
} else {
|
|
||||||
nodep->modVarp(refp);
|
|
||||||
if (refp->user5p() && refp->user5p()->castNode()!=nodep) {
|
|
||||||
nodep->v3error("Duplicate pin connection: "<<nodep->prettyName()<<endl
|
|
||||||
<<refp->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 #(."<<nodep->name()<<"(...etc...))");
|
|
||||||
AstNode* foundp = m_curVarsp->findIdFallback(nodep->path())->nodep();
|
|
||||||
AstCell* cellp = foundp->castCell();
|
|
||||||
if (!cellp) {
|
|
||||||
nodep->v3error("In defparam, cell "<<nodep->path()<<" never declared");
|
|
||||||
} else {
|
|
||||||
AstNode* exprp = nodep->rhsp()->unlinkFrBack();
|
|
||||||
UINFO(9,"Defparam cell "<<nodep->path()<<"."<<nodep->name()
|
|
||||||
<<" <= "<<exprp<<endl);
|
|
||||||
// Don't need to check the name of the defparam exists. V3Param does.
|
|
||||||
AstPin* pinp = new AstPin (nodep->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: "<<nodep<<endl);
|
|
||||||
VSymEnt* srcp = symsFind(nodep->packagep());
|
|
||||||
if (nodep->name()!="*") {
|
|
||||||
VSymEnt* impp = srcp->findIdFlat(nodep->name());
|
|
||||||
if (!impp) {
|
|
||||||
nodep->v3error("Import object not found: "<<nodep->packagep()->prettyName()<<"::"<<nodep->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__<<": "<<endl);
|
|
||||||
LinkVisitor visitor(rootp);
|
|
||||||
}
|
|
35
src/V3Link.h
35
src/V3Link.h
@ -1,35 +0,0 @@
|
|||||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
||||||
//*************************************************************************
|
|
||||||
// DESCRIPTION: Verilator: Link modules/signals together
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//*************************************************************************
|
|
||||||
|
|
||||||
#ifndef _V3LINK_H_
|
|
||||||
#define _V3LINK_H_ 1
|
|
||||||
#include "config_build.h"
|
|
||||||
#include "verilatedos.h"
|
|
||||||
#include "V3Error.h"
|
|
||||||
#include "V3Ast.h"
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
class V3Link {
|
|
||||||
public:
|
|
||||||
static void link(AstNetlist* nodep);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // Guard
|
|
File diff suppressed because it is too large
Load Diff
@ -114,6 +114,7 @@ private:
|
|||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||||
|
if (nodep->dead()) return;
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_repeatNum = 0;
|
m_repeatNum = 0;
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
|
@ -66,9 +66,13 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VISITs
|
// 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*) {
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||||
// Module: Create sim table for entire module and iterate
|
// Module: Create sim table for entire module and iterate
|
||||||
UINFO(8,"MODULE "<<nodep<<endl);
|
UINFO(8,"MODULE "<<nodep<<endl);
|
||||||
|
if (nodep->dead()) return;
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_senitemCvtNum = 0;
|
m_senitemCvtNum = 0;
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
|
@ -54,6 +54,7 @@ class ParamVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// AstNodeModule::user5() // bool True if processed
|
// AstNodeModule::user5() // bool True if processed
|
||||||
|
// AstGenFor::user5() // bool True if processed
|
||||||
// AstVar::user5() // bool True if constant propagated
|
// AstVar::user5() // bool True if constant propagated
|
||||||
// AstVar::user4() // int Global parameter number (for naming new module)
|
// AstVar::user4() // int Global parameter number (for naming new module)
|
||||||
// // (0=not processed, 1=iterated, but no number, 65+ parameter numbered)
|
// // (0=not processed, 1=iterated, but no number, 65+ parameter numbered)
|
||||||
@ -142,7 +143,9 @@ private:
|
|||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
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. "<<nodep<<endl); // Marked by LinkDot
|
||||||
|
} else if (nodep->level() <= 2) { // Haven't added top yet, so level 2 is the top
|
||||||
// Add request to END of modules left to process
|
// Add request to END of modules left to process
|
||||||
m_todoModps.push_back(nodep);
|
m_todoModps.push_back(nodep);
|
||||||
visitModules();
|
visitModules();
|
||||||
@ -214,13 +217,36 @@ private:
|
|||||||
//! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional
|
//! @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
|
//! 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.
|
//! move to more generic constant expressions, such code will be neede here.
|
||||||
virtual void visit(AstGenFor* nodep, AstNUser*) {
|
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||||||
// We parse a very limited form of FOR, so we don't need to do a full
|
if (nodep->genforp()) {
|
||||||
// simulation to unroll the loop
|
AstGenFor* forp = nodep->genforp()->castGenFor();
|
||||||
UINFO(9," GENFOR "<<nodep<<endl);
|
if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN");
|
||||||
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body
|
// 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 "<<nodep<<endl);
|
||||||
|
UINFO(9," GENFOR "<<forp<<endl);
|
||||||
|
V3Width::widthParamsEdit(forp); // Param typed widthing will NOT recurse the body
|
||||||
|
// Outer wrapper around generate used to hold genvar, and to insure genvar
|
||||||
|
// doesn't conflict in V3LinkDot resolution with other genvars
|
||||||
|
// Now though we need to change BEGIN("zzz",GENFOR(...)) to
|
||||||
|
// a BEGIN("zzz__BRA__{loop#}__KET__")
|
||||||
|
string beginName = nodep->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
|
// Note V3Unroll will replace some AstVarRef's to the loop variable with constants
|
||||||
V3Unroll::unrollGen(nodep); nodep=NULL;
|
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*) {
|
||||||
|
nodep->v3fatalSrc("GENFOR should have been wrapped in BEGIN");
|
||||||
}
|
}
|
||||||
virtual void visit(AstGenCase* nodep, AstNUser*) {
|
virtual void visit(AstGenCase* nodep, AstNUser*) {
|
||||||
UINFO(9," GENCASE "<<nodep<<endl);
|
UINFO(9," GENCASE "<<nodep<<endl);
|
||||||
|
@ -54,8 +54,8 @@ private:
|
|||||||
bool m_varModeCheck; // Just checking RHS assignments
|
bool m_varModeCheck; // Just checking RHS assignments
|
||||||
bool m_varModeReplace; // Replacing varrefs
|
bool m_varModeReplace; // Replacing varrefs
|
||||||
bool m_varAssignHit; // Assign var hit
|
bool m_varAssignHit; // Assign var hit
|
||||||
bool m_inBegin; // Inside a begin/end loop
|
|
||||||
bool m_generate; // Expand single generate For loop
|
bool m_generate; // Expand single generate For loop
|
||||||
|
string m_beginName; // What name to give begin iterations
|
||||||
V3Double0 m_statLoops; // Statistic tracking
|
V3Double0 m_statLoops; // Statistic tracking
|
||||||
V3Double0 m_statIters; // Statistic tracking
|
V3Double0 m_statIters; // Statistic tracking
|
||||||
|
|
||||||
@ -283,7 +283,12 @@ private:
|
|||||||
m_varModeReplace = true;
|
m_varModeReplace = true;
|
||||||
tempp->stmtsp()->iterateAndNext(*this);
|
tempp->stmtsp()->iterateAndNext(*this);
|
||||||
m_varModeReplace = false;
|
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);
|
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 "<<nname<<" "<<nodep<<endl);
|
|
||||||
nodep->name(nname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||||
if (m_varModeCheck
|
if (m_varModeCheck
|
||||||
&& nodep->varp() == m_forVarp
|
&& nodep->varp() == m_forVarp
|
||||||
@ -433,14 +415,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTUCTORS
|
||||||
UnrollVisitor(AstNode* nodep, bool generate) {
|
UnrollVisitor(AstNode* nodep, bool generate, string beginName) {
|
||||||
m_forVarp = NULL;
|
m_forVarp = NULL;
|
||||||
m_forVscp = NULL;
|
m_forVscp = NULL;
|
||||||
m_ignoreIncp = NULL;
|
m_ignoreIncp = NULL;
|
||||||
m_varModeCheck = false;
|
m_varModeCheck = false;
|
||||||
m_varModeReplace = false;
|
m_varModeReplace = false;
|
||||||
m_inBegin = false;
|
|
||||||
m_generate = generate;
|
m_generate = generate;
|
||||||
|
m_beginName = beginName;
|
||||||
//
|
//
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
}
|
}
|
||||||
@ -455,10 +437,10 @@ public:
|
|||||||
|
|
||||||
void V3Unroll::unrollAll(AstNetlist* nodep) {
|
void V3Unroll::unrollAll(AstNetlist* nodep) {
|
||||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||||
UnrollVisitor visitor (nodep, false);
|
UnrollVisitor visitor (nodep, false, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void V3Unroll::unrollGen(AstNodeFor* nodep) {
|
void V3Unroll::unrollGen(AstNodeFor* nodep, string beginName) {
|
||||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||||
UnrollVisitor visitor (nodep, true);
|
UnrollVisitor visitor (nodep, true, beginName);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
class V3Unroll {
|
class V3Unroll {
|
||||||
public:
|
public:
|
||||||
static void unrollAll(AstNetlist* nodep);
|
static void unrollAll(AstNetlist* nodep);
|
||||||
static void unrollGen(AstNodeFor* nodep);
|
static void unrollGen(AstNodeFor* nodep, string beginName);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
@ -58,7 +58,6 @@
|
|||||||
#include "V3Inst.h"
|
#include "V3Inst.h"
|
||||||
#include "V3Life.h"
|
#include "V3Life.h"
|
||||||
#include "V3LifePost.h"
|
#include "V3LifePost.h"
|
||||||
#include "V3Link.h"
|
|
||||||
#include "V3LinkCells.h"
|
#include "V3LinkCells.h"
|
||||||
#include "V3LinkDot.h"
|
#include "V3LinkDot.h"
|
||||||
#include "V3LinkJump.h"
|
#include "V3LinkJump.h"
|
||||||
@ -158,8 +157,6 @@ void process () {
|
|||||||
V3LinkParse::linkParse(v3Global.rootp());
|
V3LinkParse::linkParse(v3Global.rootp());
|
||||||
if (dumpMore) V3Global::dumpGlobalTree("linkparse.tree");
|
if (dumpMore) V3Global::dumpGlobalTree("linkparse.tree");
|
||||||
// Cross-link signal names
|
// Cross-link signal names
|
||||||
V3Link::link(v3Global.rootp());
|
|
||||||
if (dumpMore) V3Global::dumpGlobalTree("linkmain.tree");
|
|
||||||
// Cross-link dotted hierarchical references
|
// Cross-link dotted hierarchical references
|
||||||
V3LinkDot::linkDotPrimary(v3Global.rootp());
|
V3LinkDot::linkDotPrimary(v3Global.rootp());
|
||||||
if (dumpMore) V3Global::dumpGlobalTree("linkdot.tree");
|
if (dumpMore) V3Global::dumpGlobalTree("linkdot.tree");
|
||||||
|
@ -1462,6 +1462,7 @@ generate_region<nodep>: // ==IEEE: generate_region
|
|||||||
generate_block_or_null<nodep>: // IEEE: generate_block_or_null
|
generate_block_or_null<nodep>: // IEEE: generate_block_or_null
|
||||||
// ';' // is included in
|
// ';' // is included in
|
||||||
// // IEEE: generate_block
|
// // 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; }
|
generate_item { $$ = $1 ? (new AstBegin($1->fileline(),"genblk",$1,true)) : NULL; }
|
||||||
| genItemBegin { $$ = $1; }
|
| genItemBegin { $$ = $1; }
|
||||||
;
|
;
|
||||||
@ -1500,17 +1501,27 @@ conditional_generate_construct<nodep>: // ==IEEE: conditional_generate_construct
|
|||||||
|
|
||||||
loop_generate_construct<nodep>: // ==IEEE: loop_generate_construct
|
loop_generate_construct<nodep>: // ==IEEE: loop_generate_construct
|
||||||
yFOR '(' genvar_initialization ';' expr ';' genvar_iteration ')' generate_block_or_null
|
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;
|
AstNode* initp = $3; AstNode* varp = $3;
|
||||||
if (varp->castVar()) { // Genvar
|
if (varp->castVar()) { // Genvar
|
||||||
initp = varp->nextp();
|
initp = varp->nextp();
|
||||||
initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init
|
initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init
|
||||||
blkp->addStmtsp(varp);
|
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
|
// for loop won't get an extra layer of hierarchy tacked on
|
||||||
blkp->addFlatsp(new AstGenFor($1,initp,$5,$7,$9));
|
blkp->addGenforp(new AstGenFor($1,initp,$5,$7,lowerNoBegp));
|
||||||
$$ = blkp; }
|
$$ = blkp;
|
||||||
|
lowerBegp->deleteTree(); lowerBegp=NULL;
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
genvar_initialization<nodep>: // ==IEEE: genvar_initalization
|
genvar_initialization<nodep>: // ==IEEE: genvar_initalization
|
||||||
|
Loading…
Reference in New Issue
Block a user