// -*- C++ -*- //************************************************************************* // DESCRIPTION: Verilator: Simulate code to determine output values/variables // // 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. // //************************************************************************* // // void example_usage() { // SimulateVisitor simvis (false, false); // simvis.clear(); // // Set all inputs to the constant // for (deque::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { // simvis.newNumber(invscp, #); // } // // Simulate // simvis.main(nodep); // // Read outputs // for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { // V3Number* outnump = simvis.fetchOutNumberNull(outvscp); // //************************************************************************* #ifndef _V3SIMULATE_H_ #define _V3SIMULATE_H_ 1 #include "config_build.h" #include "verilatedos.h" #include "V3Error.h" #include "V3Ast.h" #include "V3Width.h" #include "V3Task.h" #include //============================================================================ //###################################################################### // Simulate class functions class SimulateVisitor : public AstNVisitor { // Simulate a node tree, returning value of variables // Two major operating modes: // Test the tree to see if it is conformant // Given a set of input values, find the output values // Both are done in this same visitor to reduce risk; if a visitor // is missing, we will simply not apply the optimization, rather then bomb. private: // NODE STATE // Cleared on each always/assignw AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; AstUser3InUse m_inuser3; // Checking: // AstVarScope::user1() -> VarUsage. Set true to indicate tracking as lvalue/rvalue // Simulating: // AstVarScope::user3() -> V3Number*. Input value of variable or node (and output for non-delayed assignments) // AstVarScope::user2() -> V3Number*. Output value of variable (delayed assignments) enum VarUsage { VU_NONE=0, VU_LV=1, VU_RV=2, VU_LVDLY=4 }; // STATE // Major mode bool m_checkOnly; ///< Checking only (no simulation) mode bool m_scoped; ///< Running with AstVarScopes instead of AstVars bool m_params; ///< Doing parameter propagation // Checking: string m_whyNotOptimizable; ///< String explaining why not optimizable or NULL to optimize AstNode* m_whyNotNodep; ///< First node not optimizable bool m_anyAssignDly; ///< True if found a delayed assignment bool m_anyAssignComb; ///< True if found a non-delayed assignment bool m_inDlyAssign; ///< Under delayed assignment int m_instrCount; ///< Number of nodes int m_dataCount; ///< Bytes of data // Simulating: deque m_numFreeps; ///< List of all numbers free and not in use deque m_numAllps; ///< List of all numbers free and in use // Note level 8&9 include debugging each simulation value static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // Checking METHODS public: /// Call other-this function on all new var references virtual void varRefCb(AstVarRef* nodep) {} void clearOptimizable(AstNode* nodep/*null ok*/, const string& why) { // Something bad found. optimizable() will return false, // and fetchNumber should not be called or it may assert. if (!m_whyNotNodep) { m_whyNotNodep = nodep; if (debug()>=5) { UINFO(0,"Clear optimizable: "<width()<width(nodep->width()); nump->fileline(nodep->fileline()); nump->setLong(value); // We do support more than 32 bit numbers, just valuep=0 in that case } else { //UINFO(7,"Num New "<width()<fileline(), nodep->width(), value); m_numAllps.push_back(nump); } return nump; } public: V3Number* newNumber(AstNode* nodep, uint32_t value=0) { // Set a constant value for this node if (!nodep->user3p()) { V3Number* nump = allocNumber(nodep, value); setNumber(nodep, nump); return nump; } else { return (fetchNumber(nodep)); } } V3Number* newOutNumber(AstNode* nodep, uint32_t value=0) { // Set a constant value for this node if (!nodep->user2p()) { V3Number* nump = allocNumber(nodep, value); setOutNumber(nodep, nump); return nump; } else { return (fetchOutNumber(nodep)); } } V3Number* fetchNumberNull(AstNode* nodep) { return ((V3Number*)nodep->user3p()); } V3Number* fetchOutNumberNull(AstNode* nodep) { return ((V3Number*)nodep->user2p()); } V3Number* fetchNumber(AstNode* nodep) { V3Number* nump = fetchNumberNull(nodep); if (!nump) nodep->v3fatalSrc("No value found for node."); //UINFO(9," fetch num "<<*nump<<" on "<v3fatalSrc("No value found for node."); return nump; } private: inline void setNumber(AstNode* nodep, const V3Number* nump) { UINFO(9," set num "<<*nump<<" on "<user3p((AstNUser*)nump); } inline void setOutNumber(AstNode* nodep, const V3Number* nump) { UINFO(9," set num "<<*nump<<" on "<user2p((AstNUser*)nump); } void checkNodeInfo(AstNode* nodep) { if (m_checkOnly) { m_instrCount += nodep->instrCount(); m_dataCount += nodep->width(); } if (!nodep->isPredictOptimizable()) { //UINFO(9," !predictopt "<prettyTypeName()<varScopep(); else vscp = nodep->varp(); if (!vscp) nodep->v3fatalSrc("Not linked"); return vscp; } int unrollCount() { return m_params ? v3Global.opt.unrollCount()*16 : v3Global.opt.unrollCount(); } // VISITORS virtual void visit(AstAlways* nodep, AstNUser*) { checkNodeInfo(nodep); nodep->iterateChildren(*this); } virtual void visit(AstSenTree* nodep, AstNUser*) { // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } virtual void visit(AstVarRef* nodep, AstNUser*) { if (!optimizable()) return; // Accelerate AstNode* vscp = varOrScope(nodep); // We can't have non-delayed assignments with same value on LHS and RHS // as we don't figure out variable ordering. // Delayed is OK though, as we'll decode the next state separately. if (!nodep->varp()->dtypeSkipRefp()->castBasicDType()) clearOptimizable(nodep,"Array references/not basic"); if (nodep->lvalue()) { if (m_inDlyAssign) { if (!(vscp->user1() & VU_LVDLY)) { vscp->user1( vscp->user1() | VU_LVDLY); if (m_checkOnly) varRefCb (nodep); } } else { // nondly asn if (!(vscp->user1() & VU_LV)) { if (!m_params && (vscp->user1() & VU_RV)) clearOptimizable(nodep,"Var read & write"); vscp->user1( vscp->user1() | VU_LV); if (m_checkOnly) varRefCb (nodep); } } } else { if (!(vscp->user1() & VU_RV)) { if (!m_params && (vscp->user1() & VU_LV)) clearOptimizable(nodep,"Var write & read"); vscp->user1( vscp->user1() | VU_RV); if (m_checkOnly) varRefCb (nodep); } } if (!m_checkOnly && optimizable()) { // simulating if (nodep->lvalue()) { nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor."); } else { // Return simulation value - copy by reference instead of value for speed V3Number* nump = fetchNumberNull(vscp); if (!nump) { if (m_params) { clearOptimizable(nodep,"Language violation: reference to non-function-local variable"); } else { nodep->v3fatalSrc("Variable value should have been set before any visitor called."); } nump = allocNumber(nodep, 0); // Any value; just so recover from error } setNumber(nodep, nump); } } } virtual void visit(AstVarXRef* nodep, AstNUser*) { 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 (!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*) { UINFO(5," IF "<iterateChildren(*this); } else { nodep->condp()->iterateAndNext(*this); if (optimizable()) { if (fetchNumber(nodep->condp())->isNeqZero()) { nodep->ifsp()->iterateAndNext(*this); } else { nodep->elsesp()->iterateAndNext(*this); } } } } virtual void visit(AstConst* nodep, AstNUser*) { checkNodeInfo(nodep); if (!m_checkOnly && optimizable()) { setNumber(nodep, &(nodep->num())); } } virtual void visit(AstNodeUniop* nodep, AstNUser*) { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); nodep->iterateChildren(*this); if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp())); } } virtual void visit(AstNodeBiop* nodep, AstNUser*) { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); nodep->iterateChildren(*this); if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp())); } } virtual void visit(AstNodeTriop* nodep, AstNUser*) { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); nodep->iterateChildren(*this); if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp()), *fetchNumber(nodep->thsp())); } } virtual void visit(AstNodeCond* nodep, AstNUser*) { // We could use above visit(AstNodeTriop), but it's slower even O(n^2) to evaluate // both sides when we really only need to evaluate one side. if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { nodep->iterateChildren(*this); } else { nodep->condp()->accept(*this); if (optimizable()) { if (fetchNumber(nodep->condp())->isNeqZero()) { nodep->expr1p()->accept(*this); newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); } else { nodep->expr2p()->accept(*this); newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); } } } } virtual void visit(AstNodeAssign* nodep, AstNUser*) { if (!optimizable()) return; // Accelerate if (nodep->castAssignDly()) { if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns"); m_anyAssignDly = true; m_inDlyAssign = true; } else { if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non dly assigns"); m_anyAssignComb = true; } if (!nodep->lhsp()->castVarRef()) { clearOptimizable(nodep, "LHS isn't simple variable"); } else if (m_checkOnly) { nodep->iterateChildren(*this); } else if (optimizable()) { nodep->rhsp()->iterateAndNext(*this); if (optimizable()) { AstNode* vscp = varOrScope(nodep->lhsp()->castVarRef()); // Copy by value, not reference, as we don't want a=a+1 to get right results if (nodep->castAssignDly()) { // Don't do setNumber, as value isn't yet visible to following statements newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); } else { newNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); } } } m_inDlyAssign = false; } virtual void visit(AstBegin* nodep, AstNUser*) { checkNodeInfo(nodep); nodep->iterateChildren(*this); } virtual void visit(AstNodeCase* nodep, AstNUser*) { UINFO(5," CASE "<iterateChildren(*this); } else if (optimizable()) { nodep->exprp()->iterateAndNext(*this); bool hit = false; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { if (!itemp->isDefault()) { for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { if (hit) break; ep->iterateAndNext(*this); if (optimizable()) { V3Number match (nodep->fileline(), 1); match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep)); if (match.isNeqZero()) { itemp->bodysp()->iterateAndNext(*this); hit = true; } } } } } // Else default match for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { if (hit) break; if (!hit && itemp->isDefault()) { itemp->bodysp()->iterateAndNext(*this); hit = true; } } } } virtual void visit(AstCaseItem* nodep, AstNUser*) { // Real handling is in AstNodeCase checkNodeInfo(nodep); nodep->iterateChildren(*this); } virtual void visit(AstComment*, AstNUser*) {} virtual void visit(AstStop* nodep, AstNUser*) { if (m_params) { // This message seems better than an obscure $stop // The spec says $stop is just ignored, it seems evil to ignore assertions clearOptimizable(nodep,"$stop executed during function constification; maybe indicates assertion firing"); } checkNodeInfo(nodep); } virtual void visit(AstNodeFor* nodep, AstNUser*) { // Doing lots of Whiles is slow, so only for parameters UINFO(5," FOR "<iterateChildren(*this); } else if (optimizable()) { int loops = 0; nodep->initsp()->iterateAndNext(*this); while (1) { UINFO(5," FOR-ITER "<condp()->iterateAndNext(*this); if (!optimizable()) break; if (!fetchNumber(nodep->condp())->isNeqZero()) { break; } nodep->bodysp()->iterateAndNext(*this); nodep->incsp()->iterateAndNext(*this); 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; } } } } virtual void visit(AstWhile* nodep, AstNUser*) { // Doing lots of Whiles is slow, so only for parameters UINFO(5," WHILE "<iterateChildren(*this); } else if (optimizable()) { int loops = 0; while (1) { UINFO(5," WHILE-ITER "<precondsp()->iterateAndNext(*this); nodep->condp()->iterateAndNext(*this); if (!optimizable()) break; if (!fetchNumber(nodep->condp())->isNeqZero()) { break; } nodep->bodysp()->iterateAndNext(*this); 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; } } } } virtual void visit(AstFuncRef* nodep, AstNUser*) { UINFO(5," FUNCREF "<taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked"); if (m_params) { V3Width::widthParamsEdit(funcp); } funcp=NULL; // Make sure we've sized the function funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked"); // Apply function call values to function // Note we'd need a stack if we allowed recursive functions! V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { AstVar* portp = it->first; AstNode* pinp = it->second; if (pinp==NULL) { // Too few arguments in function call - ignore it } else { if (portp->isOutput()) { clearOptimizable(portp,"Language violation: Outputs not allowed in constant functions"); return; } // Evaluate pin value pinp->accept(*this); // Apply value to the function if (!m_checkOnly && optimizable()) { newNumber(portp)->opAssign(*fetchNumber(pinp)); } } } // Evaluate the function funcp->accept(*this); if (!m_checkOnly && optimizable()) { // Grab return value from output variable (if it's a function) if (!funcp->fvarp()) nodep->v3fatalSrc("Function reference points at non-function"); newNumber(nodep)->opAssign(*fetchNumber(funcp->fvarp())); } } virtual void visit(AstVar* nodep, AstNUser*) { if (!m_params) { badNodeType(nodep); return; } } // default // These types are definately not reducable // AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish, // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt virtual void visit(AstNode* nodep, AstNUser*) { badNodeType(nodep); } 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; m_anyAssignComb = false; m_anyAssignDly = false; m_inDlyAssign = false; m_instrCount = 0; m_dataCount = 0; AstNode::user1ClearTree(); // user1p() used on entire tree AstNode::user2ClearTree(); // user2p() used on entire tree AstNode::user3ClearTree(); // user3p() used on entire tree // Move all allocated numbers to the free pool m_numFreeps = m_numAllps; } void mainTableCheck (AstNode* nodep) { setMode(true/*scoped*/,true/*checking*/, false/*params*/); nodep->accept(*this); } void mainTableEmulate (AstNode* nodep) { setMode(true/*scoped*/,false/*checking*/, false/*params*/); nodep->accept(*this); } void mainParamEmulate (AstNode* nodep) { setMode(false/*scoped*/,false/*checking*/, true/*params*/); nodep->accept(*this); } virtual ~SimulateVisitor() { for (deque::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) { delete (*it); } m_numFreeps.clear(); m_numAllps.clear(); } }; #endif // Guard