// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Add temporaries, such as for changed nodes // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2018 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. // //************************************************************************* // V3Changed's Transformations: // // Each module: // Each combo block // For each variable that comes from combo block and is generated AFTER a usage // Add __Vlast_{var} to local section, init to current value (just use array?) // Change = if any var != last. // If a signal is used as a clock in this module or any // module *below*, and it isn't a input to this module, // we need to indicate a new clock has been created. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include "V3Global.h" #include "V3Ast.h" #include "V3Changed.h" #include "V3EmitCBase.h" //###################################################################### class ChangedState { public: // STATE AstNodeModule* m_topModp; // Top module AstScope* m_scopetopp; // Scope under TOPSCOPE AstCFunc* m_chgFuncp; // Change function we're building AstCFunc* m_tlChgFuncp; // Top level change function we're building int m_numStmts; // Number of statements added to m_chgFuncp int m_funcNum; // Number of change functions emitted ChangedState() { m_topModp = NULL; m_chgFuncp = NULL; m_scopetopp = NULL; m_tlChgFuncp = NULL; m_numStmts = 0; m_funcNum = 0; } ~ChangedState() {} void maybeCreateChgFuncp() { // Don't create an extra function call if splitting is disabled if (!v3Global.opt.outputSplitCFuncs()) { m_chgFuncp = m_tlChgFuncp; return; } if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) { m_chgFuncp = new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), m_scopetopp, "QData"); m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); m_chgFuncp->symProlog(true); m_chgFuncp->declPrivate(true); m_scopetopp->addActivep(m_chgFuncp); // Add a top call to it AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp); callp->argTypes("vlSymsp"); if (!m_tlChgFuncp->stmtsp()) { m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp)); } else { AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function"); // This is currently using AstLogOr which will shortcut the evaluation if // any function returns true. This is likely what we want and is similar to the logic already in use // inside V3EmitC, however, it also means that verbose logging may miss to print change detect variables. AstNode* newp = new AstCReturn(m_scopetopp->fileline(), new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack())); returnp->replaceWith(newp); returnp->deleteTree(); VL_DANGLING(returnp); } m_numStmts = 0; } } }; //###################################################################### // Utility visitor to find elements to be compared class ChangedInsertVisitor : public AstNVisitor { private: // STATE ChangedState* m_statep; // Shared state across visitors AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting AstNode* m_varEqnp; // Original var's equation to get var value AstNode* m_newLvEqnp; // New var's equation to read value AstNode* m_newRvEqnp; // New var's equation to set value uint32_t m_detects; // # detects created // CONSTANTS enum MiscConsts { DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error // Ok to increase this, but may result in much slower model }; void newChangeDet() { if (++m_detects > DETECTARRAY_MAX_INDEXES) { m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<prettyName()<warnMore() <<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<maybeCreateChgFuncp(); AstChangeDet* changep = new AstChangeDet (m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false); m_statep->m_chgFuncp->addStmtsp(changep); AstAssign* initp = new AstAssign (m_vscp->fileline(), m_newLvEqnp->cloneTree(true), m_varEqnp->cloneTree(true)); m_statep->m_chgFuncp->addFinalsp(initp); EmitCBaseCounterVisitor visitor(initp); m_statep->m_numStmts += visitor.count(); } virtual void visit(AstBasicDType* nodep) { newChangeDet(); } virtual void visit(AstPackArrayDType* nodep) { newChangeDet(); } virtual void visit(AstUnpackArrayDType* nodep) { for (int index=0; index < nodep->elementsConst(); ++index) { AstNode* origVEp = m_varEqnp; AstNode* origNLEp = m_newLvEqnp; AstNode* origNREp = m_newRvEqnp; m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index); m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index); m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index); nodep->subDTypep()->skipRefp()->accept(*this); m_varEqnp->deleteTree(); m_newLvEqnp->deleteTree(); m_newRvEqnp->deleteTree(); m_varEqnp = origVEp; m_newLvEqnp = origNLEp; m_newRvEqnp = origNREp; } } virtual void visit(AstNodeClassDType* nodep) { if (nodep->packedUnsup()) { newChangeDet(); } else { if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-class-"); m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable (probably with UNOPTFLAT warning suppressed): "<varp()->prettyName()); } } virtual void visit(AstNode* nodep) { nodep->iterateChildren(*this); if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-"); m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable (probably with UNOPTFLAT warning suppressed): "<varp()->prettyName()); } public: // CONSTUCTORS ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) { m_statep = statep; m_vscp = vscp; m_detects = 0; { AstVar* varp = m_vscp->varp(); string newvarname = "__Vchglast__"+m_vscp->scopep()->nameDotless()+"__"+varp->shortName(); // Create: VARREF(_last) // ASSIGN(VARREF(_last), VARREF(var)) // ... // CHANGEDET(VARREF(_last), VARREF(var)) AstVar* newvarp = new AstVar (varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); m_statep->m_topModp->addStmtp(newvarp); m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp); m_statep->m_scopetopp->addVarp(m_newvscp); m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false); m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true); m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false); } vscp->dtypep()->skipRefp()->accept(*this); m_varEqnp->deleteTree(); m_newLvEqnp->deleteTree(); m_newRvEqnp->deleteTree(); } virtual ~ChangedInsertVisitor() {} }; //###################################################################### // Changed state, as a visitor of each AstNode class ChangedVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: // AstVarScope::user1() -> bool. True indicates processed AstUser1InUse m_inuser1; // STATE ChangedState* m_statep; // Shared state across visitors // METHODS static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } void genChangeDet(AstVarScope* vscp) { vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<isTop()) { m_statep->m_topModp = nodep; } nodep->iterateChildren(*this); } virtual void visit(AstTopScope* nodep) { UINFO(4," TS "<scopep(); if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?"); m_statep->m_scopetopp = scopep; // Create a wrapper change detection function that calls each change detection function m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData"); m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); m_statep->m_tlChgFuncp->symProlog(true); m_statep->m_tlChgFuncp->declPrivate(true); m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp); // Each change detection function needs at least one AstChangeDet // to ensure that V3EmitC outputs the necessary code. m_statep->maybeCreateChgFuncp(); m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false)); nodep->iterateChildren(*this); } virtual void visit(AstVarScope* nodep) { if (nodep->isCircular()) { UINFO(8," CIRC "<user1SetOnce()) { genChangeDet(nodep); } } } virtual void visit(AstNodeMath* nodep) { // Short-circuit } //-------------------- // Default: Just iterate virtual void visit(AstNode* nodep) { nodep->iterateChildren(*this); } public: // CONSTUCTORS ChangedVisitor(AstNetlist* nodep, ChangedState* statep) { m_statep = statep; nodep->accept(*this); } virtual ~ChangedVisitor() {} }; //###################################################################### // Changed class functions void V3Changed::changedAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 3); }