// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Break always into sensitivity active domains // // 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. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* // V3Active's Transformations: // // Note this can be called multiple times. // Across all ACTIVES // SenTrees are now under each ACTIVE statement, we want them global: // Find SenTree in under global TopScope, or create it there // Move SenTree the global SenTree // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3ActiveTop.h" #include "V3Ast.h" #include "V3SenTree.h" #include "V3Const.h" //###################################################################### // Active class functions class ActiveTopVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist // AstNode::user() bool. True if processed // Each call to V3Const::constify // AstNode::user4() Used by V3Const::constify, called below AstUser1InUse m_inuser1; // STATE AstTopScope* m_topscopep; // Top scope for adding sentrees under SenTreeFinder m_finder; // Find global sentree's and add them // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstTopScope* nodep) VL_OVERRIDE { m_topscopep = nodep; m_finder.main(m_topscopep); iterateChildren(nodep); m_topscopep = NULL; } virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { // Create required actives and add to module // We can start ordering at a module, or a scope UINFO(4, " MOD " << nodep << endl); iterateChildren(nodep); } virtual void visit(AstActive* nodep) VL_OVERRIDE { UINFO(4, " ACTIVE " << nodep << endl); // Remove duplicate clocks and such; sensesp() may change! V3Const::constifyExpensiveEdit(nodep); AstSenTree* sensesp = nodep->sensesp(); UASSERT_OBJ(sensesp, nodep, "NULL"); if (sensesp->sensesp() && VN_IS(sensesp->sensesp(), SenItem) && VN_CAST(sensesp->sensesp(), SenItem)->isNever()) { // Never executing. Kill it. UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep, "Never senitem should be alone, else the never should be eliminated."); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } // Copy combo tree to settlement tree with duplicated statements if (sensesp->hasCombo()) { AstSenTree* newsentreep = new AstSenTree( nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle())); AstActive* newp = new AstActive(nodep->fileline(), "settle", newsentreep); newp->sensesStorep(newsentreep); if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true)); nodep->addNextHere(newp); } // Move the SENTREE for each active up to the global level. // This way we'll easily see what clock domains are identical AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp); UINFO(4, " lookdone\n"); if (wantp != sensesp) { // Move the active's contents to the other active UINFO(4, " merge active " << sensesp << " into " << wantp << endl); if (nodep->sensesStorep()) { UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep, "sensesStore should have been deleted earlier if different"); sensesp->unlinkFrBack(); // There may be other references to same sense tree, // we'll be removing all references when we get to them, // but don't dangle our pointer yet! pushDeletep(sensesp); } nodep->sensesp(wantp); } // No need to do statements under it, they're already moved. // iterateChildren(nodep); } virtual void visit(AstNodeProcedure* nodep) VL_OVERRIDE { // LCOV_EXCL_LINE nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAssignAlias* nodep) VL_OVERRIDE { // LCOV_EXCL_LINE nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAssignW* nodep) VL_OVERRIDE { // LCOV_EXCL_LINE nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAlwaysPublic* nodep) VL_OVERRIDE { // LCOV_EXCL_LINE nodep->v3fatalSrc("Node should have been under ACTIVE"); } //-------------------- virtual void visit(AstNodeMath*) VL_OVERRIDE {} // Accelerate virtual void visit(AstVarScope*) VL_OVERRIDE {} // Accelerate virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } public: // CONSTRUCTORS explicit ActiveTopVisitor(AstNetlist* nodep) : m_topscopep(NULL) { iterate(nodep); } virtual ~ActiveTopVisitor() {} }; //###################################################################### // Active class functions void V3ActiveTop::activeTopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ActiveTopVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); }