verilator/src/V3LinkParse.cpp

445 lines
16 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Parse 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.
//
//*************************************************************************
// LinkParse TRANSFORMATIONS:
// Top-down traversal
// Replace ParseRef with lower parseref
// TASKREF(PARSEREF(DOT(TEXTa,TEXTb))) -> DOT(PARSEREF(a),TASKREF(b))
// PARSEREF(TEXTa) -> PARSEREF(a) (no change)
// PARSEREF(DOT(TEXTa,TEXTb)) -> DOT(PARSEREF(A),PARSEREF(b)
// PARSEREF(DOT(DOT(TEXTa,TEXTb),TEXTc)) -> DOT(DOT(PARSEREF(a),PARSEREF(b)),PARSEREF(c))
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <map>
#include <set>
#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
// AstNode::user1() -> bool. True if processed
// AstNode::user2() -> bool. True if fileline recomputed
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
// TYPES
typedef map <pair<void*,string>,AstTypedef*> ImplTypedefMap;
typedef set <FileLine*> FileLineSet;
// STATE
AstParseRefExp m_exp; // Type of data we're looking for
AstVar* m_varp; // Variable we're under
ImplTypedefMap m_implTypedef; // Created typedefs for each <container,name>
FileLineSet m_filelines; // Filelines that have been seen
bool m_inAlways; // Inside an always
bool m_inGenerate; // Inside a generate
bool m_needStart; // Need start marker on lower AstParse
AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module
AstNodeModule* m_modp; // Current module
AstParseRef* m_rightParsep; // RHS()-most parseref
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
void cleanFileline(AstNode* nodep) {
if (!nodep->user2SetOnce()) { // Process once
// 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());
}
}
void checkExpected(AstNode* nodep) {
if (m_exp != AstParseRefExp::PX_NONE) {
nodep->v3fatalSrc("Tree syntax error: Not expecting "<<nodep->type()<<" under a "<<nodep->backp()->type());
m_exp = AstParseRefExp::PX_NONE;
}
}
// VISITs
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
if (!nodep->user1SetOnce()) { // Process only once.
cleanFileline(nodep);
UINFO(5," "<<nodep<<endl);
checkExpected(nodep);
AstNodeModule* upperValueModp = m_valueModp;
m_valueModp = NULL;
nodep->iterateChildren(*this);
m_valueModp = upperValueModp;
}
}
virtual void visit(AstParseRef* nodep, AstNUser*) {
if (nodep->user1SetOnce()) return; // Process only once.
// VarRef: Parse its reference
UINFO(5," "<<nodep<<endl);
{ // First do any function call
AstParseRefExp lastExp = m_exp;
{
m_exp = AstParseRefExp::PX_NONE;
if (nodep->ftaskrefp()) nodep->ftaskrefp()->iterateAndNext(*this);
}
m_exp = lastExp;
}
if (nodep->start()) { // Start of new parseref stack
// The start parseref indicates the type of element to be created.
// Push the start marking down to the lowest non-start parseref under any DOTs.
// May be a varref inside a select, etc, so save state and recurse
AstParseRefExp lastExp = m_exp;
AstParseRef* lastRightp = m_rightParsep;
{
m_exp = nodep->expect();
m_rightParsep = NULL;
m_needStart = true;
nodep->lhsp()->accept(*this);
if (!m_rightParsep) nodep->v3fatalSrc("No child ParseRef found");
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
if (nodep->expect() == AstParseRefExp::PX_FTASK) {
// Put the FTaskRef under the final DOT
AstNodeFTaskRef* ftaskrefp = nodep->ftaskrefp()->castNodeFTaskRef();
if (!ftaskrefp) nodep->v3fatalSrc("Parseref indicates FTASKref but none found");
ftaskrefp->name(m_rightParsep->name());
ftaskrefp->unlinkFrBack();
if (ftaskrefp->packagep()) {
// Can remove the parse stack entirely and V3LinkDot will deal with it natively
lhsp = ftaskrefp;
} else {
m_rightParsep->ftaskrefp(ftaskrefp);
}
}
nodep->replaceWith(lhsp);
nodep->deleteTree(); nodep=NULL;
}
m_exp = lastExp;
m_rightParsep = lastRightp;
}
else { // inside existing stack
nodep->expect(m_exp);
m_rightParsep = nodep;
// Move the start marker down to a standalone parse reference
if (m_needStart) { nodep->start(true); m_needStart = false; }
}
}
virtual void visit(AstDot* nodep, AstNUser*) {
UINFO(5," "<<nodep<<endl);
if (!nodep->user1SetOnce()) { // Process only once.
cleanFileline(nodep);
if (m_exp == AstParseRefExp::PX_NONE) nodep->v3fatalSrc("Tree syntax error: Not expecting "<<nodep->type()<<" under a "<<nodep->backp()->type());
AstParseRefExp lastExp = m_exp;
m_exp = AstParseRefExp::PX_PREDOT;
if (m_needStart) { nodep->start(true); m_needStart = false; }
nodep->lhsp()->iterateAndNext(*this);
m_exp = lastExp;
nodep->rhsp()->iterateAndNext(*this);
}
}
virtual void visit(AstSelBit* nodep, AstNUser*) {
if (!nodep->user1SetOnce()) { // Process only once.
cleanFileline(nodep);
if (m_exp==AstParseRefExp::PX_FTASK) {
nodep->v3error("Syntax Error: Range selection '[]' is not allowed as part of function/task names");
} else {
nodep->lhsp()->iterateAndNext(*this);
AstParseRefExp lastExp = m_exp;
{
m_exp = AstParseRefExp::PX_NONE;
nodep->rhsp()->iterateAndNext(*this);
}
m_exp = lastExp;
}
}
}
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
// Excludes simple AstSel, see above
if (!nodep->user1SetOnce()) { // Process only once.
cleanFileline(nodep);
if (m_exp == AstParseRefExp::PX_PREDOT) { // Already under dot, so this is {modulepart} DOT {modulepart}
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference");
} else if (m_exp==AstParseRefExp::PX_FTASK) {
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed as part of function/task names");
} else {
nodep->lhsp()->iterateAndNext(*this);
AstParseRefExp lastExp = m_exp;
{
m_exp = AstParseRefExp::PX_NONE;
nodep->rhsp()->iterateAndNext(*this);
nodep->thsp()->iterateAndNext(*this);
}
m_exp = lastExp;
}
}
}
virtual void visit(AstEnumItem* nodep, AstNUser*) {
// Expand ranges
cleanFileline(nodep);
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);
AstNode* valuep = NULL;
if (nodep->valuep()) valuep = new AstAdd(nodep->fileline(), nodep->valuep()->cloneTree(true),
new AstConst(nodep->fileline(), AstConst::Unsized32(), offset_from_init));
addp = addp->addNextNull(new AstEnumItem(nodep->fileline(), name, NULL, valuep));
}
nodep->replaceWith(addp);
nodep->deleteTree();
}
}
virtual void visit(AstVar* nodep, AstNUser*) {
cleanFileline(nodep);
// 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);
}
m_varp = nodep;
nodep->iterateChildren(*this);
m_varp = NULL;
// 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
if (nodep->isParam() || nodep->isInOnly()) {
} // 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()));
}
}
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {
cleanFileline(nodep);
nodep->iterateChildren(*this);
if (nodep->attrType() == AstAttrType::VAR_CLOCK) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrScClocked(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrClockEn(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRWPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRdPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRWPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrIsolateAssign(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrSFormat(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_SC_BV) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrScBv(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
// AlwaysPublic was attached under a var, but it's a statement that should be
// at the same level as the var
cleanFileline(nodep);
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));
}
}
virtual void visit(AstDefImplicitDType* nodep, AstNUser*) {
cleanFileline(nodep);
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();
for (; backp; backp=backp->backp()) {
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");
AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack();
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);
nodep->deleteTree(); nodep=NULL;
return;
} else {
defp = new AstTypedef(nodep->fileline(), nodep->name(), VFlagChildDType(), dtypep);
m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp));
backp->addNextHere(defp);
}
}
nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name()));
nodep->deleteTree(); nodep=NULL;
}
virtual void visit(AstTypedefFwd* nodep, AstNUser*) {
// 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();
}
virtual void visit(AstNodeModule* nodep, AstNUser*) {
// Module: Create sim table for entire module and iterate
cleanFileline(nodep);
checkExpected(nodep); // So we detect node types we forgot to list here
//
m_modp = nodep;
m_valueModp = nodep;
nodep->iterateChildren(*this);
m_modp = NULL;
m_valueModp = NULL;
}
void visitIterateNoValueMod(AstNode* nodep) {
// Iterate a node which shouldn't have any local variables moved to an Initial
cleanFileline(nodep);
checkExpected(nodep); // So we detect node types we forgot to list here
//
AstNodeModule* upperValueModp = m_valueModp;
m_valueModp = NULL;
nodep->iterateChildren(*this);
m_valueModp = upperValueModp;
}
virtual void visit(AstInitial* nodep, AstNUser*) {
visitIterateNoValueMod(nodep);
}
virtual void visit(AstFinal* nodep, AstNUser*) {
visitIterateNoValueMod(nodep);
}
virtual void visit(AstAlways* nodep, AstNUser*) {
m_inAlways = true;
visitIterateNoValueMod(nodep);
m_inAlways = false;
}
virtual void visit(AstPslCover* nodep, AstNUser*) {
visitIterateNoValueMod(nodep);
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
cleanFileline(nodep);
checkExpected(nodep); // So we detect node types we forgot to list here
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
LinkParseVisitor(AstNetlist* rootp) {
m_varp = NULL;
m_exp = AstParseRefExp::PX_NONE;
m_modp = NULL;
m_inAlways = false;
m_inGenerate = false;
m_needStart = false;
m_valueModp = NULL;
m_rightParsep = NULL;
rootp->accept(*this);
}
virtual ~LinkParseVisitor() {}
};
//######################################################################
// Link class functions
void V3LinkParse::linkParse(AstNetlist* rootp) {
UINFO(4,__FUNCTION__<<": "<<endl);
LinkParseVisitor visitor(rootp);
}