diff --git a/Changes b/Changes index 22b31b142..04f059847 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.80*** +*** Support "break", "continue", "return". + **** Skip SystemC tests if not installed. [Iztok Jeras] **** Fix make uninstall, bug216. [Iztok Jeras] diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 87e993dfa..d6add2ccc 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -194,8 +194,9 @@ RAW_OBJS = \ V3Link.o \ V3LinkCells.o \ V3LinkDot.o \ - V3LinkLevel.o \ + V3LinkJump.o \ V3LinkLValue.o \ + V3LinkLevel.o \ V3LinkParse.o \ V3LinkResolve.o \ V3Localize.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index 7440bf28e..2a5e4cd5f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -761,6 +761,7 @@ public: 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 bool brokeExists() const; + bool brokeExistsAbove() const; // CONSTRUCTORS virtual ~AstNode(); @@ -870,6 +871,7 @@ public: 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 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 replaceWith(AstNode* newp); // Replace current node in tree with new node void v3errorEnd(ostringstream& str) const; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d4e726113..39e868aef 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -494,6 +494,12 @@ void AstDisplay::dump(ostream& str) { this->AstNode::dump(str); //str<<" "<AstNode::dump(str); + str<<" -> "; + if (labelp()) { labelp()->dump(str); } + else { str<<"%Error:UNLINKED"; } +} void AstEnumItemRef::dump(ostream& str) { this->AstNode::dump(str); str<<" -> "; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 907cba462..0ee8d4512 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1846,14 +1846,6 @@ struct AstGenFor : public AstNodeFor { 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 { AstRepeat(FileLine* fileline, AstNode* countp, AstNode* bodysp) : AstNodeStmt(fileline) { @@ -1869,16 +1861,18 @@ struct AstRepeat : 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) { - setOp2p(condp); addNOp3p(bodysp); + setOp2p(condp); addNOp3p(bodysp); addNOp4p(incsp); } ASTNODE_NODE_FUNCS(While, WHILE) 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* 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 addBodysp(AstNode* newp) { addOp3p(newp); } + void addIncsp(AstNode* newp) { addOp4p(newp); } virtual bool isGateOptimizable() const { return false; } virtual int instrCount() const { return instrCountBranch(); } 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 }; +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 { AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp) : AstNodeIf(fileline, condp, ifsp, elsesp) { @@ -1901,6 +1925,53 @@ struct AstIf : public AstNodeIf { 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 { // Quasi-while loop until given signals are stable // Parents: CFUNC (generally) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 3402ad63d..218389b25 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -135,53 +135,6 @@ private: 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*) { // If there's a %m in the display text, we add a special node that will contain the name() // Similar code in V3Inline diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index ea261b0d0..eb8f21f5a 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -52,6 +52,7 @@ private: 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_LEAKED = 0x08; // Known to have been leaked + static const int FLAG_UNDER_NOW = 0x10; // Is in tree as parent of current node public: // METHODS static void deleted(const AstNode* nodep) { @@ -74,6 +75,15 @@ public: 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) { #ifndef VL_LEAK_CHECKS if (!linkable) return; // save some time, else the map will get huge! @@ -111,6 +121,14 @@ public: if (!(iter->second & FLAG_LINKABLE)) return false; 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() { #ifndef VL_LEAK_CHECKS s_nodes.clear(); @@ -152,6 +170,10 @@ bool AstNode::brokeExists() const { // Called by node->broken() routines to do table lookup 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 { private: virtual void visit(AstNode* nodep, AstNUser*) { + BrokenTable::setUnder(nodep,true); if (nodep->broken()) { nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n"); } @@ -192,6 +215,7 @@ private: } } nodep->iterateChildren(*this); + BrokenTable::setUnder(nodep,false); } public: // CONSTUCTORS diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 757ea1091..9a1040b50 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -96,6 +96,7 @@ private: // ** 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. // AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor + // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label // STATE bool m_params; // If true, propogate parameterized and true numbers only @@ -1487,7 +1488,8 @@ private: nodep->iterateChildren(*this); if (nodep->condp()->isZero()) { UINFO(4,"WHILE(0) => nop "<unlinkFrBack(); + if (nodep->precondsp()) nodep->replaceWith(nodep->precondsp()); + else nodep->unlinkFrBack(); nodep->deleteTree(); nodep=NULL; } 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 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 "<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 // "AstNODETYPE { # bracket not paren diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 6e43c6c55..a2f3ef4fa 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -360,12 +360,22 @@ public: 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*) { nodep->precondsp()->iterateAndNext(*this); puts("while ("); nodep->condp()->iterateAndNext(*this); puts(") {\n"); nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop puts("}\n"); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index e753e8538..5f1626ff0 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -75,6 +75,7 @@ class EmitCSyms : EmitCBaseVisitor { ScopeFuncs m_scopeFuncs; // Each {scope,dpiexportfunc} V3LanguageWords m_words; // Reserved word detector int m_coverBins; // Coverage bin number + int m_labelNum; // Next label number // METHODS void emitSymHdr(); @@ -114,6 +115,7 @@ class EmitCSyms : EmitCBaseVisitor { virtual void visit(AstNodeModule* nodep, AstNUser*) { nameCheck(nodep); m_modp = nodep; + m_labelNum = 0; nodep->iterateChildren(*this); m_modp = NULL; } @@ -138,6 +140,10 @@ class EmitCSyms : EmitCBaseVisitor { nodep->binNum(m_coverBins++); } } + virtual void visit(AstJumpLabel* nodep, AstNUser*) { + nodep->labelNum(++m_labelNum); + nodep->iterateChildren(*this); + } virtual void visit(AstCFunc* nodep, AstNUser*) { if (nodep->dpiImport() || nodep->dpiExportWrapper()) { m_dpis.push_back(nodep); @@ -160,6 +166,7 @@ public: m_funcp = NULL; m_modp = NULL; m_coverBins = 0; + m_labelNum = 0; nodep->accept(*this); } }; diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 208965cd0..253f26153 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -135,6 +135,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { nodep->rhsp()->iterateAndNext(*this); if (!m_suppressSemi) puts(";\n"); } + virtual void visit(AstBreak* nodep, AstNUser*) { + putbs("break"); + if (!m_suppressSemi) puts(";\n"); + } virtual void visit(AstSenTree* nodep, AstNUser*) { // AstSenItem is called for dumping in isolation by V3Order putfs(nodep,"@("); @@ -180,6 +184,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { puts((string)"// "+nodep->name()+"\n"); 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(AstCoverInc*, 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); 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*) { putfs(nodep,nodep->verilogKwd()); putbs(" ("); @@ -272,6 +288,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { nodep->condp()->iterateAndNext(*this); puts(") begin\n"); nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop putfs(nodep,"end\n"); } @@ -287,6 +304,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } 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*) { putfs(nodep,"$stop;\n"); } diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 5e0a48391..a9b9a6b2d 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -288,6 +288,7 @@ private: // STATE LifeState* m_statep; // Current state bool m_sideEffect; // Side effects discovered in assign RHS + bool m_noopt; // Disable optimization of variables in this block // LIFE MAP // 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 } // 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(); if (!vscp) nodep->v3fatalSrc("Scope lost on variable"); m_lifep->simpleAssign(vscp, nodep); @@ -388,6 +389,7 @@ private: { m_lifep = bodyLifep; nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); m_lifep = prevLifep; } UINFO(4," joinfor"<stmtsp()->iterateAndNext(*this); + m_lifep = prevLifep; + m_noopt = prev_noopt; + } + UINFO(4," joinjump"<lifeToAbove(); + delete bodyLifep; + } virtual void visit(AstCCall* nodep, AstNUser*) { //UINFO(4," CCALL "<iterateChildren(*this); @@ -432,6 +453,7 @@ public: UINFO(4," LifeVisitor on "<accept(*this); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp new file mode 100644 index 000000000..3869c840c --- /dev/null +++ b/src/V3LinkJump.cpp @@ -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 +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3LinkJump.h" +#include "V3Ast.h" + +//###################################################################### + +class LinkJumpVisitor : public AstNVisitor { +private: + // TYPES + typedef vector 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 "<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," "<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__<<": "<iterateChildren(*this); 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*) { if (m_assertp) nodep->v3error("Assert not allowed under another assert"); m_assertp = nodep; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index f1acb36df..399233e90 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -170,6 +170,7 @@ private: m_inWhilep = NULL; startStatement(nodep); nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); m_stmtp = NULL; } virtual void visit(AstNodeAssign* nodep, AstNUser*) { diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index b6776dbbd..ecd821b04 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -143,6 +143,7 @@ private: //======= // 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); } // Inherit from others diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 7169075c8..80804cd43 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -88,6 +88,7 @@ private: bool m_inDlyAssign; ///< Under delayed assignment int m_instrCount; ///< Number of nodes int m_dataCount; ///< Bytes of data + AstJumpGo* m_jumpp; ///< Jump label we're branching from // Simulating: deque m_numFreeps; ///< List of all numbers free and not in use deque m_numAllps; ///< List of all numbers free and in use @@ -230,8 +231,14 @@ private: : 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 virtual void visit(AstAlways* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; checkNodeInfo(nodep); nodep->iterateChildren(*this); } @@ -239,6 +246,7 @@ private: // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } virtual void visit(AstVarRef* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; if (!optimizable()) return; // Accelerate AstNode* vscp = varOrScope(nodep); @@ -285,16 +293,19 @@ private: } } virtual void visit(AstVarXRef* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; if (m_scoped) { badNodeType(nodep); return; } else { clearOptimizable(nodep,"Language violation: Dotted hierarchical references not allowed in constant functions"); } } virtual void visit(AstNodeFTask* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; if (!m_params) { badNodeType(nodep); return; } if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); } checkNodeInfo(nodep); nodep->iterateChildren(*this); } virtual void visit(AstNodeIf* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; UINFO(5," IF "<castAssignDly()) { if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns"); @@ -400,6 +412,7 @@ private: nodep->iterateChildren(*this); } virtual void visit(AstNodeCase* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; UINFO(5," CASE "<iterateChildren(*this); } virtual void visit(AstComment*, AstNUser*) {} + virtual void visit(AstJumpGo* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; + checkNodeInfo(nodep); + if (!m_checkOnly) { + UINFO(5," JUMP GO "<iterateChildren(*this); + if (m_jumpp && m_jumpp->labelp() == nodep) { + UINFO(5," JUMP DONE "<precondsp()->iterateAndNext(*this); + if (jumpingOver(nodep)) break; nodep->condp()->iterateAndNext(*this); + if (jumpingOver(nodep)) break; if (!optimizable()) break; if (!fetchNumber(nodep->condp())->isNeqZero()) { break; } nodep->bodysp()->iterateAndNext(*this); + if (jumpingOver(nodep)) break; + nodep->incsp()->iterateAndNext(*this); + if (jumpingOver(nodep)) break; + + // Prep for next loop if (loops++ > unrollCount()*16) { clearOptimizable(nodep, "Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "+cvtToStr(unrollCount())); break; @@ -504,6 +543,7 @@ private: } virtual void visit(AstFuncRef* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; UINFO(5," FUNCREF "<taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked"); @@ -540,6 +580,7 @@ private: } virtual void visit(AstVar* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; if (!m_params) { badNodeType(nodep); return; } } @@ -548,20 +589,30 @@ private: // AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish, // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt virtual void visit(AstNode* nodep, AstNUser*) { + if (jumpingOver(nodep)) return; 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: // CONSTRUCTORS SimulateVisitor() { setMode(false,false,false); 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() { m_whyNotOptimizable = ""; m_whyNotNodep = NULL; @@ -570,6 +621,7 @@ public: m_inDlyAssign = false; m_instrCount = 0; m_dataCount = 0; + m_jumpp = NULL; AstNode::user1ClearTree(); // user1p() used on entire tree AstNode::user2ClearTree(); // user2p() used on entire tree @@ -580,15 +632,15 @@ public: } void mainTableCheck (AstNode* nodep) { setMode(true/*scoped*/,true/*checking*/, false/*params*/); - nodep->accept(*this); + mainGuts(nodep); } void mainTableEmulate (AstNode* nodep) { setMode(true/*scoped*/,false/*checking*/, false/*params*/); - nodep->accept(*this); + mainGuts(nodep); } void mainParamEmulate (AstNode* nodep) { setMode(false/*scoped*/,false/*checking*/, true/*params*/); - nodep->accept(*this); + mainGuts(nodep); } virtual ~SimulateVisitor() { for (deque::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) { diff --git a/src/V3Task.cpp b/src/V3Task.cpp index ec80728ff..4c80fc67a 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1039,7 +1039,7 @@ private: // Create output variable string namePrefix = "__Vfunc_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++); AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(), - namePrefix+"__out"); + namePrefix+"__Vfuncout"); // Create cloned statements if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); } if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?"); @@ -1127,6 +1127,7 @@ private: // Body insert just before themselves m_insStmtp = NULL; // First thing should be new statement nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); // Done the loop m_insStmtp = NULL; // Next thing should be new statement } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 099b2b076..5b2f001b5 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -185,6 +185,9 @@ private: for (AstNode* bodp = bodysp; bodp; bodp=bodp->nextp()) { bodySize++; } + for (AstNode* bodp = incp; bodp; bodp=bodp->nextp()) { + bodySize++; + } if (bodySize > v3Global.opt.unrollStmts()) return cantUnroll(nodep, "too many statements"); } @@ -195,6 +198,7 @@ private: m_ignoreIncp = incp; precondsp->iterateAndNext(*this); bodysp->iterateAndNext(*this); + incp->iterateAndNext(*this); m_varModeCheck = false; m_ignoreIncp = NULL; if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); @@ -231,6 +235,10 @@ private: bodysp->unlinkFrBackWithNext(); 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. V3Number loopValue(nodep->fileline(), m_forVarp->width()); // May differ in size from numInitp 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() if (newbodysp) nodep->replaceWith(newbodysp); else nodep->unlinkFrBack(); @@ -303,9 +310,13 @@ private: if (nodep->backp()->nextp() == nodep) initp=nodep->backp(); // Grab assignment AstNode* incp = NULL; // Should be last statement - for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} - if (incp) { V3Const::constifyEdit(incp); incp=NULL; } - for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed + if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); + if (nodep->incsp()) incp = nodep->incsp(); + else { + for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} + if (incp) { V3Const::constifyEdit(incp); incp=NULL; } + for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed + } // And check it if (forUnrollCheck(nodep, initp, nodep->precondsp(), nodep->condp(), diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a8a893c96..510ee54d9 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -84,6 +84,7 @@ private: bool m_paramsOnly; // Computing parameter value; limit operation AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations AstNodeCase* m_casep; // Current case statement CaseItem is under + AstFunc* m_funcp; // Current function // CLASSES #define ANYSIZE 0 @@ -711,6 +712,7 @@ private: nodep->precondsp()->iterateAndNext(*this); nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); nodep->bodysp()->iterateAndNext(*this); + nodep->incsp()->iterateAndNext(*this); widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like an if() condition. } virtual void visit(AstNodeIf* nodep, AstNUser*) { @@ -924,6 +926,20 @@ private: 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) { visit(nodep->castNodeFTaskRef(), vup); @@ -1028,6 +1044,7 @@ public: m_taskDepth = 0; m_cellRangep = NULL; m_casep = NULL; + m_funcp = NULL; } AstNode* mainAcceptEdit(AstNode* nodep) { return nodep->acceptSubtreeReturnEdits(*this, WidthVP(ANYSIZE,0,BOTH).p()); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index f6aca4110..afcdb70a1 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -52,8 +52,8 @@ #include "V3File.h" #include "V3Cdc.h" #include "V3Gate.h" -#include "V3Graph.h" #include "V3GenClk.h" +#include "V3Graph.h" #include "V3Inline.h" #include "V3Inst.h" #include "V3Life.h" @@ -61,9 +61,10 @@ #include "V3Link.h" #include "V3LinkCells.h" #include "V3LinkDot.h" +#include "V3LinkJump.h" +#include "V3LinkLValue.h" #include "V3LinkLevel.h" #include "V3LinkParse.h" -#include "V3LinkLValue.h" #include "V3LinkResolve.h" #include "V3Localize.h" #include "V3Name.h" @@ -143,6 +144,8 @@ void process () { V3LinkResolve::linkResolve(v3Global.rootp()); // Set Lvalue's in variable refs V3LinkLValue::linkLValue(v3Global.rootp()); + // Convert return/continue/disable to jumps + V3LinkJump::linkJump(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree")); V3Error::abortIfErrors(); diff --git a/src/verilog.l b/src/verilog.l index fb195862e..3f34ad786 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -382,10 +382,12 @@ escid \\[^ \t\f\r\n]+ "always_ff" { FL; return yALWAYS; } "always_latch" { FL; return yALWAYS; } "bit" { FL; return yBIT; } + "break" { FL; return yBREAK; } "byte" { FL; return yBYTE; } "chandle" { FL; return yCHANDLE; } "clocking" { FL; return yCLOCKING; } "context" { FL; return yCONTEXT; } + "continue" { FL; return yCONTINUE; } "do" { FL; return yDO; } "endclocking" { FL; return yENDCLOCKING; } "endpackage" { FL; return yENDPACKAGE; } @@ -403,6 +405,7 @@ escid \\[^ \t\f\r\n]+ "priority" { FL; return yPRIORITY; } "program" { FL; return yPROGRAM; } "pure" { FL; return yPURE; } + "return" { FL; return yRETURN; } "shortint" { FL; return ySHORTINT; } "static" { FL; return ySTATIC; } "string" { FL; return ySTRING; } @@ -419,10 +422,8 @@ escid \\[^ \t\f\r\n]+ "bind" { 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); } - "break" { 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); } - "continue" { 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); } "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); } "randsequence" { 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); } "solve" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "struct" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } diff --git a/src/verilog.y b/src/verilog.y index 7118af90b..58ba70cb4 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -264,6 +264,7 @@ class AstSenTree; %token yAUTOMATIC "automatic" %token yBEGIN "begin" %token yBIT "bit" +%token yBREAK "break" %token yBUF "buf" %token yBUFIF0 "bufif0" %token yBUFIF1 "bufif1" @@ -275,6 +276,7 @@ class AstSenTree; %token yCLOCKING "clocking" %token yCMOS "cmos" %token yCONTEXT "context" +%token yCONTINUE "continue" %token yCOVER "cover" %token yDEFAULT "default" %token yDEFPARAM "defparam" @@ -340,6 +342,7 @@ class AstSenTree; %token yRCMOS "rcmos" %token yREG "reg" %token yREPEAT "repeat" +%token yRETURN "return" %token yRNMOS "rnmos" %token yRPMOS "rpmos" %token yRTRAN "rtran" @@ -1436,6 +1439,7 @@ genvar_iteration: // ==IEEE: genvar_iteration | 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)); } // // 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_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")))); } @@ -1831,7 +1835,7 @@ statement_item: // IEEE: statement_item | statementVerilatorPragmas { $$ = $1; } // // // IEEE: disable_statement - //UNSUP yDISABLE hierarchical_identifier/*task_or_block*/ ';' { UNSUP } + //UNSUP yDISABLE idAny/*hierarchical_identifier-task_or_block*/ ';' { UNSUP } //UNSUP yDISABLE yFORK ';' { UNSUP } // // IEEE: event_trigger //UNSUP yP_MINUSGT hierarchical_identifier/*event*/ ';' { UNSUP } @@ -1842,15 +1846,15 @@ statement_item: // IEEE: statement_item | yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile($1,$3,$5);} // // for's first ';' is in for_initalization | 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));} //UNSUP yFOREACH '(' idClassForeach/*array_id[loop_variables]*/ ')' stmt { UNSUP } // // // IEEE: jump_statement - //UNSUP yRETURN ';' { UNSUP } - //UNSUP yRETURN expr ';' { UNSUP } - //UNSUP yBREAK ';' { UNSUP } - //UNSUP yCONTINUE ';' { UNSUP } + | yRETURN ';' { $$ = new AstReturn($1); } + | yRETURN expr ';' { $$ = new AstReturn($1,$2); } + | yBREAK ';' { $$ = new AstBreak($1); } + | yCONTINUE ';' { $$ = new AstContinue($1); } // //UNSUP par_block { $$ = $1; } // // IEEE: procedural_timing_control_statement + procedural_timing_control diff --git a/test_regress/t/t_for_break.pl b/test_regress/t/t_for_break.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_for_break.pl @@ -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; diff --git a/test_regress/t/t_for_break.v b/test_regress/t/t_for_break.v new file mode 100644 index 000000000..d7bb3aa27 --- /dev/null +++ b/test_regress/t/t_for_break.v @@ -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 diff --git a/test_regress/t/t_func_const.v b/test_regress/t/t_func_const.v index 6b0b71090..771218d65 100644 --- a/test_regress/t/t_func_const.v +++ b/test_regress/t/t_func_const.v @@ -10,6 +10,7 @@ module t; localparam P5 = f_while(7); localparam P16 = f_for(P4); localparam P18 = f_case(P4); + localparam P6 = f_return(P4); localparam P3 = 3; initial begin @@ -19,6 +20,7 @@ module t; if (P3 !== 3) $stop; if (P4 !== 4) $stop; if (P5 !== 5) $stop; + if (P6 !== 6) $stop; if (P8 !== 8) $stop; if (P16 !== 16) $stop; if (P18 !== 18) $stop; @@ -69,4 +71,17 @@ module t; default: f_case = 99; endcase 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 diff --git a/test_regress/t/t_repeat.v b/test_regress/t/t_repeat.v index 6c04f97db..b49007203 100644 --- a/test_regress/t/t_repeat.v +++ b/test_regress/t/t_repeat.v @@ -13,7 +13,10 @@ module t (/*AUTOARG*/); repeat (0) $stop; repeat (-1) $stop; negcnt = 'sb111; + // Not all commercial simulators agree on the below stopping or not + // verilator lint_off WIDTH repeat (negcnt) $stop; + // verilator lint_on WIDTH repeat (5) begin repeat (2) begin times = times + 1;