forked from github/verilator
Support "break", "continue", "return".
This commit is contained in:
parent
48603c0ee2
commit
cdd06e7236
2
Changes
2
Changes
@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||||||
|
|
||||||
* Verilator 3.80***
|
* Verilator 3.80***
|
||||||
|
|
||||||
|
*** Support "break", "continue", "return".
|
||||||
|
|
||||||
**** Skip SystemC tests if not installed. [Iztok Jeras]
|
**** Skip SystemC tests if not installed. [Iztok Jeras]
|
||||||
|
|
||||||
**** Fix make uninstall, bug216. [Iztok Jeras]
|
**** Fix make uninstall, bug216. [Iztok Jeras]
|
||||||
|
@ -194,8 +194,9 @@ RAW_OBJS = \
|
|||||||
V3Link.o \
|
V3Link.o \
|
||||||
V3LinkCells.o \
|
V3LinkCells.o \
|
||||||
V3LinkDot.o \
|
V3LinkDot.o \
|
||||||
V3LinkLevel.o \
|
V3LinkJump.o \
|
||||||
V3LinkLValue.o \
|
V3LinkLValue.o \
|
||||||
|
V3LinkLevel.o \
|
||||||
V3LinkParse.o \
|
V3LinkParse.o \
|
||||||
V3LinkResolve.o \
|
V3LinkResolve.o \
|
||||||
V3Localize.o \
|
V3Localize.o \
|
||||||
|
@ -761,6 +761,7 @@ public:
|
|||||||
AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); }
|
AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); }
|
||||||
AstNode* firstAbovep() const { return ((backp() && backp()->nextp()!=this) ? backp() : NULL); } // Returns NULL when second or later in list
|
AstNode* firstAbovep() const { return ((backp() && backp()->nextp()!=this) ? backp() : NULL); } // Returns NULL when second or later in list
|
||||||
bool brokeExists() const;
|
bool brokeExists() const;
|
||||||
|
bool brokeExistsAbove() const;
|
||||||
|
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
virtual ~AstNode();
|
virtual ~AstNode();
|
||||||
@ -870,6 +871,7 @@ public:
|
|||||||
AstNode* addNext(AstNode* newp); // Returns this, adds to end of list
|
AstNode* addNext(AstNode* newp); // Returns this, adds to end of list
|
||||||
AstNode* addNextNull(AstNode* newp); // Returns this, adds to end of list, NULL is OK
|
AstNode* addNextNull(AstNode* newp); // Returns this, adds to end of list, NULL is OK
|
||||||
void addNextHere(AstNode* newp); // Adds after speced node
|
void addNextHere(AstNode* newp); // Adds after speced node
|
||||||
|
void addPrev(AstNode* newp) { replaceWith(newp); newp->addNext(this); }
|
||||||
void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next
|
void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next
|
||||||
void replaceWith(AstNode* newp); // Replace current node in tree with new node
|
void replaceWith(AstNode* newp); // Replace current node in tree with new node
|
||||||
void v3errorEnd(ostringstream& str) const;
|
void v3errorEnd(ostringstream& str) const;
|
||||||
|
@ -494,6 +494,12 @@ void AstDisplay::dump(ostream& str) {
|
|||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
//str<<" "<<displayType().ascii();
|
//str<<" "<<displayType().ascii();
|
||||||
}
|
}
|
||||||
|
void AstJumpGo::dump(ostream& str) {
|
||||||
|
this->AstNode::dump(str);
|
||||||
|
str<<" -> ";
|
||||||
|
if (labelp()) { labelp()->dump(str); }
|
||||||
|
else { str<<"%Error:UNLINKED"; }
|
||||||
|
}
|
||||||
void AstEnumItemRef::dump(ostream& str) {
|
void AstEnumItemRef::dump(ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
str<<" -> ";
|
str<<" -> ";
|
||||||
|
@ -1846,14 +1846,6 @@ struct AstGenFor : public AstNodeFor {
|
|||||||
ASTNODE_NODE_FUNCS(GenFor, GENFOR)
|
ASTNODE_NODE_FUNCS(GenFor, GENFOR)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstFor : public AstNodeFor {
|
|
||||||
AstFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
|
|
||||||
AstNode* incsp, AstNode* bodysp)
|
|
||||||
: AstNodeFor(fileline, initsp, condp, incsp, bodysp) {
|
|
||||||
}
|
|
||||||
ASTNODE_NODE_FUNCS(For, FOR)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AstRepeat : public AstNodeStmt {
|
struct AstRepeat : public AstNodeStmt {
|
||||||
AstRepeat(FileLine* fileline, AstNode* countp, AstNode* bodysp)
|
AstRepeat(FileLine* fileline, AstNode* countp, AstNode* bodysp)
|
||||||
: AstNodeStmt(fileline) {
|
: AstNodeStmt(fileline) {
|
||||||
@ -1869,16 +1861,18 @@ struct AstRepeat : public AstNodeStmt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct AstWhile : public AstNodeStmt {
|
struct AstWhile : public AstNodeStmt {
|
||||||
AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp)
|
AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp, AstNode* incsp=NULL)
|
||||||
: AstNodeStmt(fileline) {
|
: AstNodeStmt(fileline) {
|
||||||
setOp2p(condp); addNOp3p(bodysp);
|
setOp2p(condp); addNOp3p(bodysp); addNOp4p(incsp);
|
||||||
}
|
}
|
||||||
ASTNODE_NODE_FUNCS(While, WHILE)
|
ASTNODE_NODE_FUNCS(While, WHILE)
|
||||||
AstNode* precondsp() const { return op1p()->castNode(); } // op1= prepare statements for condition (exec every loop)
|
AstNode* precondsp() const { return op1p()->castNode(); } // op1= prepare statements for condition (exec every loop)
|
||||||
AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue
|
AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue
|
||||||
AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop
|
AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop
|
||||||
|
AstNode* incsp() const { return op4p()->castNode(); } // op4= increment (if from a FOR loop)
|
||||||
void addPrecondsp(AstNode* newp) { addOp1p(newp); }
|
void addPrecondsp(AstNode* newp) { addOp1p(newp); }
|
||||||
void addBodysp(AstNode* newp) { addOp3p(newp); }
|
void addBodysp(AstNode* newp) { addOp3p(newp); }
|
||||||
|
void addIncsp(AstNode* newp) { addOp4p(newp); }
|
||||||
virtual bool isGateOptimizable() const { return false; }
|
virtual bool isGateOptimizable() const { return false; }
|
||||||
virtual int instrCount() const { return instrCountBranch(); }
|
virtual int instrCount() const { return instrCountBranch(); }
|
||||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
@ -1887,6 +1881,36 @@ struct AstWhile : public AstNodeStmt {
|
|||||||
virtual void addNextStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
|
virtual void addNextStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AstBreak : public AstNodeStmt {
|
||||||
|
AstBreak(FileLine* fileline)
|
||||||
|
: AstNodeStmt (fileline) {}
|
||||||
|
ASTNODE_NODE_FUNCS(Break, BREAK)
|
||||||
|
virtual string verilogKwd() const { return "break"; };
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstContinue : public AstNodeStmt {
|
||||||
|
AstContinue(FileLine* fileline)
|
||||||
|
: AstNodeStmt (fileline) {}
|
||||||
|
ASTNODE_NODE_FUNCS(Continue, CONTINUE)
|
||||||
|
virtual string verilogKwd() const { return "continue"; };
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstReturn : public AstNodeStmt {
|
||||||
|
AstReturn(FileLine* fileline, AstNode* lhsp=NULL)
|
||||||
|
: AstNodeStmt (fileline) {
|
||||||
|
setNOp1p(lhsp);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(Return, RETURN)
|
||||||
|
virtual string verilogKwd() const { return "return"; };
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
AstNode* lhsp() const { return op1p(); }
|
||||||
|
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
|
||||||
|
};
|
||||||
|
|
||||||
struct AstGenIf : public AstNodeIf {
|
struct AstGenIf : public AstNodeIf {
|
||||||
AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
|
AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
|
||||||
: AstNodeIf(fileline, condp, ifsp, elsesp) {
|
: AstNodeIf(fileline, condp, ifsp, elsesp) {
|
||||||
@ -1901,6 +1925,53 @@ struct AstIf : public AstNodeIf {
|
|||||||
ASTNODE_NODE_FUNCS(If, IF)
|
ASTNODE_NODE_FUNCS(If, IF)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AstJumpLabel : public AstNodeStmt {
|
||||||
|
// Jump point declaration
|
||||||
|
// Separate from AstJumpGo; as a declaration can't be deleted
|
||||||
|
// Parents: {statement list}
|
||||||
|
// Children: {statement list, with JumpGo below}
|
||||||
|
private:
|
||||||
|
int m_labelNum; // Set by V3EmitCSyms to tell final V3Emit what to increment
|
||||||
|
public:
|
||||||
|
AstJumpLabel(FileLine* fl, AstNode* stmtsp)
|
||||||
|
: AstNodeStmt(fl) ,m_labelNum(0) {
|
||||||
|
addNOp1p(stmtsp);
|
||||||
|
}
|
||||||
|
virtual int instrCount() const { return 0; }
|
||||||
|
ASTNODE_NODE_FUNCS(JumpLabel, JUMPLABEL)
|
||||||
|
virtual bool maybePointedTo() const { return true; }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
virtual bool same(AstNode* samep) const { return true; }
|
||||||
|
// op1 = Statements
|
||||||
|
AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements
|
||||||
|
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
|
||||||
|
int labelNum() const { return m_labelNum; }
|
||||||
|
void labelNum(int flag) { m_labelNum=flag; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstJumpGo : public AstNodeStmt {
|
||||||
|
// Jump point; branch up to the JumpLabel
|
||||||
|
// Parents: {statement list}
|
||||||
|
private:
|
||||||
|
AstJumpLabel* m_labelp; // [After V3Jump] Pointer to declaration
|
||||||
|
public:
|
||||||
|
AstJumpGo(FileLine* fl, AstJumpLabel* labelp)
|
||||||
|
: AstNodeStmt(fl) {
|
||||||
|
m_labelp = labelp;
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(JumpGo, JUMPGO)
|
||||||
|
virtual bool broken() const { return !labelp()->brokeExistsAbove(); }
|
||||||
|
virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep()->castJumpLabel(); }
|
||||||
|
virtual void dump(ostream& str);
|
||||||
|
virtual int instrCount() const { return instrCountBranch(); }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(labelp()); }
|
||||||
|
virtual bool same(AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
|
||||||
|
return labelp()==samep->castJumpGo()->labelp(); }
|
||||||
|
virtual bool isGateOptimizable() const { return false; }
|
||||||
|
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
|
||||||
|
AstJumpLabel* labelp() const { return m_labelp; }
|
||||||
|
};
|
||||||
|
|
||||||
struct AstUntilStable : public AstNodeStmt {
|
struct AstUntilStable : public AstNodeStmt {
|
||||||
// Quasi-while loop until given signals are stable
|
// Quasi-while loop until given signals are stable
|
||||||
// Parents: CFUNC (generally)
|
// Parents: CFUNC (generally)
|
||||||
|
@ -135,53 +135,6 @@ private:
|
|||||||
m_modp->addStmtp(nodep);
|
m_modp->addStmtp(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstFor* nodep, AstNUser*) {
|
|
||||||
// So later optimizations don't need to deal with them,
|
|
||||||
// FOR(init,cond,assign,body) -> init,WHILE(cond) { body, assign }
|
|
||||||
AstNode* initsp = nodep->initsp(); if (initsp) initsp->unlinkFrBackWithNext();
|
|
||||||
AstNode* condp = nodep->condp(); if (condp) condp->unlinkFrBackWithNext();
|
|
||||||
AstNode* incsp = nodep->incsp(); if (incsp) incsp->unlinkFrBackWithNext();
|
|
||||||
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
|
||||||
bodysp = bodysp->addNext(incsp);
|
|
||||||
AstNode* newp = new AstWhile(nodep->fileline(),
|
|
||||||
condp,
|
|
||||||
bodysp);
|
|
||||||
initsp = initsp->addNext(newp);
|
|
||||||
newp = initsp;
|
|
||||||
nodep->replaceWith(newp);
|
|
||||||
nodep->deleteTree(); nodep=NULL;
|
|
||||||
}
|
|
||||||
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++);
|
|
||||||
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, AstLogicPacked(), countp->width());
|
|
||||||
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)));
|
|
||||||
AstNode* condp;
|
|
||||||
if (countp->isSigned()) {
|
|
||||||
condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
|
||||||
new AstConst(nodep->fileline(), 0));
|
|
||||||
} else {
|
|
||||||
condp = new AstGt (nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
|
||||||
new AstConst(nodep->fileline(), 0));
|
|
||||||
}
|
|
||||||
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
|
||||||
bodysp = bodysp->addNext(decp);
|
|
||||||
AstNode* newp = new AstWhile(nodep->fileline(),
|
|
||||||
condp,
|
|
||||||
bodysp);
|
|
||||||
initsp = initsp->addNext(newp);
|
|
||||||
newp = initsp;
|
|
||||||
nodep->replaceWith(newp);
|
|
||||||
nodep->deleteTree(); nodep=NULL;
|
|
||||||
}
|
|
||||||
virtual void visit(AstScopeName* nodep, AstNUser*) {
|
virtual void visit(AstScopeName* nodep, AstNUser*) {
|
||||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||||
// Similar code in V3Inline
|
// Similar code in V3Inline
|
||||||
|
@ -52,6 +52,7 @@ private:
|
|||||||
static const int FLAG_IN_TREE = 0x02; // Is in netlist tree
|
static const int FLAG_IN_TREE = 0x02; // Is in netlist tree
|
||||||
static const int FLAG_LINKABLE = 0x04; // Is in netlist tree, can be linked to
|
static const int FLAG_LINKABLE = 0x04; // Is in netlist tree, can be linked to
|
||||||
static const int FLAG_LEAKED = 0x08; // Known to have been leaked
|
static const int FLAG_LEAKED = 0x08; // Known to have been leaked
|
||||||
|
static const int FLAG_UNDER_NOW = 0x10; // Is in tree as parent of current node
|
||||||
public:
|
public:
|
||||||
// METHODS
|
// METHODS
|
||||||
static void deleted(const AstNode* nodep) {
|
static void deleted(const AstNode* nodep) {
|
||||||
@ -74,6 +75,15 @@ public:
|
|||||||
s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED));
|
s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void setUnder(const AstNode* nodep, bool flag) {
|
||||||
|
// Called by BrokenCheckVisitor when each node entered/exited
|
||||||
|
if (!okIfLinkedTo(nodep)) return;
|
||||||
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
|
if (iter!=s_nodes.end()) {
|
||||||
|
iter->second &= ~FLAG_UNDER_NOW;
|
||||||
|
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
static void addInTree(AstNode* nodep, bool linkable) {
|
static void addInTree(AstNode* nodep, bool linkable) {
|
||||||
#ifndef VL_LEAK_CHECKS
|
#ifndef VL_LEAK_CHECKS
|
||||||
if (!linkable) return; // save some time, else the map will get huge!
|
if (!linkable) return; // save some time, else the map will get huge!
|
||||||
@ -111,6 +121,14 @@ public:
|
|||||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
static bool okIfBelow(const AstNode* nodep) {
|
||||||
|
// Must be linked to and below current node
|
||||||
|
if (!okIfLinkedTo(nodep)) return false;
|
||||||
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
|
if (iter == s_nodes.end()) return false;
|
||||||
|
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
static void prepForTree() {
|
static void prepForTree() {
|
||||||
#ifndef VL_LEAK_CHECKS
|
#ifndef VL_LEAK_CHECKS
|
||||||
s_nodes.clear();
|
s_nodes.clear();
|
||||||
@ -152,6 +170,10 @@ bool AstNode::brokeExists() const {
|
|||||||
// Called by node->broken() routines to do table lookup
|
// Called by node->broken() routines to do table lookup
|
||||||
return BrokenTable::okIfLinkedTo(this);
|
return BrokenTable::okIfLinkedTo(this);
|
||||||
}
|
}
|
||||||
|
bool AstNode::brokeExistsAbove() const {
|
||||||
|
// Called by node->broken() routines to do table lookup
|
||||||
|
return BrokenTable::okIfBelow(this);
|
||||||
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
@ -180,6 +202,7 @@ public:
|
|||||||
class BrokenCheckVisitor : public AstNVisitor {
|
class BrokenCheckVisitor : public AstNVisitor {
|
||||||
private:
|
private:
|
||||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||||
|
BrokenTable::setUnder(nodep,true);
|
||||||
if (nodep->broken()) {
|
if (nodep->broken()) {
|
||||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n");
|
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n");
|
||||||
}
|
}
|
||||||
@ -192,6 +215,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
|
BrokenTable::setUnder(nodep,false);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTUCTORS
|
||||||
|
@ -96,6 +96,7 @@ private:
|
|||||||
// ** only when m_warn/m_expensive is set. If state is needed other times,
|
// ** only when m_warn/m_expensive is set. If state is needed other times,
|
||||||
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
||||||
// AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor
|
// AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor
|
||||||
|
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
bool m_params; // If true, propogate parameterized and true numbers only
|
bool m_params; // If true, propogate parameterized and true numbers only
|
||||||
@ -1487,7 +1488,8 @@ private:
|
|||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
if (nodep->condp()->isZero()) {
|
if (nodep->condp()->isZero()) {
|
||||||
UINFO(4,"WHILE(0) => nop "<<nodep<<endl);
|
UINFO(4,"WHILE(0) => nop "<<nodep<<endl);
|
||||||
nodep->unlinkFrBack();
|
if (nodep->precondsp()) nodep->replaceWith(nodep->precondsp());
|
||||||
|
else nodep->unlinkFrBack();
|
||||||
nodep->deleteTree(); nodep=NULL;
|
nodep->deleteTree(); nodep=NULL;
|
||||||
}
|
}
|
||||||
else if (operandBoolShift(nodep->condp())) {
|
else if (operandBoolShift(nodep->condp())) {
|
||||||
@ -1499,6 +1501,30 @@ private:
|
|||||||
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
|
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
|
||||||
virtual void visit(AstNodePreSel* nodep, AstNUser*) {}
|
virtual void visit(AstNodePreSel* nodep, AstNUser*) {}
|
||||||
|
|
||||||
|
//-----
|
||||||
|
// Jump elimination
|
||||||
|
|
||||||
|
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
if (m_expensive) { nodep->labelp()->user4(true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
// Because JumpLabels disable many optimizations,
|
||||||
|
// remove JumpLabels that are not pointed to by any AstJumpGos
|
||||||
|
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
// AstJumpGo's below here that point to this node will set user4
|
||||||
|
if (m_expensive && !nodep->user4()) {
|
||||||
|
UINFO(4,"JUMPLABEL => unused "<<nodep<<endl);
|
||||||
|
AstNode* underp = NULL;
|
||||||
|
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||||
|
if (underp) nodep->replaceWith(underp);
|
||||||
|
else nodep->unlinkFrBack();
|
||||||
|
nodep->deleteTree(); nodep=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----
|
//-----
|
||||||
// Below lines are magic expressions processed by astgen
|
// Below lines are magic expressions processed by astgen
|
||||||
// "AstNODETYPE { # bracket not paren
|
// "AstNODETYPE { # bracket not paren
|
||||||
|
@ -360,12 +360,22 @@ public:
|
|||||||
puts(")); }\n");
|
puts(")); }\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
||||||
|
puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n");
|
||||||
|
}
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
puts("{\n");
|
||||||
|
nodep->stmtsp()->iterateAndNext(*this);
|
||||||
|
puts("}\n");
|
||||||
|
puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n");
|
||||||
|
}
|
||||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||||
nodep->precondsp()->iterateAndNext(*this);
|
nodep->precondsp()->iterateAndNext(*this);
|
||||||
puts("while (");
|
puts("while (");
|
||||||
nodep->condp()->iterateAndNext(*this);
|
nodep->condp()->iterateAndNext(*this);
|
||||||
puts(") {\n");
|
puts(") {\n");
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
||||||
puts("}\n");
|
puts("}\n");
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||||||
ScopeFuncs m_scopeFuncs; // Each {scope,dpiexportfunc}
|
ScopeFuncs m_scopeFuncs; // Each {scope,dpiexportfunc}
|
||||||
V3LanguageWords m_words; // Reserved word detector
|
V3LanguageWords m_words; // Reserved word detector
|
||||||
int m_coverBins; // Coverage bin number
|
int m_coverBins; // Coverage bin number
|
||||||
|
int m_labelNum; // Next label number
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void emitSymHdr();
|
void emitSymHdr();
|
||||||
@ -114,6 +115,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||||
nameCheck(nodep);
|
nameCheck(nodep);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
|
m_labelNum = 0;
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
@ -138,6 +140,10 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||||||
nodep->binNum(m_coverBins++);
|
nodep->binNum(m_coverBins++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
nodep->labelNum(++m_labelNum);
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||||
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
|
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
|
||||||
m_dpis.push_back(nodep);
|
m_dpis.push_back(nodep);
|
||||||
@ -160,6 +166,7 @@ public:
|
|||||||
m_funcp = NULL;
|
m_funcp = NULL;
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_coverBins = 0;
|
m_coverBins = 0;
|
||||||
|
m_labelNum = 0;
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -135,6 +135,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||||||
nodep->rhsp()->iterateAndNext(*this);
|
nodep->rhsp()->iterateAndNext(*this);
|
||||||
if (!m_suppressSemi) puts(";\n");
|
if (!m_suppressSemi) puts(";\n");
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstBreak* nodep, AstNUser*) {
|
||||||
|
putbs("break");
|
||||||
|
if (!m_suppressSemi) puts(";\n");
|
||||||
|
}
|
||||||
virtual void visit(AstSenTree* nodep, AstNUser*) {
|
virtual void visit(AstSenTree* nodep, AstNUser*) {
|
||||||
// AstSenItem is called for dumping in isolation by V3Order
|
// AstSenItem is called for dumping in isolation by V3Order
|
||||||
putfs(nodep,"@(");
|
putfs(nodep,"@(");
|
||||||
@ -180,6 +184,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||||||
puts((string)"// "+nodep->name()+"\n");
|
puts((string)"// "+nodep->name()+"\n");
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstContinue* nodep, AstNUser*) {
|
||||||
|
putbs("continue");
|
||||||
|
if (!m_suppressSemi) puts(";\n");
|
||||||
|
}
|
||||||
virtual void visit(AstCoverDecl*, AstNUser*) {} // N/A
|
virtual void visit(AstCoverDecl*, AstNUser*) {} // N/A
|
||||||
virtual void visit(AstCoverInc*, AstNUser*) {} // N/A
|
virtual void visit(AstCoverInc*, AstNUser*) {} // N/A
|
||||||
virtual void visit(AstCoverToggle*, AstNUser*) {} // N/A
|
virtual void visit(AstCoverToggle*, AstNUser*) {} // N/A
|
||||||
@ -236,6 +244,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||||||
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
|
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
|
||||||
puts(");\n");
|
puts(");\n");
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
||||||
|
putbs("disable "+cvtToStr((void*)(nodep->labelp()))+";\n");
|
||||||
|
}
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
putbs("begin : "+cvtToStr((void*)(nodep))+"\n");
|
||||||
|
if (nodep->stmtsp()) nodep->stmtsp()->iterateChildren(*this);
|
||||||
|
puts("end\n");
|
||||||
|
}
|
||||||
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
||||||
putfs(nodep,nodep->verilogKwd());
|
putfs(nodep,nodep->verilogKwd());
|
||||||
putbs(" (");
|
putbs(" (");
|
||||||
@ -272,6 +288,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||||||
nodep->condp()->iterateAndNext(*this);
|
nodep->condp()->iterateAndNext(*this);
|
||||||
puts(") begin\n");
|
puts(") begin\n");
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
||||||
putfs(nodep,"end\n");
|
putfs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
@ -287,6 +304,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||||||
}
|
}
|
||||||
putqs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstReturn* nodep, AstNUser*) {
|
||||||
|
putfs(nodep,"return ");
|
||||||
|
nodep->lhsp()->iterateAndNext(*this);
|
||||||
|
puts(";\n");
|
||||||
|
}
|
||||||
virtual void visit(AstStop* nodep, AstNUser*) {
|
virtual void visit(AstStop* nodep, AstNUser*) {
|
||||||
putfs(nodep,"$stop;\n");
|
putfs(nodep,"$stop;\n");
|
||||||
}
|
}
|
||||||
|
@ -288,6 +288,7 @@ private:
|
|||||||
// STATE
|
// STATE
|
||||||
LifeState* m_statep; // Current state
|
LifeState* m_statep; // Current state
|
||||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||||
|
bool m_noopt; // Disable optimization of variables in this block
|
||||||
|
|
||||||
// LIFE MAP
|
// LIFE MAP
|
||||||
// For each basic block, we'll make a new map of what variables that if/else is changing
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
||||||
@ -327,7 +328,7 @@ private:
|
|||||||
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
||||||
}
|
}
|
||||||
// Has to be direct assignment without any EXTRACTing.
|
// Has to be direct assignment without any EXTRACTing.
|
||||||
if (nodep->lhsp()->castVarRef() && !m_sideEffect) {
|
if (nodep->lhsp()->castVarRef() && !m_sideEffect && !m_noopt) {
|
||||||
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
|
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||||
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
|
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
|
||||||
m_lifep->simpleAssign(vscp, nodep);
|
m_lifep->simpleAssign(vscp, nodep);
|
||||||
@ -388,6 +389,7 @@ private:
|
|||||||
{
|
{
|
||||||
m_lifep = bodyLifep;
|
m_lifep = bodyLifep;
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
m_lifep = prevLifep;
|
m_lifep = prevLifep;
|
||||||
}
|
}
|
||||||
UINFO(4," joinfor"<<endl);
|
UINFO(4," joinfor"<<endl);
|
||||||
@ -397,6 +399,25 @@ private:
|
|||||||
delete condLifep;
|
delete condLifep;
|
||||||
delete bodyLifep;
|
delete bodyLifep;
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
// As with While's we can't predict if a JumpGo will kill us or not
|
||||||
|
// It's worse though as an IF(..., JUMPGO) may change the control flow.
|
||||||
|
// Just don't optimize blocks with labels; they're rare - so far.
|
||||||
|
LifeBlock* prevLifep = m_lifep;
|
||||||
|
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
|
||||||
|
bool prev_noopt = m_noopt;
|
||||||
|
{
|
||||||
|
m_lifep = bodyLifep;
|
||||||
|
m_noopt = true;
|
||||||
|
nodep->stmtsp()->iterateAndNext(*this);
|
||||||
|
m_lifep = prevLifep;
|
||||||
|
m_noopt = prev_noopt;
|
||||||
|
}
|
||||||
|
UINFO(4," joinjump"<<endl);
|
||||||
|
// For the next assignments, clear any variables that were read or written in the block
|
||||||
|
bodyLifep->lifeToAbove();
|
||||||
|
delete bodyLifep;
|
||||||
|
}
|
||||||
virtual void visit(AstCCall* nodep, AstNUser*) {
|
virtual void visit(AstCCall* nodep, AstNUser*) {
|
||||||
//UINFO(4," CCALL "<<nodep<<endl);
|
//UINFO(4," CCALL "<<nodep<<endl);
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
@ -432,6 +453,7 @@ public:
|
|||||||
UINFO(4," LifeVisitor on "<<nodep<<endl);
|
UINFO(4," LifeVisitor on "<<nodep<<endl);
|
||||||
m_statep = statep;
|
m_statep = statep;
|
||||||
m_sideEffect = false;
|
m_sideEffect = false;
|
||||||
|
m_noopt = false;
|
||||||
{
|
{
|
||||||
m_lifep = new LifeBlock (NULL, m_statep);
|
m_lifep = new LifeBlock (NULL, m_statep);
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
|
227
src/V3LinkJump.cpp
Normal file
227
src/V3LinkJump.cpp
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Replace return/continue with jumps
|
||||||
|
//
|
||||||
|
// Code available from: http://www.veripool.org/verilator
|
||||||
|
//
|
||||||
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-2010 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.
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
// 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
|
||||||
|
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);
|
||||||
|
varp->isSigned(true);
|
||||||
|
varp->dtypep()->isSigned(true);
|
||||||
|
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)));
|
||||||
|
AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->isSigned(true);
|
||||||
|
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;
|
||||||
|
m_loopp = nodep;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
// CONSTUCTORS
|
||||||
|
LinkJumpVisitor(AstNetlist* nodep) {
|
||||||
|
m_modp = NULL;
|
||||||
|
m_ftaskp = NULL;
|
||||||
|
m_loopp = NULL;
|
||||||
|
m_repeatNum = 0;
|
||||||
|
nodep->accept(*this);
|
||||||
|
}
|
||||||
|
virtual ~LinkJumpVisitor() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Task class functions
|
||||||
|
|
||||||
|
void V3LinkJump::linkJump(AstNetlist* nodep) {
|
||||||
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||||
|
LinkJumpVisitor bvisitor (nodep);
|
||||||
|
}
|
37
src/V3LinkJump.h
Normal file
37
src/V3LinkJump.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// -*- C++ -*-
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Replace return/continue with jumps
|
||||||
|
//
|
||||||
|
// Code available from: http://www.veripool.org/verilator
|
||||||
|
//
|
||||||
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-2010 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 _V3LINKJUMP_H_
|
||||||
|
#define _V3LINKJUMP_H_ 1
|
||||||
|
#include "config_build.h"
|
||||||
|
#include "verilatedos.h"
|
||||||
|
#include "V3Error.h"
|
||||||
|
#include "V3Ast.h"
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
class V3LinkJump {
|
||||||
|
public:
|
||||||
|
static void linkJump(AstNetlist* nodep);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
@ -75,6 +75,13 @@ private:
|
|||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstInitial* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
// Initial assignments under function/tasks can just be simple assignments without the initial
|
||||||
|
if (m_ftaskp) {
|
||||||
|
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); nodep=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstVAssert* nodep, AstNUser*) {
|
virtual void visit(AstVAssert* nodep, AstNUser*) {
|
||||||
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
|
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
|
||||||
m_assertp = nodep;
|
m_assertp = nodep;
|
||||||
|
@ -170,6 +170,7 @@ private:
|
|||||||
m_inWhilep = NULL;
|
m_inWhilep = NULL;
|
||||||
startStatement(nodep);
|
startStatement(nodep);
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
m_stmtp = NULL;
|
m_stmtp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||||
|
@ -143,6 +143,7 @@ private:
|
|||||||
|
|
||||||
//=======
|
//=======
|
||||||
// These have proper signedness set when they were created.
|
// These have proper signedness set when they were created.
|
||||||
|
virtual void visit(AstReturn* nodep, AstNUser*) { nodep->iterateChildren(*this); }
|
||||||
virtual void visit(AstNodeDType* nodep, AstNUser*) { nodep->iterateChildren(*this); }
|
virtual void visit(AstNodeDType* nodep, AstNUser*) { nodep->iterateChildren(*this); }
|
||||||
|
|
||||||
// Inherit from others
|
// Inherit from others
|
||||||
|
@ -88,6 +88,7 @@ private:
|
|||||||
bool m_inDlyAssign; ///< Under delayed assignment
|
bool m_inDlyAssign; ///< Under delayed assignment
|
||||||
int m_instrCount; ///< Number of nodes
|
int m_instrCount; ///< Number of nodes
|
||||||
int m_dataCount; ///< Bytes of data
|
int m_dataCount; ///< Bytes of data
|
||||||
|
AstJumpGo* m_jumpp; ///< Jump label we're branching from
|
||||||
// Simulating:
|
// Simulating:
|
||||||
deque<V3Number*> m_numFreeps; ///< List of all numbers free and not in use
|
deque<V3Number*> m_numFreeps; ///< List of all numbers free and not in use
|
||||||
deque<V3Number*> m_numAllps; ///< List of all numbers free and in use
|
deque<V3Number*> m_numAllps; ///< List of all numbers free and in use
|
||||||
@ -230,8 +231,14 @@ private:
|
|||||||
: v3Global.opt.unrollCount();
|
: v3Global.opt.unrollCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jumpingOver(AstNode* nodep) {
|
||||||
|
// True to jump over this node - all visitors must call this up front
|
||||||
|
return (m_jumpp && m_jumpp->labelp()!=nodep);
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
@ -239,6 +246,7 @@ private:
|
|||||||
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
|
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
if (!optimizable()) return; // Accelerate
|
if (!optimizable()) return; // Accelerate
|
||||||
AstNode* vscp = varOrScope(nodep);
|
AstNode* vscp = varOrScope(nodep);
|
||||||
|
|
||||||
@ -285,16 +293,19 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
if (m_scoped) { badNodeType(nodep); return; }
|
if (m_scoped) { badNodeType(nodep); return; }
|
||||||
else { clearOptimizable(nodep,"Language violation: Dotted hierarchical references not allowed in constant functions"); }
|
else { clearOptimizable(nodep,"Language violation: Dotted hierarchical references not allowed in constant functions"); }
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
if (!m_params) { badNodeType(nodep); return; }
|
if (!m_params) { badNodeType(nodep); return; }
|
||||||
if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); }
|
if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); }
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
UINFO(5," IF "<<nodep<<endl);
|
UINFO(5," IF "<<nodep<<endl);
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
if (m_checkOnly) {
|
if (m_checkOnly) {
|
||||||
@ -364,6 +375,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
if (!optimizable()) return; // Accelerate
|
if (!optimizable()) return; // Accelerate
|
||||||
if (nodep->castAssignDly()) {
|
if (nodep->castAssignDly()) {
|
||||||
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
|
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
|
||||||
@ -400,6 +412,7 @@ private:
|
|||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeCase* nodep, AstNUser*) {
|
virtual void visit(AstNodeCase* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
UINFO(5," CASE "<<nodep<<endl);
|
UINFO(5," CASE "<<nodep<<endl);
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
if (m_checkOnly) {
|
if (m_checkOnly) {
|
||||||
@ -436,12 +449,30 @@ private:
|
|||||||
|
|
||||||
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
||||||
// Real handling is in AstNodeCase
|
// Real handling is in AstNodeCase
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstComment*, AstNUser*) {}
|
virtual void visit(AstComment*, AstNUser*) {}
|
||||||
|
|
||||||
|
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
|
checkNodeInfo(nodep);
|
||||||
|
if (!m_checkOnly) {
|
||||||
|
UINFO(5," JUMP GO "<<nodep<<endl);
|
||||||
|
m_jumpp = nodep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
|
checkNodeInfo(nodep);
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
if (m_jumpp && m_jumpp->labelp() == nodep) {
|
||||||
|
UINFO(5," JUMP DONE "<<nodep<<endl);
|
||||||
|
m_jumpp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstStop* nodep, AstNUser*) {
|
virtual void visit(AstStop* nodep, AstNUser*) {
|
||||||
if (m_params) { // This message seems better than an obscure $stop
|
if (m_params) { // This message seems better than an obscure $stop
|
||||||
// The spec says $stop is just ignored, it seems evil to ignore assertions
|
// The spec says $stop is just ignored, it seems evil to ignore assertions
|
||||||
@ -479,6 +510,7 @@ private:
|
|||||||
|
|
||||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||||
// Doing lots of Whiles is slow, so only for parameters
|
// Doing lots of Whiles is slow, so only for parameters
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
UINFO(5," WHILE "<<nodep<<endl);
|
UINFO(5," WHILE "<<nodep<<endl);
|
||||||
if (!m_params) { badNodeType(nodep); return; }
|
if (!m_params) { badNodeType(nodep); return; }
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
@ -489,12 +521,19 @@ private:
|
|||||||
while (1) {
|
while (1) {
|
||||||
UINFO(5," WHILE-ITER "<<nodep<<endl);
|
UINFO(5," WHILE-ITER "<<nodep<<endl);
|
||||||
nodep->precondsp()->iterateAndNext(*this);
|
nodep->precondsp()->iterateAndNext(*this);
|
||||||
|
if (jumpingOver(nodep)) break;
|
||||||
nodep->condp()->iterateAndNext(*this);
|
nodep->condp()->iterateAndNext(*this);
|
||||||
|
if (jumpingOver(nodep)) break;
|
||||||
if (!optimizable()) break;
|
if (!optimizable()) break;
|
||||||
if (!fetchNumber(nodep->condp())->isNeqZero()) {
|
if (!fetchNumber(nodep->condp())->isNeqZero()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
if (jumpingOver(nodep)) break;
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
|
if (jumpingOver(nodep)) break;
|
||||||
|
|
||||||
|
// Prep for next loop
|
||||||
if (loops++ > unrollCount()*16) {
|
if (loops++ > unrollCount()*16) {
|
||||||
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "+cvtToStr(unrollCount()));
|
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "+cvtToStr(unrollCount()));
|
||||||
break;
|
break;
|
||||||
@ -504,6 +543,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstFuncRef* nodep, AstNUser*) {
|
virtual void visit(AstFuncRef* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
UINFO(5," FUNCREF "<<nodep<<endl);
|
UINFO(5," FUNCREF "<<nodep<<endl);
|
||||||
if (!m_params) { badNodeType(nodep); return; }
|
if (!m_params) { badNodeType(nodep); return; }
|
||||||
AstNodeFTask* funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked");
|
AstNodeFTask* funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked");
|
||||||
@ -540,6 +580,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
if (!m_params) { badNodeType(nodep); return; }
|
if (!m_params) { badNodeType(nodep); return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,20 +589,30 @@ private:
|
|||||||
// AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish,
|
// AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish,
|
||||||
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
|
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
|
||||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||||
|
if (jumpingOver(nodep)) return;
|
||||||
badNodeType(nodep);
|
badNodeType(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// MEMBERS - called by constructor
|
||||||
|
void setMode(bool scoped, bool checkOnly, bool params) {
|
||||||
|
m_checkOnly = checkOnly;
|
||||||
|
m_scoped = scoped;
|
||||||
|
m_params = params;
|
||||||
|
}
|
||||||
|
void mainGuts(AstNode* nodep) {
|
||||||
|
nodep->accept(*this);
|
||||||
|
if (m_jumpp) {
|
||||||
|
m_jumpp->v3fatalSrc("JumpGo branched to label that wasn't found");
|
||||||
|
m_jumpp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
SimulateVisitor() {
|
SimulateVisitor() {
|
||||||
setMode(false,false,false);
|
setMode(false,false,false);
|
||||||
clear(); // We reuse this structure in the main loop, so put initializers inside clear()
|
clear(); // We reuse this structure in the main loop, so put initializers inside clear()
|
||||||
}
|
}
|
||||||
void setMode(bool scoped, bool checkOnly, bool params) {
|
|
||||||
m_checkOnly = checkOnly;
|
|
||||||
m_scoped = scoped;
|
|
||||||
m_params = params;
|
|
||||||
}
|
|
||||||
void clear() {
|
void clear() {
|
||||||
m_whyNotOptimizable = "";
|
m_whyNotOptimizable = "";
|
||||||
m_whyNotNodep = NULL;
|
m_whyNotNodep = NULL;
|
||||||
@ -570,6 +621,7 @@ public:
|
|||||||
m_inDlyAssign = false;
|
m_inDlyAssign = false;
|
||||||
m_instrCount = 0;
|
m_instrCount = 0;
|
||||||
m_dataCount = 0;
|
m_dataCount = 0;
|
||||||
|
m_jumpp = NULL;
|
||||||
|
|
||||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||||
AstNode::user2ClearTree(); // user2p() used on entire tree
|
AstNode::user2ClearTree(); // user2p() used on entire tree
|
||||||
@ -580,15 +632,15 @@ public:
|
|||||||
}
|
}
|
||||||
void mainTableCheck (AstNode* nodep) {
|
void mainTableCheck (AstNode* nodep) {
|
||||||
setMode(true/*scoped*/,true/*checking*/, false/*params*/);
|
setMode(true/*scoped*/,true/*checking*/, false/*params*/);
|
||||||
nodep->accept(*this);
|
mainGuts(nodep);
|
||||||
}
|
}
|
||||||
void mainTableEmulate (AstNode* nodep) {
|
void mainTableEmulate (AstNode* nodep) {
|
||||||
setMode(true/*scoped*/,false/*checking*/, false/*params*/);
|
setMode(true/*scoped*/,false/*checking*/, false/*params*/);
|
||||||
nodep->accept(*this);
|
mainGuts(nodep);
|
||||||
}
|
}
|
||||||
void mainParamEmulate (AstNode* nodep) {
|
void mainParamEmulate (AstNode* nodep) {
|
||||||
setMode(false/*scoped*/,false/*checking*/, true/*params*/);
|
setMode(false/*scoped*/,false/*checking*/, true/*params*/);
|
||||||
nodep->accept(*this);
|
mainGuts(nodep);
|
||||||
}
|
}
|
||||||
virtual ~SimulateVisitor() {
|
virtual ~SimulateVisitor() {
|
||||||
for (deque<V3Number*>::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) {
|
for (deque<V3Number*>::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) {
|
||||||
|
@ -1039,7 +1039,7 @@ private:
|
|||||||
// Create output variable
|
// Create output variable
|
||||||
string namePrefix = "__Vfunc_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++);
|
string namePrefix = "__Vfunc_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++);
|
||||||
AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(),
|
AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(),
|
||||||
namePrefix+"__out");
|
namePrefix+"__Vfuncout");
|
||||||
// Create cloned statements
|
// Create cloned statements
|
||||||
if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); }
|
if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); }
|
||||||
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?");
|
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?");
|
||||||
@ -1127,6 +1127,7 @@ private:
|
|||||||
// Body insert just before themselves
|
// Body insert just before themselves
|
||||||
m_insStmtp = NULL; // First thing should be new statement
|
m_insStmtp = NULL; // First thing should be new statement
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
// Done the loop
|
// Done the loop
|
||||||
m_insStmtp = NULL; // Next thing should be new statement
|
m_insStmtp = NULL; // Next thing should be new statement
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,9 @@ private:
|
|||||||
for (AstNode* bodp = bodysp; bodp; bodp=bodp->nextp()) {
|
for (AstNode* bodp = bodysp; bodp; bodp=bodp->nextp()) {
|
||||||
bodySize++;
|
bodySize++;
|
||||||
}
|
}
|
||||||
|
for (AstNode* bodp = incp; bodp; bodp=bodp->nextp()) {
|
||||||
|
bodySize++;
|
||||||
|
}
|
||||||
if (bodySize > v3Global.opt.unrollStmts())
|
if (bodySize > v3Global.opt.unrollStmts())
|
||||||
return cantUnroll(nodep, "too many statements");
|
return cantUnroll(nodep, "too many statements");
|
||||||
}
|
}
|
||||||
@ -195,6 +198,7 @@ private:
|
|||||||
m_ignoreIncp = incp;
|
m_ignoreIncp = incp;
|
||||||
precondsp->iterateAndNext(*this);
|
precondsp->iterateAndNext(*this);
|
||||||
bodysp->iterateAndNext(*this);
|
bodysp->iterateAndNext(*this);
|
||||||
|
incp->iterateAndNext(*this);
|
||||||
m_varModeCheck = false;
|
m_varModeCheck = false;
|
||||||
m_ignoreIncp = NULL;
|
m_ignoreIncp = NULL;
|
||||||
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
||||||
@ -231,6 +235,10 @@ private:
|
|||||||
bodysp->unlinkFrBackWithNext();
|
bodysp->unlinkFrBackWithNext();
|
||||||
stmtsp = stmtsp->addNextNull(bodysp); // Maybe null if no body
|
stmtsp = stmtsp->addNextNull(bodysp); // Maybe null if no body
|
||||||
}
|
}
|
||||||
|
if (incp && !nodep->castGenFor()) { // Generates don't need to increment loop index
|
||||||
|
incp->unlinkFrBackWithNext();
|
||||||
|
stmtsp = stmtsp->addNextNull(incp); // Maybe null if no body
|
||||||
|
}
|
||||||
// If it's a While, then incp is already part of bodysp.
|
// If it's a While, then incp is already part of bodysp.
|
||||||
V3Number loopValue(nodep->fileline(), m_forVarp->width()); // May differ in size from numInitp
|
V3Number loopValue(nodep->fileline(), m_forVarp->width()); // May differ in size from numInitp
|
||||||
loopValue.opAssign(numInit);
|
loopValue.opAssign(numInit);
|
||||||
@ -279,7 +287,6 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Leaving the iterator at the final value is handled by the increment statements being left the final body
|
|
||||||
// Replace the FOR()
|
// Replace the FOR()
|
||||||
if (newbodysp) nodep->replaceWith(newbodysp);
|
if (newbodysp) nodep->replaceWith(newbodysp);
|
||||||
else nodep->unlinkFrBack();
|
else nodep->unlinkFrBack();
|
||||||
@ -303,9 +310,13 @@ private:
|
|||||||
if (nodep->backp()->nextp() == nodep) initp=nodep->backp();
|
if (nodep->backp()->nextp() == nodep) initp=nodep->backp();
|
||||||
// Grab assignment
|
// Grab assignment
|
||||||
AstNode* incp = NULL; // Should be last statement
|
AstNode* incp = NULL; // Should be last statement
|
||||||
|
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp());
|
||||||
|
if (nodep->incsp()) incp = nodep->incsp();
|
||||||
|
else {
|
||||||
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
||||||
if (incp) { V3Const::constifyEdit(incp); incp=NULL; }
|
if (incp) { V3Const::constifyEdit(incp); incp=NULL; }
|
||||||
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed
|
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed
|
||||||
|
}
|
||||||
// And check it
|
// And check it
|
||||||
if (forUnrollCheck(nodep, initp,
|
if (forUnrollCheck(nodep, initp,
|
||||||
nodep->precondsp(), nodep->condp(),
|
nodep->precondsp(), nodep->condp(),
|
||||||
|
@ -84,6 +84,7 @@ private:
|
|||||||
bool m_paramsOnly; // Computing parameter value; limit operation
|
bool m_paramsOnly; // Computing parameter value; limit operation
|
||||||
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
|
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
|
||||||
AstNodeCase* m_casep; // Current case statement CaseItem is under
|
AstNodeCase* m_casep; // Current case statement CaseItem is under
|
||||||
|
AstFunc* m_funcp; // Current function
|
||||||
|
|
||||||
// CLASSES
|
// CLASSES
|
||||||
#define ANYSIZE 0
|
#define ANYSIZE 0
|
||||||
@ -711,6 +712,7 @@ private:
|
|||||||
nodep->precondsp()->iterateAndNext(*this);
|
nodep->precondsp()->iterateAndNext(*this);
|
||||||
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
|
nodep->incsp()->iterateAndNext(*this);
|
||||||
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like an if() condition.
|
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like an if() condition.
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
||||||
@ -924,6 +926,20 @@ private:
|
|||||||
nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width());
|
nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_funcp = NULL;
|
||||||
|
}
|
||||||
|
virtual void visit(AstReturn* nodep, AstNUser* vup) {
|
||||||
|
if (!m_funcp) {
|
||||||
|
if (nodep->lhsp()) { // Return w/o value ok other places
|
||||||
|
nodep->v3error("Return with return value isn't underneath a function");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nodep->lhsp()) {
|
||||||
|
// Function hasn't been widthed, so make it so.
|
||||||
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||||
|
nodep->widthSignedFrom(m_funcp->fvarp());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstFuncRef* nodep, AstNUser* vup) {
|
virtual void visit(AstFuncRef* nodep, AstNUser* vup) {
|
||||||
visit(nodep->castNodeFTaskRef(), vup);
|
visit(nodep->castNodeFTaskRef(), vup);
|
||||||
@ -1028,6 +1044,7 @@ public:
|
|||||||
m_taskDepth = 0;
|
m_taskDepth = 0;
|
||||||
m_cellRangep = NULL;
|
m_cellRangep = NULL;
|
||||||
m_casep = NULL;
|
m_casep = NULL;
|
||||||
|
m_funcp = NULL;
|
||||||
}
|
}
|
||||||
AstNode* mainAcceptEdit(AstNode* nodep) {
|
AstNode* mainAcceptEdit(AstNode* nodep) {
|
||||||
return nodep->acceptSubtreeReturnEdits(*this, WidthVP(ANYSIZE,0,BOTH).p());
|
return nodep->acceptSubtreeReturnEdits(*this, WidthVP(ANYSIZE,0,BOTH).p());
|
||||||
|
@ -52,8 +52,8 @@
|
|||||||
#include "V3File.h"
|
#include "V3File.h"
|
||||||
#include "V3Cdc.h"
|
#include "V3Cdc.h"
|
||||||
#include "V3Gate.h"
|
#include "V3Gate.h"
|
||||||
#include "V3Graph.h"
|
|
||||||
#include "V3GenClk.h"
|
#include "V3GenClk.h"
|
||||||
|
#include "V3Graph.h"
|
||||||
#include "V3Inline.h"
|
#include "V3Inline.h"
|
||||||
#include "V3Inst.h"
|
#include "V3Inst.h"
|
||||||
#include "V3Life.h"
|
#include "V3Life.h"
|
||||||
@ -61,9 +61,10 @@
|
|||||||
#include "V3Link.h"
|
#include "V3Link.h"
|
||||||
#include "V3LinkCells.h"
|
#include "V3LinkCells.h"
|
||||||
#include "V3LinkDot.h"
|
#include "V3LinkDot.h"
|
||||||
|
#include "V3LinkJump.h"
|
||||||
|
#include "V3LinkLValue.h"
|
||||||
#include "V3LinkLevel.h"
|
#include "V3LinkLevel.h"
|
||||||
#include "V3LinkParse.h"
|
#include "V3LinkParse.h"
|
||||||
#include "V3LinkLValue.h"
|
|
||||||
#include "V3LinkResolve.h"
|
#include "V3LinkResolve.h"
|
||||||
#include "V3Localize.h"
|
#include "V3Localize.h"
|
||||||
#include "V3Name.h"
|
#include "V3Name.h"
|
||||||
@ -143,6 +144,8 @@ void process () {
|
|||||||
V3LinkResolve::linkResolve(v3Global.rootp());
|
V3LinkResolve::linkResolve(v3Global.rootp());
|
||||||
// Set Lvalue's in variable refs
|
// Set Lvalue's in variable refs
|
||||||
V3LinkLValue::linkLValue(v3Global.rootp());
|
V3LinkLValue::linkLValue(v3Global.rootp());
|
||||||
|
// Convert return/continue/disable to jumps
|
||||||
|
V3LinkJump::linkJump(v3Global.rootp());
|
||||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree"));
|
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree"));
|
||||||
V3Error::abortIfErrors();
|
V3Error::abortIfErrors();
|
||||||
|
|
||||||
|
@ -382,10 +382,12 @@ escid \\[^ \t\f\r\n]+
|
|||||||
"always_ff" { FL; return yALWAYS; }
|
"always_ff" { FL; return yALWAYS; }
|
||||||
"always_latch" { FL; return yALWAYS; }
|
"always_latch" { FL; return yALWAYS; }
|
||||||
"bit" { FL; return yBIT; }
|
"bit" { FL; return yBIT; }
|
||||||
|
"break" { FL; return yBREAK; }
|
||||||
"byte" { FL; return yBYTE; }
|
"byte" { FL; return yBYTE; }
|
||||||
"chandle" { FL; return yCHANDLE; }
|
"chandle" { FL; return yCHANDLE; }
|
||||||
"clocking" { FL; return yCLOCKING; }
|
"clocking" { FL; return yCLOCKING; }
|
||||||
"context" { FL; return yCONTEXT; }
|
"context" { FL; return yCONTEXT; }
|
||||||
|
"continue" { FL; return yCONTINUE; }
|
||||||
"do" { FL; return yDO; }
|
"do" { FL; return yDO; }
|
||||||
"endclocking" { FL; return yENDCLOCKING; }
|
"endclocking" { FL; return yENDCLOCKING; }
|
||||||
"endpackage" { FL; return yENDPACKAGE; }
|
"endpackage" { FL; return yENDPACKAGE; }
|
||||||
@ -403,6 +405,7 @@ escid \\[^ \t\f\r\n]+
|
|||||||
"priority" { FL; return yPRIORITY; }
|
"priority" { FL; return yPRIORITY; }
|
||||||
"program" { FL; return yPROGRAM; }
|
"program" { FL; return yPROGRAM; }
|
||||||
"pure" { FL; return yPURE; }
|
"pure" { FL; return yPURE; }
|
||||||
|
"return" { FL; return yRETURN; }
|
||||||
"shortint" { FL; return ySHORTINT; }
|
"shortint" { FL; return ySHORTINT; }
|
||||||
"static" { FL; return ySTATIC; }
|
"static" { FL; return ySTATIC; }
|
||||||
"string" { FL; return ySTRING; }
|
"string" { FL; return ySTRING; }
|
||||||
@ -419,10 +422,8 @@ escid \\[^ \t\f\r\n]+
|
|||||||
"bind" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"bind" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"binsof" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"binsof" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"break" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
|
||||||
"class" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"class" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"constraint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"constraint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"continue" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
|
||||||
"covergroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"covergroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"coverpoint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"coverpoint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"cross" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"cross" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
@ -457,7 +458,6 @@ escid \\[^ \t\f\r\n]+
|
|||||||
"randomize" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"randomize" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"randsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"randsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"ref" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"ref" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"return" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
|
||||||
"shortreal" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"shortreal" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"solve" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"solve" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
"struct" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
"struct" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||||
|
@ -264,6 +264,7 @@ class AstSenTree;
|
|||||||
%token<fl> yAUTOMATIC "automatic"
|
%token<fl> yAUTOMATIC "automatic"
|
||||||
%token<fl> yBEGIN "begin"
|
%token<fl> yBEGIN "begin"
|
||||||
%token<fl> yBIT "bit"
|
%token<fl> yBIT "bit"
|
||||||
|
%token<fl> yBREAK "break"
|
||||||
%token<fl> yBUF "buf"
|
%token<fl> yBUF "buf"
|
||||||
%token<fl> yBUFIF0 "bufif0"
|
%token<fl> yBUFIF0 "bufif0"
|
||||||
%token<fl> yBUFIF1 "bufif1"
|
%token<fl> yBUFIF1 "bufif1"
|
||||||
@ -275,6 +276,7 @@ class AstSenTree;
|
|||||||
%token<fl> yCLOCKING "clocking"
|
%token<fl> yCLOCKING "clocking"
|
||||||
%token<fl> yCMOS "cmos"
|
%token<fl> yCMOS "cmos"
|
||||||
%token<fl> yCONTEXT "context"
|
%token<fl> yCONTEXT "context"
|
||||||
|
%token<fl> yCONTINUE "continue"
|
||||||
%token<fl> yCOVER "cover"
|
%token<fl> yCOVER "cover"
|
||||||
%token<fl> yDEFAULT "default"
|
%token<fl> yDEFAULT "default"
|
||||||
%token<fl> yDEFPARAM "defparam"
|
%token<fl> yDEFPARAM "defparam"
|
||||||
@ -340,6 +342,7 @@ class AstSenTree;
|
|||||||
%token<fl> yRCMOS "rcmos"
|
%token<fl> yRCMOS "rcmos"
|
||||||
%token<fl> yREG "reg"
|
%token<fl> yREG "reg"
|
||||||
%token<fl> yREPEAT "repeat"
|
%token<fl> yREPEAT "repeat"
|
||||||
|
%token<fl> yRETURN "return"
|
||||||
%token<fl> yRNMOS "rnmos"
|
%token<fl> yRNMOS "rnmos"
|
||||||
%token<fl> yRPMOS "rpmos"
|
%token<fl> yRPMOS "rpmos"
|
||||||
%token<fl> yRTRAN "rtran"
|
%token<fl> yRTRAN "rtran"
|
||||||
@ -1436,6 +1439,7 @@ genvar_iteration<nodep>: // ==IEEE: genvar_iteration
|
|||||||
| varRefBase yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); }
|
| varRefBase yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); }
|
||||||
| varRefBase yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); }
|
| varRefBase yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); }
|
||||||
// // inc_or_dec_operator
|
// // inc_or_dec_operator
|
||||||
|
// When support ++ as a real AST type, maybe AstWhile::precondsp() becomes generic AstMathStmt?
|
||||||
| yP_PLUSPLUS varRefBase { $$ = new AstAssign($1,$2,new AstAdd ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
| yP_PLUSPLUS varRefBase { $$ = new AstAssign($1,$2,new AstAdd ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||||
| yP_MINUSMINUS varRefBase { $$ = new AstAssign($1,$2,new AstSub ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
| yP_MINUSMINUS varRefBase { $$ = new AstAssign($1,$2,new AstSub ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||||
| varRefBase yP_PLUSPLUS { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
| varRefBase yP_PLUSPLUS { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
||||||
@ -1831,7 +1835,7 @@ statement_item<nodep>: // IEEE: statement_item
|
|||||||
| statementVerilatorPragmas { $$ = $1; }
|
| statementVerilatorPragmas { $$ = $1; }
|
||||||
//
|
//
|
||||||
// // IEEE: disable_statement
|
// // IEEE: disable_statement
|
||||||
//UNSUP yDISABLE hierarchical_identifier/*task_or_block*/ ';' { UNSUP }
|
//UNSUP yDISABLE idAny/*hierarchical_identifier-task_or_block*/ ';' { UNSUP }
|
||||||
//UNSUP yDISABLE yFORK ';' { UNSUP }
|
//UNSUP yDISABLE yFORK ';' { UNSUP }
|
||||||
// // IEEE: event_trigger
|
// // IEEE: event_trigger
|
||||||
//UNSUP yP_MINUSGT hierarchical_identifier/*event*/ ';' { UNSUP }
|
//UNSUP yP_MINUSGT hierarchical_identifier/*event*/ ';' { UNSUP }
|
||||||
@ -1842,15 +1846,15 @@ statement_item<nodep>: // IEEE: statement_item
|
|||||||
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile($1,$3,$5);}
|
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile($1,$3,$5);}
|
||||||
// // for's first ';' is in for_initalization
|
// // for's first ';' is in for_initalization
|
||||||
| yFOR '(' for_initialization expr ';' for_stepE ')' stmtBlock
|
| yFOR '(' for_initialization expr ';' for_stepE ')' stmtBlock
|
||||||
{ $$ = new AstBegin($1,"",$3); $3->addNext(new AstFor($1,NULL,$4,$6,$8));}
|
{ $$ = new AstBegin($1,"",$3); $3->addNext(new AstWhile($1, $4,$8,$6)); }
|
||||||
| yDO stmtBlock yWHILE '(' expr ')' { $$ = $2->cloneTree(true); $$->addNext(new AstWhile($1,$5,$2));}
|
| yDO stmtBlock yWHILE '(' expr ')' { $$ = $2->cloneTree(true); $$->addNext(new AstWhile($1,$5,$2));}
|
||||||
//UNSUP yFOREACH '(' idClassForeach/*array_id[loop_variables]*/ ')' stmt { UNSUP }
|
//UNSUP yFOREACH '(' idClassForeach/*array_id[loop_variables]*/ ')' stmt { UNSUP }
|
||||||
//
|
//
|
||||||
// // IEEE: jump_statement
|
// // IEEE: jump_statement
|
||||||
//UNSUP yRETURN ';' { UNSUP }
|
| yRETURN ';' { $$ = new AstReturn($1); }
|
||||||
//UNSUP yRETURN expr ';' { UNSUP }
|
| yRETURN expr ';' { $$ = new AstReturn($1,$2); }
|
||||||
//UNSUP yBREAK ';' { UNSUP }
|
| yBREAK ';' { $$ = new AstBreak($1); }
|
||||||
//UNSUP yCONTINUE ';' { UNSUP }
|
| yCONTINUE ';' { $$ = new AstContinue($1); }
|
||||||
//
|
//
|
||||||
//UNSUP par_block { $$ = $1; }
|
//UNSUP par_block { $$ = $1; }
|
||||||
// // IEEE: procedural_timing_control_statement + procedural_timing_control
|
// // IEEE: procedural_timing_control_statement + procedural_timing_control
|
||||||
|
18
test_regress/t/t_for_break.pl
Executable file
18
test_regress/t/t_for_break.pl
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 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.
|
||||||
|
|
||||||
|
compile (
|
||||||
|
);
|
||||||
|
|
||||||
|
execute (
|
||||||
|
check_finished=>1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
134
test_regress/t/t_for_break.v
Normal file
134
test_regress/t/t_for_break.v
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2009 by Wilson Snyder.
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
integer cyc=0;
|
||||||
|
reg [63:0] crc;
|
||||||
|
reg [63:0] sum;
|
||||||
|
|
||||||
|
// Take CRC data and apply to testblock inputs
|
||||||
|
wire [3:0] l_stop = crc[3:0];
|
||||||
|
wire [3:0] l_break = crc[7:4];
|
||||||
|
wire [3:0] l_continue = crc[11:8];
|
||||||
|
|
||||||
|
/*AUTOWIRE*/
|
||||||
|
|
||||||
|
wire [15:0] out0 = Test0(l_stop, l_break, l_continue);
|
||||||
|
wire [15:0] out1 = Test1(l_stop, l_break, l_continue);
|
||||||
|
wire [15:0] out2 = Test2(l_stop, l_break, l_continue);
|
||||||
|
wire [15:0] out3 = Test3(l_stop, l_break, l_continue);
|
||||||
|
|
||||||
|
// Aggregate outputs into a single result vector
|
||||||
|
wire [63:0] result = {out3,out2,out1,out0};
|
||||||
|
|
||||||
|
// Test loop
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
`ifdef TEST_VERBOSE
|
||||||
|
$write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result);
|
||||||
|
`endif
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||||
|
sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]};
|
||||||
|
if (cyc==0) begin
|
||||||
|
// Setup
|
||||||
|
crc <= 64'h5aef0c8d_d70a4497;
|
||||||
|
sum <= 64'h0;
|
||||||
|
end
|
||||||
|
else if (cyc<10) begin
|
||||||
|
sum <= 64'h0;
|
||||||
|
end
|
||||||
|
else if (cyc<90) begin
|
||||||
|
if (out0!==out1) $stop;
|
||||||
|
if (out0!==out2) $stop;
|
||||||
|
if (out0!==out3) $stop;
|
||||||
|
end
|
||||||
|
else if (cyc==99) begin
|
||||||
|
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
|
||||||
|
if (crc !== 64'hc77bb9b3784ea091) $stop;
|
||||||
|
// What checksum will we end up with (above print should match)
|
||||||
|
`define EXPECTED_SUM 64'h293e9f9798e97da0
|
||||||
|
if (sum !== `EXPECTED_SUM) $stop;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function [15:0] Test0;
|
||||||
|
input [3:0] loop_stop;
|
||||||
|
input [3:0] loop_break;
|
||||||
|
input [3:0] loop_continue;
|
||||||
|
integer i;
|
||||||
|
reg broken;
|
||||||
|
|
||||||
|
Test0 = 0;
|
||||||
|
broken = 0;
|
||||||
|
begin
|
||||||
|
for (i=1; i<20; i=i+1) begin
|
||||||
|
if (!broken) begin
|
||||||
|
Test0 = Test0 + 1;
|
||||||
|
if (i[3:0] != loop_continue) begin // continue
|
||||||
|
if (i[3:0] == loop_break) begin
|
||||||
|
broken = 1'b1;
|
||||||
|
end
|
||||||
|
if (!broken) begin
|
||||||
|
Test0 = Test0 + i[15:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function [15:0] Test1;
|
||||||
|
input [3:0] loop_stop;
|
||||||
|
input [3:0] loop_break;
|
||||||
|
input [3:0] loop_continue;
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
// Placeholder
|
||||||
|
return Test0(loop_stop,loop_break,loop_continue);
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function [15:0] Test2;
|
||||||
|
input [3:0] loop_stop;
|
||||||
|
input [3:0] loop_break;
|
||||||
|
input [3:0] loop_continue;
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
Test2 = 0;
|
||||||
|
begin
|
||||||
|
for (i=1; i<20; i=i+1) begin
|
||||||
|
Test2 = Test2 + 1;
|
||||||
|
if (i[3:0] == loop_continue) continue;
|
||||||
|
if (i[3:0] == loop_break) break;
|
||||||
|
Test2 = Test2 + i[15:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function [15:0] Test3;
|
||||||
|
input [3:0] loop_stop;
|
||||||
|
input [3:0] loop_break;
|
||||||
|
input [3:0] loop_continue;
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
Test3 = 0;
|
||||||
|
begin
|
||||||
|
for (i=1; i<20; i=i+1) begin
|
||||||
|
Test3 = Test3 + 1;
|
||||||
|
if (i[3:0] == loop_continue) continue;
|
||||||
|
// return, IE jump to end-of-function optionally setting return value
|
||||||
|
if (i[3:0] == loop_break) return Test3;
|
||||||
|
Test3 = Test3 + i[15:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
endmodule
|
@ -10,6 +10,7 @@ module t;
|
|||||||
localparam P5 = f_while(7);
|
localparam P5 = f_while(7);
|
||||||
localparam P16 = f_for(P4);
|
localparam P16 = f_for(P4);
|
||||||
localparam P18 = f_case(P4);
|
localparam P18 = f_case(P4);
|
||||||
|
localparam P6 = f_return(P4);
|
||||||
localparam P3 = 3;
|
localparam P3 = 3;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
@ -19,6 +20,7 @@ module t;
|
|||||||
if (P3 !== 3) $stop;
|
if (P3 !== 3) $stop;
|
||||||
if (P4 !== 4) $stop;
|
if (P4 !== 4) $stop;
|
||||||
if (P5 !== 5) $stop;
|
if (P5 !== 5) $stop;
|
||||||
|
if (P6 !== 6) $stop;
|
||||||
if (P8 !== 8) $stop;
|
if (P8 !== 8) $stop;
|
||||||
if (P16 !== 16) $stop;
|
if (P16 !== 16) $stop;
|
||||||
if (P18 !== 18) $stop;
|
if (P18 !== 18) $stop;
|
||||||
@ -69,4 +71,17 @@ module t;
|
|||||||
default: f_case = 99;
|
default: f_case = 99;
|
||||||
endcase
|
endcase
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function integer f_return(input [31:0] a);
|
||||||
|
integer out = 2;
|
||||||
|
while (1) begin
|
||||||
|
out = out+1;
|
||||||
|
if (a>1) break;
|
||||||
|
end
|
||||||
|
while (1) begin
|
||||||
|
out = out+1;
|
||||||
|
if (a>1) return 2+out;
|
||||||
|
end
|
||||||
|
f_return = 0;
|
||||||
|
endfunction
|
||||||
endmodule
|
endmodule
|
||||||
|
@ -13,7 +13,10 @@ module t (/*AUTOARG*/);
|
|||||||
repeat (0) $stop;
|
repeat (0) $stop;
|
||||||
repeat (-1) $stop;
|
repeat (-1) $stop;
|
||||||
negcnt = 'sb111;
|
negcnt = 'sb111;
|
||||||
|
// Not all commercial simulators agree on the below stopping or not
|
||||||
|
// verilator lint_off WIDTH
|
||||||
repeat (negcnt) $stop;
|
repeat (negcnt) $stop;
|
||||||
|
// verilator lint_on WIDTH
|
||||||
repeat (5) begin
|
repeat (5) begin
|
||||||
repeat (2) begin
|
repeat (2) begin
|
||||||
times = times + 1;
|
times = times + 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user