2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-12-21 21:53:51 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Parse module/signal name references
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-12-21 21:53:51 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2017-01-15 17:09:59 +00:00
|
|
|
|
// Copyright 2003-2017 by Wilson Snyder. This program is free software; you can
|
2006-12-21 21:53:51 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2006-12-21 21:53:51 +00:00
|
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LinkParse TRANSFORMATIONS:
|
|
|
|
|
// Top-down traversal
|
2012-12-31 22:05:13 +00:00
|
|
|
|
// Move some attributes around
|
2006-12-21 21:53:51 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 17:11:25 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
2006-12-21 21:53:51 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <map>
|
2011-01-19 02:28:51 +00:00
|
|
|
|
#include <set>
|
2006-12-21 21:53:51 +00:00
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3LinkParse.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class LinkParseVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on netlist
|
2011-01-19 02:28:51 +00:00
|
|
|
|
// AstNode::user1() -> bool. True if processed
|
|
|
|
|
// AstNode::user2() -> bool. True if fileline recomputed
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser1InUse m_inuser1;
|
2011-01-19 02:28:51 +00:00
|
|
|
|
AstUser2InUse m_inuser2;
|
2006-12-21 21:53:51 +00:00
|
|
|
|
|
2010-01-07 00:04:20 +00:00
|
|
|
|
// TYPES
|
|
|
|
|
typedef map <pair<void*,string>,AstTypedef*> ImplTypedefMap;
|
2011-01-19 02:28:51 +00:00
|
|
|
|
typedef set <FileLine*> FileLineSet;
|
2010-01-07 00:04:20 +00:00
|
|
|
|
|
2006-12-21 21:53:51 +00:00
|
|
|
|
// STATE
|
2012-07-24 10:26:35 +00:00
|
|
|
|
AstVar* m_varp; // Variable we're under
|
2010-01-07 00:04:20 +00:00
|
|
|
|
ImplTypedefMap m_implTypedef; // Created typedefs for each <container,name>
|
2011-01-19 02:28:51 +00:00
|
|
|
|
FileLineSet m_filelines; // Filelines that have been seen
|
2012-06-14 03:08:45 +00:00
|
|
|
|
bool m_inAlways; // Inside an always
|
|
|
|
|
bool m_inGenerate; // Inside a generate
|
2012-07-24 10:26:35 +00:00
|
|
|
|
bool m_needStart; // Need start marker on lower AstParse
|
2012-06-14 03:08:45 +00:00
|
|
|
|
AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module
|
2012-07-22 00:33:24 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module
|
2014-03-08 17:26:34 +00:00
|
|
|
|
AstNodeFTask* m_ftaskp; // Current task
|
2014-11-07 12:50:11 +00:00
|
|
|
|
AstNodeDType* m_dtypep; // Current data type
|
2006-12-21 21:53:51 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-19 02:28:51 +00:00
|
|
|
|
void cleanFileline(AstNode* nodep) {
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user2SetOnce()) { // Process once
|
2011-01-19 02:28:51 +00:00
|
|
|
|
// We make all filelines unique per AstNode. This allows us to
|
|
|
|
|
// later turn off messages on a fileline when an issue is found
|
|
|
|
|
// so that messages on replicated blocks occur only once,
|
|
|
|
|
// without suppressing other token's messages as a side effect.
|
|
|
|
|
// We could have verilog.l create a new one on every token,
|
|
|
|
|
// but that's a lot more structures than only doing AST nodes.
|
|
|
|
|
if (m_filelines.find(nodep->fileline()) != m_filelines.end()) {
|
|
|
|
|
nodep->fileline(new FileLine(nodep->fileline()));
|
|
|
|
|
}
|
|
|
|
|
m_filelines.insert(nodep->fileline());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-21 21:53:51 +00:00
|
|
|
|
// VISITs
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeFTask* nodep) {
|
2014-03-08 17:26:34 +00:00
|
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_ftaskp = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeFTaskRef* nodep) {
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2006-12-21 21:53:51 +00:00
|
|
|
|
UINFO(5," "<<nodep<<endl);
|
2012-06-14 03:08:45 +00:00
|
|
|
|
AstNodeModule* upperValueModp = m_valueModp;
|
|
|
|
|
m_valueModp = NULL;
|
2006-12-21 21:53:51 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_valueModp = upperValueModp;
|
2006-12-21 21:53:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeDType* nodep) {
|
2014-11-07 12:50:11 +00:00
|
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
AstNodeDType* upperDtypep = m_dtypep;
|
|
|
|
|
m_dtypep = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_dtypep = upperDtypep;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstEnumItem* nodep) {
|
2009-12-27 13:29:55 +00:00
|
|
|
|
// Expand ranges
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2009-12-27 13:29:55 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (nodep->rangep()) {
|
|
|
|
|
if (!nodep->rangep()->msbp()->castConst()
|
|
|
|
|
|| !nodep->rangep()->lsbp()->castConst()) nodep->v3error("Enum ranges must be integral, per spec");
|
|
|
|
|
int msb = nodep->rangep()->msbConst();
|
|
|
|
|
int lsb = nodep->rangep()->lsbConst();
|
|
|
|
|
int increment = (msb > lsb) ? -1 : 1;
|
|
|
|
|
int offset_from_init = 0;
|
|
|
|
|
AstNode* addp = NULL;
|
|
|
|
|
for (int i=msb; i!=(lsb+increment); i+=increment, offset_from_init++) {
|
|
|
|
|
string name = nodep->name() + cvtToStr(i);
|
2010-01-21 23:20:47 +00:00
|
|
|
|
AstNode* valuep = NULL;
|
|
|
|
|
if (nodep->valuep()) valuep = new AstAdd(nodep->fileline(), nodep->valuep()->cloneTree(true),
|
|
|
|
|
new AstConst(nodep->fileline(), AstConst::Unsized32(), offset_from_init));
|
2017-07-07 00:25:59 +00:00
|
|
|
|
AstNode* newp = new AstEnumItem(nodep->fileline(), name, NULL, valuep);
|
|
|
|
|
if (addp) addp = addp->addNextNull(newp);
|
|
|
|
|
else addp = newp;
|
2009-12-27 13:29:55 +00:00
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(addp);
|
|
|
|
|
nodep->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVar* nodep) {
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2016-03-15 01:51:31 +00:00
|
|
|
|
if (nodep->subDTypep()->castParseTypeDType()) {
|
|
|
|
|
// It's a parameter type. Use a different node type for this.
|
|
|
|
|
AstNodeDType* dtypep = nodep->valuep()->castNodeDType();
|
|
|
|
|
if (!dtypep) {
|
|
|
|
|
nodep->v3error("Parameter type's initial value isn't a type: "<<nodep->prettyName());
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
} else {
|
|
|
|
|
dtypep->unlinkFrBack();
|
|
|
|
|
AstNode* newp = new AstParamTypeDType(nodep->fileline(), nodep->varType(), nodep->name(),
|
|
|
|
|
VFlagChildDType(), dtypep);
|
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-22 00:33:24 +00:00
|
|
|
|
// 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
|
|
|
|
|
&& (m_modp->level()-1) > v3Global.opt.traceDepth()) {
|
|
|
|
|
m_modp->modTrace(false);
|
|
|
|
|
nodep->trace(false);
|
|
|
|
|
}
|
2009-05-07 22:28:05 +00:00
|
|
|
|
m_varp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_varp = NULL;
|
2012-06-14 03:08:45 +00:00
|
|
|
|
// temporaries under an always aren't expected to be blocking
|
|
|
|
|
if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
|
|
|
|
if (nodep->valuep()) {
|
|
|
|
|
// A variable with an = value can be three things:
|
|
|
|
|
FileLine* fl = nodep->valuep()->fileline();
|
|
|
|
|
// 1. Parameters and function inputs: It's a default to use if not overridden
|
2014-03-08 17:26:34 +00:00
|
|
|
|
if (nodep->isParam() || (m_ftaskp && nodep->isInOnly())) {
|
|
|
|
|
}
|
|
|
|
|
else if (!m_ftaskp && nodep->isInOnly()) {
|
|
|
|
|
nodep->v3error("Unsupported: Default value on module input: "<<nodep->prettyName());
|
|
|
|
|
nodep->valuep()->unlinkFrBack()->deleteTree();
|
2012-06-14 03:08:45 +00:00
|
|
|
|
} // 2. Under modules, it's an initial value to be loaded at time 0 via an AstInitial
|
|
|
|
|
else if (m_valueModp) {
|
|
|
|
|
nodep->addNextHere
|
|
|
|
|
(new AstInitial
|
|
|
|
|
(fl, new AstAssign (fl, new AstVarRef(fl, nodep->name(), true),
|
|
|
|
|
nodep->valuep()->unlinkFrBack())));
|
|
|
|
|
} // 3. Under blocks, it's an initial value to be under an assign
|
|
|
|
|
else {
|
|
|
|
|
nodep->addNextHere
|
|
|
|
|
(new AstAssign (fl, new AstVarRef(fl, nodep->name(), true),
|
|
|
|
|
nodep->valuep()->unlinkFrBack()));
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-28 01:39:19 +00:00
|
|
|
|
if (nodep->isIfaceRef() && !nodep->isIfaceParent()) {
|
|
|
|
|
// Only AstIfaceRefDType's at this point correspond to ports;
|
|
|
|
|
// haven't made additional ones for interconnect yet, so assert is simple
|
|
|
|
|
// What breaks later is we don't have a Scope/Cell representing the interface to attach to
|
|
|
|
|
if (m_modp->level()<=2) nodep->v3error("Unsupported: Interfaced port on top level module");
|
|
|
|
|
}
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAttrOf* nodep) {
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
2014-11-07 12:50:11 +00:00
|
|
|
|
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
|
|
|
|
|
AstTypedef* typep = nodep->backp()->castTypedef();
|
|
|
|
|
if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef");
|
|
|
|
|
typep->attrPublic(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2014-11-07 12:50:11 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_CLOCK) {
|
2009-05-07 22:28:05 +00:00
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrScClocked(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrClockEn(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
2010-04-06 00:01:17 +00:00
|
|
|
|
m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
2010-04-06 00:01:17 +00:00
|
|
|
|
m_varp->sigUserRWPublic(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2010-04-06 00:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->sigUserRdPublic(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2010-04-06 00:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->sigUserRWPublic(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrIsolateAssign(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
2010-01-18 00:13:44 +00:00
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrSFormat(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2010-01-18 00:13:44 +00:00
|
|
|
|
}
|
2011-10-26 12:57:27 +00:00
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_SC_BV) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrScBv(true);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2011-10-26 12:57:27 +00:00
|
|
|
|
}
|
2015-03-12 23:20:46 +00:00
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_YES);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2015-03-12 23:20:46 +00:00
|
|
|
|
}
|
|
|
|
|
else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) {
|
|
|
|
|
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
|
|
|
|
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_NO);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
2015-03-12 23:20:46 +00:00
|
|
|
|
}
|
2009-05-07 22:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlwaysPublic* nodep) {
|
2010-04-06 00:01:17 +00:00
|
|
|
|
// AlwaysPublic was attached under a var, but it's a statement that should be
|
|
|
|
|
// at the same level as the var
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2010-04-06 00:01:17 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (m_varp) {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
m_varp->addNext(nodep);
|
|
|
|
|
// lvalue is true, because we know we have a verilator public_flat_rw
|
|
|
|
|
// but someday we may be more general
|
|
|
|
|
bool lvalue = m_varp->isSigUserRWPublic();
|
|
|
|
|
nodep->addStmtp(new AstVarRef(nodep->fileline(), m_varp, lvalue));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstDefImplicitDType* nodep) {
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2010-01-07 00:04:20 +00:00
|
|
|
|
UINFO(8," DEFIMPLICIT "<<nodep<<endl);
|
|
|
|
|
// Must remember what names we've already created, and combine duplicates
|
|
|
|
|
// so that for "var enum {...} a,b" a & b will share a common typedef
|
|
|
|
|
// Unique name space under each containerp() so that an addition of a new type won't change every verilated module.
|
|
|
|
|
AstTypedef* defp = NULL;
|
|
|
|
|
ImplTypedefMap::iterator it = m_implTypedef.find(make_pair(nodep->containerp(), nodep->name()));
|
|
|
|
|
if (it != m_implTypedef.end()) {
|
|
|
|
|
defp = it->second;
|
|
|
|
|
} else {
|
|
|
|
|
// Definition must be inserted right after the variable (etc) that needed it
|
|
|
|
|
// AstVar, AstTypedef, AstNodeFTask are common containers
|
|
|
|
|
AstNode* backp = nodep->backp();
|
2011-12-26 23:32:23 +00:00
|
|
|
|
for (; backp; backp=backp->backp()) {
|
2010-01-07 00:04:20 +00:00
|
|
|
|
if (backp->castVar()) break;
|
|
|
|
|
else if (backp->castTypedef()) break;
|
|
|
|
|
else if (backp->castNodeFTask()) break;
|
|
|
|
|
}
|
|
|
|
|
if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type");
|
2012-04-29 14:14:13 +00:00
|
|
|
|
AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack();
|
2010-01-07 00:04:20 +00:00
|
|
|
|
if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing
|
|
|
|
|
// For typedefs just remove the AstRefDType level of abstraction
|
|
|
|
|
nodep->replaceWith(dtypep);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2010-01-07 00:04:20 +00:00
|
|
|
|
return;
|
|
|
|
|
} else {
|
2014-11-07 12:50:11 +00:00
|
|
|
|
defp = new AstTypedef(nodep->fileline(), nodep->name(), NULL, VFlagChildDType(), dtypep);
|
2010-01-07 00:04:20 +00:00
|
|
|
|
m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp));
|
|
|
|
|
backp->addNextHere(defp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name()));
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2010-01-07 00:04:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstTypedefFwd* nodep) {
|
2012-05-28 14:23:47 +00:00
|
|
|
|
// We only needed the forward declaration in order to parse correctly.
|
|
|
|
|
// We won't even check it was ever really defined, as it might have been in a header
|
|
|
|
|
// file referring to a module we never needed
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstForeach* nodep) {
|
2016-09-20 02:00:13 +00:00
|
|
|
|
// FOREACH(array,loopvars,body)
|
|
|
|
|
// -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body))
|
|
|
|
|
//nodep->dumpTree(cout, "-foreach-old:");
|
|
|
|
|
AstNode* newp = nodep->bodysp()->unlinkFrBackWithNext();
|
|
|
|
|
AstNode* arrayp = nodep->arrayp();
|
|
|
|
|
int dimension = 1;
|
|
|
|
|
// Must do innermost (last) variable first
|
|
|
|
|
AstNode* firstVarsp = nodep->varsp()->unlinkFrBackWithNext();
|
|
|
|
|
AstNode* lastVarsp = firstVarsp;
|
|
|
|
|
while (lastVarsp->nextp()) { lastVarsp = lastVarsp->nextp(); dimension++; }
|
|
|
|
|
for (AstNode* varsp = lastVarsp; varsp; varsp=varsp->backp()) {
|
|
|
|
|
UINFO(0,"foreachVar "<<varsp<<endl);
|
|
|
|
|
FileLine* fl = varsp->fileline();
|
|
|
|
|
AstNode* varp = new AstVar(fl, AstVarType::BLOCKTEMP,
|
|
|
|
|
varsp->name(), nodep->findSigned32DType());
|
|
|
|
|
AstNode* leftp = new AstAttrOf(fl, AstAttrType::DIM_LEFT,
|
|
|
|
|
new AstVarRef(fl, arrayp->name(), false),
|
|
|
|
|
new AstConst(fl, dimension));
|
|
|
|
|
AstNode* rightp = new AstAttrOf(fl, AstAttrType::DIM_RIGHT,
|
|
|
|
|
new AstVarRef(fl, arrayp->name(), false),
|
|
|
|
|
new AstConst(fl, dimension));
|
|
|
|
|
AstNode* stmtsp = varp;
|
|
|
|
|
stmtsp->addNext(new AstAssign(fl, new AstVarRef(fl, varp->name(), true), leftp));
|
|
|
|
|
AstNode* comparep =
|
|
|
|
|
new AstCond(fl, new AstLte(fl, leftp->cloneTree(true), rightp->cloneTree(true)),
|
|
|
|
|
// left increments up to right
|
|
|
|
|
new AstLte(fl, new AstVarRef(fl, varp->name(), false), rightp->cloneTree(true)),
|
|
|
|
|
// left decrements down to right
|
|
|
|
|
new AstGte(fl, new AstVarRef(fl, varp->name(), false), rightp));
|
|
|
|
|
AstNode* incp =
|
|
|
|
|
new AstAssign(fl, new AstVarRef(fl, varp->name(), true),
|
|
|
|
|
new AstAdd(fl, new AstVarRef(fl, varp->name(), false),
|
|
|
|
|
new AstNegate(fl, new AstAttrOf(fl, AstAttrType::DIM_INCREMENT,
|
|
|
|
|
new AstVarRef(fl, arrayp->name(), false),
|
|
|
|
|
new AstConst(fl, dimension)))));
|
|
|
|
|
stmtsp->addNext(new AstWhile(fl, comparep, newp, incp));
|
|
|
|
|
newp = new AstBegin(nodep->fileline(),"",stmtsp);
|
|
|
|
|
dimension--;
|
|
|
|
|
}
|
|
|
|
|
//newp->dumpTree(cout, "-foreach-new:");
|
|
|
|
|
firstVarsp->deleteTree(); VL_DANGLING(firstVarsp);
|
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2012-06-14 03:08:45 +00:00
|
|
|
|
// Module: Create sim table for entire module and iterate
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
//
|
2012-07-22 00:33:24 +00:00
|
|
|
|
m_modp = nodep;
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_valueModp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
2012-07-22 00:33:24 +00:00
|
|
|
|
m_modp = NULL;
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_valueModp = NULL;
|
|
|
|
|
}
|
|
|
|
|
void visitIterateNoValueMod(AstNode* nodep) {
|
|
|
|
|
// Iterate a node which shouldn't have any local variables moved to an Initial
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
//
|
|
|
|
|
AstNodeModule* upperValueModp = m_valueModp;
|
|
|
|
|
m_valueModp = NULL;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_valueModp = upperValueModp;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstInitial* nodep) {
|
2012-06-14 03:08:45 +00:00
|
|
|
|
visitIterateNoValueMod(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstFinal* nodep) {
|
2012-06-14 03:08:45 +00:00
|
|
|
|
visitIterateNoValueMod(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlways* nodep) {
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_inAlways = true;
|
|
|
|
|
visitIterateNoValueMod(nodep);
|
|
|
|
|
m_inAlways = false;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstPslCover* nodep) {
|
2012-06-14 03:08:45 +00:00
|
|
|
|
visitIterateNoValueMod(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2006-12-21 21:53:51 +00:00
|
|
|
|
// Default: Just iterate
|
2011-01-19 02:28:51 +00:00
|
|
|
|
cleanFileline(nodep);
|
2006-12-21 21:53:51 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit LinkParseVisitor(AstNetlist* rootp) {
|
2009-05-07 22:28:05 +00:00
|
|
|
|
m_varp = NULL;
|
2012-07-22 00:33:24 +00:00
|
|
|
|
m_modp = NULL;
|
2014-03-08 17:26:34 +00:00
|
|
|
|
m_ftaskp = NULL;
|
2014-11-07 12:50:11 +00:00
|
|
|
|
m_dtypep = NULL;
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_inAlways = false;
|
|
|
|
|
m_inGenerate = false;
|
2012-07-24 10:26:35 +00:00
|
|
|
|
m_needStart = false;
|
2012-06-14 03:08:45 +00:00
|
|
|
|
m_valueModp = NULL;
|
2006-12-21 21:53:51 +00:00
|
|
|
|
rootp->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~LinkParseVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Link class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
|
|
|
|
UINFO(4,__FUNCTION__<<": "<<endl);
|
|
|
|
|
LinkParseVisitor visitor(rootp);
|
2017-09-18 02:52:57 +00:00
|
|
|
|
V3Global::dumpCheckGlobalTree("linkparse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
2006-12-21 21:53:51 +00:00
|
|
|
|
}
|