2010-02-14 15:01:21 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Replace return/continue with jumps
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
|
|
|
|
//
|
|
|
|
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2011-01-01 23:21:19 +00:00
|
|
|
|
// Copyright 2003-2011 by Wilson Snyder. This program is free software; you can
|
2010-02-14 15:01:21 +00:00
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3LinkJump's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Each module:
|
|
|
|
|
// Look for BEGINs
|
|
|
|
|
// BEGIN(VAR...) -> VAR ... {renamed}
|
|
|
|
|
// FOR -> WHILEs
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3LinkJump.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class LinkJumpVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// TYPES
|
|
|
|
|
typedef vector<AstBegin*> BeginStack;
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
AstModule* m_modp; // Current module
|
|
|
|
|
AstNodeFTask* m_ftaskp; // Current function/task
|
|
|
|
|
AstWhile* m_loopp; // Current loop
|
2011-01-06 11:46:19 +00:00
|
|
|
|
bool m_loopInc; // In loop increment
|
2010-02-14 15:01:21 +00:00
|
|
|
|
int m_repeatNum; // Repeat counter
|
|
|
|
|
BeginStack m_beginStack; // All begin blocks above current node
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
|
|
|
|
|
// Put label under given node, and if WHILE optionally at end of iteration
|
|
|
|
|
UINFO(4,"Create label for "<<nodep<<endl);
|
|
|
|
|
if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done
|
|
|
|
|
|
|
|
|
|
AstNode* underp = NULL;
|
|
|
|
|
bool under_and_next = true;
|
|
|
|
|
if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp();
|
|
|
|
|
else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp();
|
|
|
|
|
else if (nodep->castWhile()) {
|
|
|
|
|
if (endOfIter) {
|
|
|
|
|
// Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip
|
|
|
|
|
underp = nodep->castWhile()->bodysp();
|
|
|
|
|
} else {
|
|
|
|
|
underp = nodep; under_and_next=false; // IE we skip the entire while
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodep->v3fatalSrc("Unknown jump point for break/disable/continue");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!underp) {
|
|
|
|
|
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
|
|
|
|
|
return NULL;
|
|
|
|
|
} else if (underp->castJumpLabel()) {
|
|
|
|
|
return underp->castJumpLabel();
|
|
|
|
|
} else { // Move underp stuff to be under a new label
|
|
|
|
|
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
|
|
|
|
|
|
|
|
|
|
AstNRelinker repHandle;
|
|
|
|
|
if (under_and_next) underp->unlinkFrBackWithNext(&repHandle);
|
|
|
|
|
else underp->unlinkFrBack(&repHandle);
|
|
|
|
|
repHandle.relink(labelp);
|
|
|
|
|
|
|
|
|
|
labelp->addStmtsp(underp);
|
|
|
|
|
// Keep any AstVars under the function not under the new JumpLabel
|
|
|
|
|
for (AstNode* nextp, *varp=underp; varp; varp = nextp) {
|
|
|
|
|
nextp = varp->nextp();
|
|
|
|
|
if (varp->castVar()) {
|
|
|
|
|
labelp->addPrev(varp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return labelp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstModule* nodep, AstNUser*) {
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_repeatNum = 0;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_ftaskp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstBegin* nodep, AstNUser*) {
|
|
|
|
|
UINFO(8," "<<nodep<<endl);
|
|
|
|
|
m_beginStack.push_back(nodep);
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_beginStack.pop_back();
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstRepeat* nodep, AstNUser*) {
|
|
|
|
|
// So later optimizations don't need to deal with them,
|
|
|
|
|
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
|
|
|
|
|
// Note var can be signed or unsigned based on original number.
|
|
|
|
|
AstNode* countp = nodep->countp()->unlinkFrBackWithNext();
|
|
|
|
|
string name = string("__Vrepeat")+cvtToStr(m_repeatNum++);
|
|
|
|
|
// Spec says value is integral, if negative is ignored
|
|
|
|
|
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name,
|
|
|
|
|
AstLogicPacked(), 32);
|
2011-07-24 19:01:51 +00:00
|
|
|
|
varp->numeric(AstNumeric::SIGNED);
|
|
|
|
|
varp->dtypep()->numeric(AstNumeric::SIGNED);
|
2011-01-06 11:46:19 +00:00
|
|
|
|
varp->usedLoopIdx(true);
|
2010-02-14 15:01:21 +00:00
|
|
|
|
m_modp->addStmtp(varp);
|
|
|
|
|
AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
|
|
|
|
|
countp);
|
|
|
|
|
AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
|
|
|
|
|
new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
|
|
|
|
new AstConst(nodep->fileline(), 1)));
|
2011-07-24 19:01:51 +00:00
|
|
|
|
AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->numeric(AstNumeric::SIGNED);
|
2010-02-14 15:01:21 +00:00
|
|
|
|
AstNode* condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
|
|
|
|
zerosp);
|
|
|
|
|
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
|
|
|
|
AstNode* newp = new AstWhile(nodep->fileline(),
|
|
|
|
|
condp,
|
|
|
|
|
bodysp,
|
|
|
|
|
decp);
|
|
|
|
|
initsp = initsp->addNext(newp);
|
|
|
|
|
newp = initsp;
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
|
|
|
|
// Don't need to track AstRepeat/AstFor as they have already been converted
|
|
|
|
|
AstWhile* lastLoopp = m_loopp;
|
2011-01-06 11:46:19 +00:00
|
|
|
|
bool lastInc = m_loopInc;
|
2010-02-14 15:01:21 +00:00
|
|
|
|
m_loopp = nodep;
|
2011-01-06 11:46:19 +00:00
|
|
|
|
m_loopInc = false;
|
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
m_loopInc = true;
|
|
|
|
|
nodep->incsp()->iterateAndNext(*this);
|
|
|
|
|
m_loopInc = lastInc;
|
2010-02-14 15:01:21 +00:00
|
|
|
|
m_loopp = lastLoopp;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstReturn* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
AstFunc* funcp = m_ftaskp->castFunc();
|
|
|
|
|
if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); }
|
|
|
|
|
else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); }
|
|
|
|
|
else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); }
|
|
|
|
|
else {
|
|
|
|
|
if (funcp && nodep->lhsp()) {
|
|
|
|
|
// Set output variable to return value
|
|
|
|
|
nodep->addPrev(new AstAssign(nodep->fileline(),
|
|
|
|
|
new AstVarRef(nodep->fileline(), funcp->fvarp()->castVar(), true),
|
|
|
|
|
nodep->lhsp()->unlinkFrBackWithNext()));
|
|
|
|
|
}
|
|
|
|
|
// Jump to the end of the function call
|
|
|
|
|
AstJumpLabel* labelp = findAddLabel(m_ftaskp, false);
|
|
|
|
|
nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp));
|
|
|
|
|
}
|
|
|
|
|
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstBreak* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); }
|
|
|
|
|
else {
|
|
|
|
|
// Jump to the end of the loop
|
|
|
|
|
AstJumpLabel* labelp = findAddLabel(m_loopp, false);
|
|
|
|
|
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
|
|
|
|
}
|
|
|
|
|
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstContinue* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); }
|
|
|
|
|
else {
|
|
|
|
|
// Jump to the end of this iteration
|
|
|
|
|
// If a "for" loop then need to still do the post-loop increment
|
|
|
|
|
AstJumpLabel* labelp = findAddLabel(m_loopp, true);
|
|
|
|
|
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
|
|
|
|
}
|
|
|
|
|
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
}
|
2011-06-29 01:26:49 +00:00
|
|
|
|
virtual void visit(AstDisable* nodep, AstNUser*) {
|
|
|
|
|
UINFO(8," DISABLE "<<nodep<<endl);
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
AstBegin* beginp = NULL;
|
|
|
|
|
for (BeginStack::reverse_iterator it = m_beginStack.rbegin(); it != m_beginStack.rend(); ++it) {
|
|
|
|
|
UINFO(9," UNDERBLK "<<*it<<endl);
|
|
|
|
|
if ((*it)->name() == nodep->name()) {
|
|
|
|
|
beginp = *it;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labeli: "); }
|
|
|
|
|
if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "<<nodep->name()); }
|
|
|
|
|
else {
|
|
|
|
|
// Jump to the end of the named begin
|
|
|
|
|
AstJumpLabel* labelp = findAddLabel(beginp, false);
|
|
|
|
|
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
|
|
|
|
}
|
|
|
|
|
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labelo: "); }
|
|
|
|
|
}
|
2011-01-06 11:46:19 +00:00
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
|
|
|
|
if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true);
|
|
|
|
|
}
|
2010-02-14 15:01:21 +00:00
|
|
|
|
|
2011-01-06 11:46:19 +00:00
|
|
|
|
virtual void visit(AstConst* nodep, AstNUser*) {}
|
2010-02-14 15:01:21 +00:00
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
LinkJumpVisitor(AstNetlist* nodep) {
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
m_ftaskp = NULL;
|
|
|
|
|
m_loopp = NULL;
|
2011-01-06 11:46:19 +00:00
|
|
|
|
m_loopInc = false;
|
2010-02-14 15:01:21 +00:00
|
|
|
|
m_repeatNum = 0;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~LinkJumpVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Task class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkJump::linkJump(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
LinkJumpVisitor bvisitor (nodep);
|
|
|
|
|
}
|