// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Add temporaries, such as for changed nodes // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2020 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 "V3Global.h" #include "V3Ast.h" #include "V3Changed.h" #include "V3EmitCBase.h" #include #include #include //###################################################################### 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); UASSERT_OBJ(returnp, m_scopetopp, "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); VL_DO_DANGLING(returnp->deleteTree(), 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) VL_OVERRIDE { newChangeDet(); } virtual void visit(AstPackArrayDType* nodep) VL_OVERRIDE { newChangeDet(); } virtual void visit(AstUnpackArrayDType* nodep) VL_OVERRIDE { 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); iterate(nodep->subDTypep()->skipRefp()); m_varEqnp->deleteTree(); m_newLvEqnp->deleteTree(); m_newRvEqnp->deleteTree(); m_varEqnp = origVEp; m_newLvEqnp = origNLEp; m_newRvEqnp = origNREp; } } virtual void visit(AstNodeUOrStructDType* nodep) VL_OVERRIDE { 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()->prettyNameQ()); } } virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); 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()->prettyNameQ()); } public: // CONSTRUCTORS 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); } iterate(vscp->dtypep()->skipRefp()); m_varEqnp->deleteTree(); m_newLvEqnp->deleteTree(); m_newRvEqnp->deleteTree(); } virtual ~ChangedInsertVisitor() {} VL_UNCOPYABLE(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 VL_DEBUG_FUNC; // Declare debug() void genChangeDet(AstVarScope* vscp) { vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<prettyNameQ()); ChangedInsertVisitor visitor (vscp, m_statep); } // VISITORS virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { UINFO(4," MOD "<isTop()) { m_statep->m_topModp = nodep; } iterateChildren(nodep); } virtual void visit(AstTopScope* nodep) VL_OVERRIDE { UINFO(4," TS "<scopep(); UASSERT_OBJ(scopep, nodep, "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)); iterateChildren(nodep); } virtual void visit(AstVarScope* nodep) VL_OVERRIDE { if (nodep->isCircular()) { UINFO(8," CIRC "<user1SetOnce()) { genChangeDet(nodep); } } } virtual void visit(AstNodeMath* nodep) VL_OVERRIDE { // Short-circuit } //-------------------- // Default: Just iterate virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } public: // CONSTRUCTORS ChangedVisitor(AstNetlist* nodep, ChangedState* statep) { m_statep = statep; iterate(nodep); } virtual ~ChangedVisitor() {} }; //###################################################################### // Changed class functions void V3Changed::changedAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 3); }