verilator/src/V3LinkResolve.cpp
Wilson Snyder a2ffe86a36 Copyright update
git-svn-id: file://localhost/svn/verilator/trunk/verilator@976 77ca24e4-aefa-0310-84f0-b9a241c72d87
2008-01-15 14:29:08 +00:00

428 lines
14 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.

// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Resolve module/signal name references
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2008 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// 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.
//
//*************************************************************************
// LinkResolve TRANSFORMATIONS:
// Top-down traversal
// Extracts:
// Add SUB so that we subtract off the "base 0-start" of the array
// SelBit: Convert to ArraySel
// Add SUB so that we subtract off the "base 0-start" of the array
// File operations
// Convert normal var to FILE* type
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3LinkResolve.h"
#include "V3Ast.h"
//######################################################################
// Link state, as a visitor of each AstNode
class LinkResolveVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstCaseItem::user2() // bool Moved default caseitems
// STATE
// Below state needs to be preserved between each module call.
AstModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Function or task we're inside
AstVAssert* m_assertp; // Current assertion
//int debug() { return 9; }
// METHODS
// VISITs
virtual void visit(AstNetlist* nodep, AstNUser*) {
AstNode::user2ClearTree();
// And recurse...
nodep->iterateChildren(*this);
}
virtual void visit(AstModule* nodep, AstNUser*) {
// Module: Create sim table for entire module and iterate
UINFO(8,"MODULE "<<nodep<<endl);
m_modp = nodep;
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstVAssert* nodep, AstNUser*) {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
m_assertp = nodep;
nodep->iterateChildren(*this);
m_assertp = NULL;
}
virtual void visit(AstVar* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->arraysp() && nodep->isIO()) {
nodep->v3error("Arrayed variables may not be inputs nor outputs");
}
if (m_ftaskp) nodep->funcLocal(true);
if (nodep->isSigModPublic()) {
nodep->sigModPublic(false); // We're done with this attribute
m_modp->modPublic(true); // Avoid flattening if signals are exposed
}
}
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
// VarRef: Resolve its reference
if (nodep->varp()) {
nodep->varp()->usedParam(true);
}
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
// NodeTask: Remember its name for later resolution
// Remember the existing symbol table scope
m_ftaskp = nodep;
nodep->iterateChildren(*this);
m_ftaskp = NULL;
}
virtual void visit(AstSenItem* nodep, AstNUser*) {
// Remove bit selects, and bark if it's not a simple variable
nodep->iterateChildren(*this);
bool did=1;
while (did) {
did=0;
if (AstNodeSel* selp = nodep->sensp()->castNodeSel()) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
did=1;
}
// NodeSel doesn't include AstSel....
if (AstSel* selp = nodep->sensp()->castSel()) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
did=1;
}
}
if (!nodep->sensp()->castNodeVarRef()) {
if (debug()) nodep->dumpTree(cout,"-tree: ");
nodep->v3error("Unsupported: Complex statement in sensitivity list");
}
}
void iterateSelTriop(AstNodePreSel* nodep) {
nodep->iterateChildren(*this);
}
AstNode* newSubAttrOf(AstNode* underp, AstNode* fromp, AstAttrType attrType) {
// Account for a variable's LSB in bit selections
// Replace underp with a SUB(underp, ATTROF(varp, attrType))
int dimension=0;
while (fromp && !fromp->castNodeVarRef() && (fromp->castSel() || fromp->castArraySel())) {
if (fromp->castSel()) fromp = fromp->castSel()->fromp();
else fromp = fromp->castArraySel()->fromp();
dimension++;
}
AstNodeVarRef* varrefp = fromp->castNodeVarRef();
if (!varrefp) fromp->v3fatalSrc("Bit/array selection of non-variable");
if (!varrefp->varp()) varrefp->v3fatalSrc("Signal not linked");
AstRange* vararrayp = varrefp->varp()->arrayp(dimension);
AstRange* varrangep = varrefp->varp()->rangep();
if ((attrType==AstAttrType::ARRAY_LSB
// SUB #'s Not needed because LSB==0? (1D only, else we'll constify it later)
? (vararrayp && !vararrayp->lsbp()->isZero())
: (varrangep && !varrangep->lsbp()->isZero()))) {
AstNode* newrefp;
if (varrefp->castVarXRef()) {
newrefp = new AstVarXRef(underp->fileline(),
varrefp->varp(), varrefp->castVarXRef()->dotted(), false);
} else {
newrefp = new AstVarRef (underp->fileline(),
varrefp->varp(), false);
}
AstNode* newp = new AstSub (underp->fileline(),
underp,
new AstAttrOf(underp->fileline(),
attrType, newrefp, dimension));
return newp;
} else {
return underp;
}
}
void selCheckDimension(AstSel* nodep) {
// Perform error checks on the node
AstNode* fromp = nodep->lhsp();
AstNode* basefromp = AstArraySel::baseFromp(fromp);
AstNodeVarRef* varrefp = basefromp->castNodeVarRef();
AstVar* varp = varrefp ? varrefp->varp() : NULL;
if (fromp->castSel()
|| (varp && !varp->rangep() && !varp->isParam())) {
nodep->v3error("Illegal bit select; variable already selected, or bad dimension");
}
}
virtual void visit(AstSelBit* nodep, AstNUser*) {
// Couldn't tell until link time if [#] references a bit or an array
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* bitp = nodep->rhsp()->unlinkFrBack();
AstNode* basefromp = AstArraySel::baseFromp(fromp);
int dimension = AstArraySel::dimension(fromp);
AstNodeVarRef* varrefp = basefromp->castNodeVarRef();
if (varrefp
&& varrefp->varp()
&& varrefp->varp()->arrayp(dimension)) {
// SELBIT(array, index) -> ARRAYSEL(array, index)
AstNode* newp = new AstArraySel
(nodep->fileline(),
fromp,
newSubAttrOf(bitp, fromp, AstAttrType::ARRAY_LSB));
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
else {
// SELBIT(range, index) -> SEL(array, index, 1)
V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(bitp, fromp, AstAttrType::RANGE_LSB),
new AstConst (nodep->fileline(),one));
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
}
virtual void visit(AstSelExtract* nodep, AstNUser*) {
// SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb)
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* msbp = nodep->rhsp()->unlinkFrBack();
AstNode* lsbp = nodep->thsp()->unlinkFrBack();
AstNode* widthp;
if (msbp->castConst() && lsbp->castConst()) {
// Quite common, save V3Const some effort
V3Number widnum (msbp->fileline(),32,msbp->castConst()->asInt() +1-lsbp->castConst()->asInt());
widnum.width(32,false); // Unsized so width from user
widthp = new AstConst (msbp->fileline(), widnum);
pushDeletep(msbp);
} else {
V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user
widthp = new AstSub (lsbp->fileline(),
new AstAdd(msbp->fileline(),
new AstConst(msbp->fileline(),one),
msbp),
lsbp->cloneTree(true));
}
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstSelPlus* nodep, AstNUser*) {
// SELPLUS -> SEL
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* lsbp = nodep->rhsp()->unlinkFrBack();
AstNode* widthp = nodep->thsp()->unlinkFrBack();
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstSelMinus* nodep, AstNUser*) {
// SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#)
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* msbp = nodep->rhsp()->unlinkFrBack();
AstNode* widthp = nodep->thsp()->unlinkFrBack();
V3Number one (msbp->fileline(),32,1); one.width(32,false); // Unsized so width from user
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(new AstSub (msbp->fileline(),
msbp,
new AstSub (msbp->fileline(),
widthp->cloneTree(true),
new AstConst (msbp->fileline(), one))),
fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstCaseItem* nodep, AstNUser*) {
// Move default caseItems to the bottom of the list
// That saves us from having to search each case list twice, for non-defaults and defaults
nodep->iterateChildren(*this);
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
nodep->user2(true);
AstNode* nextp = nodep->nextp();
nodep->unlinkFrBack();
nextp->addNext(nodep);
}
}
virtual void visit(AstPragma* nodep, AstNUser*) {
if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module\n");
m_modp->modPublic(true);
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) {
if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task\n");
m_ftaskp->taskPublic(true);
m_modp->modPublic(true); // Need to get to the task...
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
}
else {
nodep->iterateChildren(*this);
}
}
void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) {
if (!filep) nodep->v3error("Unsupported: $fopen/$fclose descriptor must be a simple variable");
if (filep && filep->varp()) filep->varp()->attrFileDescr(true);
}
virtual void visit(AstFOpen* nodep, AstNUser*) {
nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
}
virtual void visit(AstFClose* nodep, AstNUser*) {
nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
}
virtual void visit(AstDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
if (!m_assertp
&& (nodep->displayType() == AstDisplayType::INFO
|| nodep->displayType() == AstDisplayType::WARNING
|| nodep->displayType() == AstDisplayType::ERROR
|| nodep->displayType() == AstDisplayType::FATAL)) {
nodep->v3error(nodep->verilogKwd()+" only allowed under a assertion.");
}
if (nodep->displayType().needScopeTracking()
|| nodep->name().find("%m") != string::npos) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstScCtor* nodep, AstNUser*) {
// Constructor info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
}
virtual void visit(AstScDtor* nodep, AstNUser*) {
// Destructor info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
}
virtual void visit(AstScInt* nodep, AstNUser*) {
// Special class info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
LinkResolveVisitor(AstNetlist* rootp) {
m_ftaskp = NULL;
m_modp = NULL;
m_assertp = NULL;
//
rootp->accept(*this);
}
virtual ~LinkResolveVisitor() {}
};
//######################################################################
// LinkBotupVisitor
// Recurses cells backwards, so we can pick up those things that propagate
// from child cells up to the top module.
class LinkBotupVisitor : public AstNVisitor {
private:
// STATE
AstModule* m_modp; // Current module
//int debug() { return 9; }
// VISITs
virtual void visit(AstNetlist* nodep, AstNUser*) {
// Iterate modules backwards, in bottom-up order.
nodep->iterateChildrenBackwards(*this);
}
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstCell* nodep, AstNUser*) {
// Parent module inherits child's publicity
if (nodep->modp()->modPublic()) m_modp->modPublic(true);
//** No iteration for speed
}
virtual void visit(AstNodeMath* nodep, AstNUser*) {
// Speedup
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
LinkBotupVisitor(AstNetlist* rootp) {
m_modp = NULL;
//
rootp->accept(*this);
}
virtual ~LinkBotupVisitor() {}
};
//######################################################################
// Link class functions
void V3LinkResolve::linkResolve(AstNetlist* rootp) {
UINFO(4,__FUNCTION__<<": "<<endl);
LinkResolveVisitor visitor(rootp);
LinkBotupVisitor visitorb(rootp);
}