diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 45f7433f9..353b4606a 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -514,10 +514,9 @@ private: visitAlways(nodep, nodep->sensesp(), nodep->keyword()); } virtual void visit(AstAlwaysPostponed* nodep) override { - if (!nodep->bodysp()) { // Empty always. Remove it now. - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - return; - } + // Might be empty with later optimizations, so this assertion can be removed, + // but for now it is guaranteed to be not empty. + UASSERT_OBJ(nodep->bodysp(), nodep, "Should not be empty"); visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS); } virtual void visit(AstAlwaysPublic* nodep) override { diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 180f087a0..ed4b1d729 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -119,30 +119,26 @@ private: pushDeletep(nodep); // Delete it later, AstActives still pointing to it } virtual void visit(AstActive* nodep) override { - // Careful if adding variables here, ACTIVES can be under other ACTIVES - // Need to save and restore any member state in AstUntilStable block + UASSERT_OBJ(nodep->hasClocked(), nodep, "Should have been converted by V3Sched"); + UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty"); + VNRelinker relinker; nodep->unlinkFrBack(&relinker); - UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty"); AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext(); - if (nodep->hasClocked()) { - // Create 'if' statement, if needed - if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) { - clearLastSen(); - m_lastSenp = nodep->sensesp(); - // Make a new if statement - m_lastIfp = makeActiveIf(m_lastSenp); - relinker.relink(m_lastIfp); - } - // Move statements to if - m_lastIfp->addIfsp(stmtsp); - } else if (nodep->hasCombo()) { + + // Create 'if' statement, if needed + if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) { clearLastSen(); - // Move statements to body - relinker.relink(stmtsp); - } else { - nodep->v3fatalSrc("Should have been removed by V3Sched::schedule"); + m_lastSenp = nodep->sensesp(); + // Make a new if statement + m_lastIfp = makeActiveIf(m_lastSenp); + relinker.relink(m_lastIfp); } + + // Move statements to if + m_lastIfp->addIfsp(stmtsp); + + // Dispose of the AstActive VL_DO_DANGLING(nodep->deleteTree(), nodep); } virtual void visit(AstExecGraph* nodep) override { diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 7eaaf466a..c1d49dfbf 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -112,8 +112,8 @@ void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) { << " Example path: " << vvertexp->nodep()->typeName() << endl; } if (OrderVarVertex* const vvertexp = dynamic_cast(vertexp)) { - std::cerr << vvertexp->varScp()->fileline()->warnOther() - << " Example path: " << vvertexp->varScp()->prettyName() << endl; + std::cerr << vvertexp->vscp()->fileline()->warnOther() + << " Example path: " << vvertexp->vscp()->prettyName() << endl; } } @@ -924,9 +924,6 @@ class OrderProcess final : VNDeleter { V3List m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId std::map, unsigned> m_funcNums; // Function ordinals - // STATS - std::array m_statCut; // Count of each edge type cut - // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -999,15 +996,7 @@ class OrderProcess final : VNDeleter { pushDeletep(m_deleteDomainp); } - ~OrderProcess() { - // Stats - for (int type = 0; type < OrderVEdgeType::_ENUM_END; type++) { - const double count = double(m_statCut[type]); - if (count != 0.0) { - V3Stats::addStat(string("Order, cut, ") + OrderVEdgeType(type).ascii(), count); - } - } - } + ~OrderProcess() = default; public: // Order the logic @@ -1071,16 +1060,18 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) { OrderEitherVertex* const fromVertexp = static_cast(edgep->fromp()); if (edgep->weight() && fromVertexp->domainMatters()) { AstSenTree* fromDomainp = fromVertexp->domainp(); + UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains"); + if (OrderVarVertex* const varVtxp = dynamic_cast(fromVertexp)) { - AstVarScope* const vscp = varVtxp->varScp(); + AstVarScope* const vscp = varVtxp->vscp(); if (AstSenTree* const externalDomainp = m_externalDomain(vscp)) { + UASSERT(!externalDomainp->hasCombo(), + "There should be no need for combinational domains"); fromDomainp = fromDomainp == m_deleteDomainp ? externalDomainp : combineDomains(fromDomainp, externalDomainp); } } - UINFO(9, " from d=" << cvtToHex(fromDomainp) << " " << fromVertexp << endl); - UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains"); // Irrelevant input vertex (never triggered) if (fromDomainp == m_deleteDomainp) continue; @@ -1088,29 +1079,22 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) { // First input to this vertex if (!domainp) domainp = fromDomainp; - // Once in combo, keep in combo; already as severe as we can get - if (domainp->hasCombo()) break; - // Make a domain that merges the two domains if (domainp != fromDomainp) domainp = combineDomains(domainp, fromDomainp); } } - // Default the domain - // This is a node which has only constant inputs, or is otherwise indeterminate. - // Presumably it has inputs which we never trigger, or nothing it's sensitive to, - // so we can rip it out. - if (!domainp && vertexp->scopep()) domainp = m_deleteDomainp; + // If nothing triggers this vertex, we can delete the corresponding logic + if (!domainp) domainp = m_deleteDomainp; - if (domainp) { - vertexp->domainp(domainp); - UINFO(5, " done d=" << cvtToHex(vertexp->domainp()) - << (domainp == m_deleteDomainp ? " [DEL]" - : vertexp->domainp()->hasCombo() ? " [COMB]" - : vertexp->domainp()->isMulti() ? " [MULT]" - : "") - << " " << vertexp << endl); - } + // Set the domain of the vertex + vertexp->domainp(domainp); + UINFO(5, " done d=" << cvtToHex(vertexp->domainp()) + << (domainp == m_deleteDomainp ? " [DEL]" + : vertexp->domainp()->hasCombo() ? " [COMB]" + : vertexp->domainp()->isMulti() ? " [MULT]" + : "") + << " " << vertexp << endl); } //###################################################################### @@ -1131,7 +1115,7 @@ void OrderProcess::processEdgeReport() { for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (OrderVarVertex* const vvertexp = dynamic_cast(itp)) { - string name(vvertexp->varScp()->prettyName()); + string name(vvertexp->vscp()->prettyName()); if (dynamic_cast(itp)) { name += " {PRE}"; } else if (dynamic_cast(itp)) { @@ -1141,7 +1125,7 @@ void OrderProcess::processEdgeReport() { } std::ostringstream os; os.setf(std::ios::left); - os << " " << cvtToHex(vvertexp->varScp()) << " " << std::setw(50) << name << " "; + os << " " << cvtToHex(vvertexp->vscp()) << " " << std::setw(50) << name << " "; AstSenTree* const senTreep = vvertexp->domainp(); if (senTreep == m_deleteDomainp) { os << "DELETED"; @@ -1314,67 +1298,65 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp, AstNode* nodep = lvertexp->nodep(); AstNodeModule* const modp = scopep->modp(); UASSERT(modp, "nullptr"); - if (VN_IS(nodep, Active)) { - // Just ignore sensitivities, we'll deal with them when we move statements that need them - } else { // Normal logic - // Move the logic into a CFunc + + // We are move the logic into a CFunc, so unlink it from the AstActive + nodep->unlinkFrBack(); + + // Process procedures per statement (unless profCFuncs), so we can split CFuncs within + // procedures. Everything else is handled in one go + if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) { + nodep = procp->bodysp(); + pushDeletep(procp); + } + + // When profCFuncs, create a new function for all logic block + if (v3Global.opt.profCFuncs()) newFuncpr = nullptr; + + while (nodep) { + // Split the CFunc if too large (but not when profCFuncs) + if (!v3Global.opt.profCFuncs() + && (v3Global.opt.outputSplitCFuncs() + && v3Global.opt.outputSplitCFuncs() < newStmtsr)) { + // Put every statement into a unique function to ease profiling or reduce function + // size + newFuncpr = nullptr; + } + if (!newFuncpr && domainp != m_deleteDomainp) { + const string name = cfuncName(modp, domainp, scopep, nodep); + newFuncpr = new AstCFunc(nodep->fileline(), name, scopep); + newFuncpr->isStatic(false); + newFuncpr->isLoose(true); + newFuncpr->slow(m_slow); + newStmtsr = 0; + scopep->addActivep(newFuncpr); + // Create top call to it + AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr); + // Where will we be adding the call? + AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp); + newActivep->addStmtsp(callp); + if (!activep) { + activep = newActivep; + } else { + activep->addNext(newActivep); + } + UINFO(6, " New " << newFuncpr << endl); + } + + AstNode* const nextp = nodep->nextp(); + // When processing statements in a procedure, unlink the current statement if (nodep->backp()) nodep->unlinkFrBack(); - // Process procedures per statement (unless profCFuncs), so we can split CFuncs within - // procedures. Everything else is handled in one go - if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) { - nodep = procp->bodysp(); - pushDeletep(procp); + if (domainp == m_deleteDomainp) { + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else { + newFuncpr->addStmtsp(nodep); + // Add in the number of nodes we're adding + if (v3Global.opt.outputSplitCFuncs()) newStmtsr += nodep->nodeCount(); } - // When profCFuncs, create a new function for all logic block - if (v3Global.opt.profCFuncs()) newFuncpr = nullptr; - - while (nodep) { - // Split the CFunc if too large (but not when profCFuncs) - if (!v3Global.opt.profCFuncs() - && (v3Global.opt.outputSplitCFuncs() - && v3Global.opt.outputSplitCFuncs() < newStmtsr)) { - // Put every statement into a unique function to ease profiling or reduce function - // size - newFuncpr = nullptr; - } - if (!newFuncpr && domainp != m_deleteDomainp) { - const string name = cfuncName(modp, domainp, scopep, nodep); - newFuncpr = new AstCFunc(nodep->fileline(), name, scopep); - newFuncpr->isStatic(false); - newFuncpr->isLoose(true); - newFuncpr->slow(m_slow); - newStmtsr = 0; - scopep->addActivep(newFuncpr); - // Create top call to it - AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr); - // Where will we be adding the call? - AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp); - newActivep->addStmtsp(callp); - if (!activep) { - activep = newActivep; - } else { - activep->addNext(newActivep); - } - UINFO(6, " New " << newFuncpr << endl); - } - - AstNode* const nextp = nodep->nextp(); - // When processing statements in a procedure, unlink the current statement - if (nodep->backp()) nodep->unlinkFrBack(); - - if (domainp == m_deleteDomainp) { - VL_DO_DANGLING(pushDeletep(nodep), nodep); - } else { - newFuncpr->addStmtsp(nodep); - // Add in the number of nodes we're adding - if (v3Global.opt.outputSplitCFuncs()) newStmtsr += nodep->nodeCount(); - } - - nodep = nextp; - } + nodep = nextp; } + return activep; } @@ -1430,7 +1412,7 @@ void OrderProcess::processMTasks() { const OrderVarVertex* const pre_varp = dynamic_cast(edgep->fromp()); if (!pre_varp) continue; - AstVar* const varp = pre_varp->varScp()->varp(); + AstVar* const varp = pre_varp->vscp()->varp(); // varp depends on logicp, so logicp produces varp, // and vice-versa below varp->addProducingMTaskId(mtaskId); @@ -1440,7 +1422,7 @@ void OrderProcess::processMTasks() { const OrderVarVertex* const post_varp = dynamic_cast(edgep->top()); if (!post_varp) continue; - AstVar* const varp = post_varp->varScp()->varp(); + AstVar* const varp = post_varp->vscp()->varp(); varp->addConsumingMTaskId(mtaskId); } // TODO? We ignore IO vars here, so those will have empty mtask diff --git a/src/V3OrderGraph.h b/src/V3OrderGraph.h index bf8da4954..33a22284d 100644 --- a/src/V3OrderGraph.h +++ b/src/V3OrderGraph.h @@ -43,7 +43,6 @@ #include -class OrderVisitor; class OrderMoveVertex; class OrderMoveVertexMaker; class OrderMoveDomScope; @@ -55,50 +54,8 @@ enum OrderWeights : uint8_t { WEIGHT_POST = 2, // Post-delayed used var WEIGHT_PRE = 3, // Breakable pre-delayed used var WEIGHT_MEDIUM = 8, // Medium weight just so dot graph looks nice - WEIGHT_NORMAL = 32 -}; // High weight just so dot graph looks nice - -struct OrderVEdgeType { - enum en : uint8_t { - VERTEX_UNKNOWN = 0, - VERTEX_LOGIC, - VERTEX_VARSTD, - VERTEX_VARPRE, - VERTEX_VARPOST, - VERTEX_VARPORD, - VERTEX_MOVE, - EDGE_STD, - EDGE_COMBOCUT, - EDGE_PRECUT, - EDGE_POSTCUT, - _ENUM_END - }; - const char* ascii() const { - static const char* const names[] - = {"%E-vedge", "VERTEX_LOGIC", "VERTEX_VARSTD", "VERTEX_VARPRE", - "VERTEX_VARPOST", "VERTEX_VARPORD", "VERTEX_MOVE", "EDGE_STD", - "EDGE_COMBOCUT", "EDGE_PRECUT", "EDGE_POSTCUT", "_ENUM_END"}; - return names[m_e]; - } - enum en m_e; - inline OrderVEdgeType() - : m_e{VERTEX_UNKNOWN} {} - // cppcheck-suppress noExplicitConstructor - inline OrderVEdgeType(en _e) - : m_e{_e} {} - explicit inline OrderVEdgeType(int _e) - : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning - operator en() const { return m_e; } + WEIGHT_NORMAL = 32 // High weight just so dot graph looks nice }; -inline bool operator==(const OrderVEdgeType& lhs, const OrderVEdgeType& rhs) { - return lhs.m_e == rhs.m_e; -} -inline bool operator==(const OrderVEdgeType& lhs, OrderVEdgeType::en rhs) { - return lhs.m_e == rhs; -} -inline bool operator==(OrderVEdgeType::en lhs, const OrderVEdgeType& rhs) { - return lhs == rhs.m_e; -} //###################################################################### // Graph types @@ -107,7 +64,7 @@ class OrderGraph final : public V3Graph { public: OrderGraph() = default; virtual ~OrderGraph() override = default; - // Methods + // METHODS virtual void loopsVertexCb(V3GraphVertex* vertexp) override; }; @@ -117,156 +74,187 @@ public: class OrderEitherVertex VL_NOT_FINAL : public V3GraphVertex { AstScope* const m_scopep; // Scope the vertex is in AstSenTree* m_domainp; // Clock domain (nullptr = to be computed as we iterate) -protected: - OrderEitherVertex(V3Graph* graphp, const OrderEitherVertex& old) - : V3GraphVertex{graphp, old} - , m_scopep{old.m_scopep} - , m_domainp{old.m_domainp} {} -public: +protected: + // CONSTRUCTOR OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp) : V3GraphVertex{graphp} , m_scopep{scopep} - , m_domainp{domainp} {} + , m_domainp{domainp} { + UASSERT(scopep, "Must not be null"); + } virtual ~OrderEitherVertex() override = default; - virtual OrderEitherVertex* clone(V3Graph* graphp) const override = 0; - // Methods - virtual OrderVEdgeType type() const = 0; - virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex - virtual string dotName() const override { return cvtToHex(m_scopep) + "_"; } + +public: + // METHODS + virtual bool domainMatters() = 0; + // ACCESSORS + AstSenTree* domainp() const { return m_domainp; } void domainp(AstSenTree* domainp) { m_domainp = domainp; } AstScope* scopep() const { return m_scopep; } - AstSenTree* domainp() const { return m_domainp; } + + // LCOV_EXCL_START // Debug code + virtual string dotName() const override { return cvtToHex(m_scopep) + "_"; } + // LCOV_EXCL_STOP }; class OrderLogicVertex final : public OrderEitherVertex { AstNode* const m_nodep; AstSenTree* const m_hybridp; -protected: - OrderLogicVertex(V3Graph* graphp, const OrderLogicVertex& old) - : OrderEitherVertex{graphp, old} - , m_nodep{old.m_nodep} - , m_hybridp{old.m_hybridp} {} - public: + // CONSTRUCTOR OrderLogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstSenTree* hybridp, AstNode* nodep) : OrderEitherVertex{graphp, scopep, domainp} , m_nodep{nodep} , m_hybridp{hybridp} { - UASSERT_OBJ(!(domainp && hybridp), nodep, "Can't have bot domainp and hybridp set"); + UASSERT_OBJ(!(domainp && hybridp), nodep, "Cannot have bot domainp and hybridp set"); } virtual ~OrderLogicVertex() override = default; - virtual OrderLogicVertex* clone(V3Graph* graphp) const override { - return new OrderLogicVertex(graphp, *this); - } - virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_LOGIC; } + + // METHODS virtual bool domainMatters() override { return true; } + // ACCESSORS + AstNode* nodep() const { return m_nodep; } + AstSenTree* hybridp() const { return m_hybridp; } + + // LCOV_EXCL_START // Debug code virtual string name() const override { return (cvtToHex(m_nodep) + "\\n " + cvtToStr(nodep()->typeName())); } - AstNode* nodep() const { return m_nodep; } - AstSenTree* hybridp() const { return m_hybridp; } virtual string dotShape() const override { return VN_IS(m_nodep, Active) ? "doubleoctagon" : "rect"; } + // LCOV_EXCL_STOP }; class OrderVarVertex VL_NOT_FINAL : public OrderEitherVertex { - AstVarScope* const m_varScp; - -protected: - OrderVarVertex(V3Graph* graphp, const OrderVarVertex& old) - : OrderEitherVertex{graphp, old} - , m_varScp{old.m_varScp} {} + AstVarScope* const m_vscp; public: - OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + // CONSTRUCTOR + OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* vscp) : OrderEitherVertex{graphp, scopep, nullptr} - , m_varScp{varScp} {} + , m_vscp{vscp} {} virtual ~OrderVarVertex() override = default; - virtual OrderVarVertex* clone(V3Graph* graphp) const override = 0; - virtual OrderVEdgeType type() const override = 0; - virtual FileLine* fileline() const override { return varScp()->fileline(); } + // ACCESSORS - AstVarScope* varScp() const { return m_varScp; } - virtual string dotShape() const override { return "ellipse"; } + AstVarScope* vscp() const { return m_vscp; } + + // LCOV_EXCL_START // Debug code + virtual string dotShape() const override final { return "ellipse"; } + virtual string nameSuffix() const = 0; + virtual string name() const override final { + return cvtToHex(m_vscp) + " " + nameSuffix() + "\\n " + m_vscp->name(); + } + // LCOV_EXCL_STOP }; class OrderVarStdVertex final : public OrderVarVertex { - OrderVarStdVertex(V3Graph* graphp, const OrderVarStdVertex& old) - : OrderVarVertex{graphp, old} {} - public: + // CONSTRUCTOR OrderVarStdVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) : OrderVarVertex{graphp, scopep, varScp} {} virtual ~OrderVarStdVertex() override = default; - virtual OrderVarStdVertex* clone(V3Graph* graphp) const override { - return new OrderVarStdVertex(graphp, *this); - } - virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARSTD; } - virtual string name() const override { - return (cvtToHex(varScp()) + "\\n " + varScp()->name()); - } - virtual string dotColor() const override { return "grey"; } - virtual bool domainMatters() override { return true; } -}; -class OrderVarPreVertex final : public OrderVarVertex { - OrderVarPreVertex(V3Graph* graphp, const OrderVarPreVertex& old) - : OrderVarVertex{graphp, old} {} + // METHODS + virtual bool domainMatters() override { return true; } + + // LCOV_EXCL_START // Debug code + virtual string nameSuffix() const override { return ""; } + virtual string dotColor() const override { return "grey"; } + // LCOV_EXCL_STOP +}; + +class OrderVarPreVertex final : public OrderVarVertex { public: + // CONSTRUCTOR OrderVarPreVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) : OrderVarVertex{graphp, scopep, varScp} {} virtual ~OrderVarPreVertex() override = default; - virtual OrderVarPreVertex* clone(V3Graph* graphp) const override { - return new OrderVarPreVertex(graphp, *this); - } - virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPRE; } - virtual string name() const override { - return (cvtToHex(varScp()) + " PRE\\n " + varScp()->name()); - } - virtual string dotColor() const override { return "green"; } - virtual bool domainMatters() override { return false; } -}; -class OrderVarPostVertex final : public OrderVarVertex { - OrderVarPostVertex(V3Graph* graphp, const OrderVarPostVertex& old) - : OrderVarVertex{graphp, old} {} + // METHODS + virtual bool domainMatters() override { return false; } + + // LCOV_EXCL_START // Debug code + virtual string nameSuffix() const override { return "PRE"; } + virtual string dotColor() const override { return "green"; } + // LCOV_EXCL_STOP +}; + +class OrderVarPostVertex final : public OrderVarVertex { public: + // CONSTRUCTOR OrderVarPostVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) : OrderVarVertex{graphp, scopep, varScp} {} - virtual OrderVarPostVertex* clone(V3Graph* graphp) const override { - return new OrderVarPostVertex(graphp, *this); - } - virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPOST; } virtual ~OrderVarPostVertex() override = default; - virtual string name() const override { - return (cvtToHex(varScp()) + " POST\\n " + varScp()->name()); - } - virtual string dotColor() const override { return "red"; } - virtual bool domainMatters() override { return false; } -}; -class OrderVarPordVertex final : public OrderVarVertex { - OrderVarPordVertex(V3Graph* graphp, const OrderVarPordVertex& old) - : OrderVarVertex{graphp, old} {} + // METHODS + virtual bool domainMatters() override { return false; } + + // LCOV_EXCL_START // Debug code + virtual string nameSuffix() const override { return "POST"; } + virtual string dotColor() const override { return "red"; } + // LCOV_EXCL_STOP +}; + +class OrderVarPordVertex final : public OrderVarVertex { public: + // CONSTRUCTOR OrderVarPordVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) : OrderVarVertex{graphp, scopep, varScp} {} virtual ~OrderVarPordVertex() override = default; - virtual OrderVarPordVertex* clone(V3Graph* graphp) const override { - return new OrderVarPordVertex(graphp, *this); - } - virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPORD; } - virtual string name() const override { - return (cvtToHex(varScp()) + " PORD\\n " + varScp()->name()); - } - virtual string dotColor() const override { return "blue"; } + + // METHODS virtual bool domainMatters() override { return false; } + + // LCOV_EXCL_START // Debug code + virtual string nameSuffix() const override { return "PORD"; } + virtual string dotColor() const override { return "blue"; } + // LCOV_EXCL_STOP +}; + +//###################################################################### +// Edge types + +class OrderEdge VL_NOT_FINAL : public V3GraphEdge { +public: + // CONSTRUCTOR + OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, + bool cutable = false) + : V3GraphEdge{graphp, fromp, top, weight, cutable} {} + virtual ~OrderEdge() override = default; +}; + +class OrderPostCutEdge final : public OrderEdge { + // Edge created from output of post assignment +public: + // CONSTRUCTOR + OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge{graphp, fromp, top, WEIGHT_COMBO, CUTABLE} {} + virtual ~OrderPostCutEdge() override = default; + + // LCOV_EXCL_START // Debug code + virtual string dotColor() const override { return "palegreen"; } + // LCOV_EXCL_STOP +}; + +class OrderPreCutEdge final : public OrderEdge { + // Edge created from var_PREVAR->consuming logic vertex + // Always breakable, just results in performance loss + // in which case we can't optimize away the pre/post delayed assignments +public: + // CONSTRUCTOR + OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge{graphp, fromp, top, WEIGHT_PRE, CUTABLE} {} + virtual ~OrderPreCutEdge() override = default; + + // LCOV_EXCL_START // Debug code + virtual string dotColor() const override { return "khaki"; } + // LCOV_EXCL_STOP }; //###################################################################### @@ -283,7 +271,7 @@ protected: friend class OrderProcess; friend class OrderMoveVertexMaker; // These only contain the "next" item, - // for the head of the list, see the same var name under OrderVisitor + // for the head of the list, see the same var name under OrderProcess V3ListEnt m_pomWaitingE; // List of nodes needing inputs to become ready V3ListEnt m_readyVerticesE; // List of ready under domain/scope public: @@ -294,12 +282,8 @@ public: , m_state{POM_WAIT} , m_domScopep{nullptr} {} virtual ~OrderMoveVertex() override = default; - virtual OrderMoveVertex* clone(V3Graph* graphp) const override { - v3fatalSrc("Unsupported"); - return nullptr; - } + // METHODS - virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; } virtual string dotColor() const override { if (logicp()) { return logicp()->dotColor(); @@ -307,13 +291,7 @@ public: return ""; } } - virtual FileLine* fileline() const override { - if (logicp()) { - return logicp()->fileline(); - } else { - return nullptr; - } - } + virtual string name() const override { string nm; if (VL_UNCOVERABLE(!logicp())) { // Avoid crash when debugging @@ -350,9 +328,6 @@ class MTaskMoveVertex final : public V3GraphVertex { const AstScope* const m_scopep; const AstSenTree* const m_domainp; -protected: - friend class OrderVisitor; - public: MTaskMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp, const OrderEitherVertex* varp, const AstScope* scopep, const AstSenTree* domainp) @@ -364,11 +339,13 @@ public: UASSERT(!(logicp && varp), "MTaskMoveVertex: logicp and varp may not both be set!\n"); } virtual ~MTaskMoveVertex() override = default; - virtual MTaskMoveVertex* clone(V3Graph* graphp) const override { - v3fatalSrc("Unsupported"); - return nullptr; - } - virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; } + + // ACCESSORS + OrderLogicVertex* logicp() const { return m_logicp; } + const OrderEitherVertex* varp() const { return m_varp; } + const AstScope* scopep() const { return m_scopep; } + const AstSenTree* domainp() const { return m_domainp; } + virtual string dotColor() const override { if (logicp()) { return logicp()->dotColor(); @@ -389,71 +366,6 @@ public: } return nm; } - // ACCESSORS - OrderLogicVertex* logicp() const { return m_logicp; } - const OrderEitherVertex* varp() const { return m_varp; } - const AstScope* scopep() const { return m_scopep; } - const AstSenTree* domainp() const { return m_domainp; } -}; - -//###################################################################### -// Edge types - -class OrderEdge VL_NOT_FINAL : public V3GraphEdge { -protected: - OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, const OrderEdge& old) - : V3GraphEdge{graphp, fromp, top, old} {} - -public: - OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, - bool cutable = false) - : V3GraphEdge{graphp, fromp, top, weight, cutable} {} - virtual ~OrderEdge() override = default; - virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_STD; } - virtual OrderEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, - V3GraphVertex* top) const override { - return new OrderEdge(graphp, fromp, top, *this); - } -}; - -class OrderPostCutEdge final : public OrderEdge { - // Edge created from output of post assignment - // Breakable if the output var feeds back to input combo logic or another clock pin - // in which case we'll need a change detect loop around this var. - OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderPostCutEdge& old) - : OrderEdge{graphp, fromp, top, old} {} - -public: - OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : OrderEdge{graphp, fromp, top, WEIGHT_COMBO, CUTABLE} {} - virtual OrderVEdgeType type() const override { return OrderVEdgeType::EDGE_POSTCUT; } - virtual ~OrderPostCutEdge() override = default; - virtual OrderPostCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, - V3GraphVertex* top) const override { - return new OrderPostCutEdge(graphp, fromp, top, *this); - } - virtual string dotColor() const override { return "PaleGreen"; } -}; - -class OrderPreCutEdge final : public OrderEdge { - // Edge created from var_PREVAR->consuming logic vertex - // Always breakable, just results in performance loss - // in which case we can't optimize away the pre/post delayed assignments - OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderPreCutEdge& old) - : OrderEdge{graphp, fromp, top, old} {} - -public: - OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : OrderEdge{graphp, fromp, top, WEIGHT_PRE, CUTABLE} {} - virtual OrderVEdgeType type() const override { return OrderVEdgeType::EDGE_PRECUT; } - virtual OrderPreCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, - V3GraphVertex* top) const override { - return new OrderPreCutEdge(graphp, fromp, top, *this); - } - virtual ~OrderPreCutEdge() override = default; - virtual string dotColor() const override { return "khaki"; } }; #endif // Guard diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 793e971ff..8b8327889 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -1951,7 +1951,7 @@ public: const OrderVarStdVertex* const ovvp = dynamic_cast(edgep->top()); if (!ovvp) continue; - if (ovvp->varScp()->varp()->isSc()) { + if (ovvp->vscp()->varp()->isSc()) { ovvSetSystemC.insert(ovvp); } else { ovvSet.insert(ovvp); diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 17c44ac01..a44fe75c1 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -128,7 +128,8 @@ void splitCheck(AstCFunc* ofuncp) { // Unlink all statements, then add item by item to new sub-functions AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]", ofuncp->stmtsp()->unlinkFrBackWithNext()}; - if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext()); + // Currently we do not use finalsp in V3Sched, if we do, it needs to be handled here + UASSERT_OBJ(!ofuncp->finalsp(), ofuncp, "Should not have any finalps"); while (tempp->stmtsp()) { AstNode* const itemp = tempp->stmtsp()->unlinkFrBack(); const int stmts = itemp->nodeCount(); diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index 61a0c7a7a..6160ec8d5 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -69,9 +69,10 @@ public: AstNode* logicp() const { return m_logicp; } AstScope* scopep() const { return m_scopep; } - // For graph dumping + // LCOV_EXCL_START // Debug code string name() const override { return m_logicp->fileline()->ascii(); }; string dotShape() const override { return "rectangle"; } + // LCOV_EXCL_STOP }; class VarVertex final : public V3GraphVertex { @@ -84,10 +85,11 @@ public: AstVarScope* vscp() const { return m_vscp; } AstVar* varp() const { return m_vscp->varp(); } - // For graph dumping + // LCOV_EXCL_START // Debug code string name() const override { return m_vscp->name(); } string dotShape() const override { return "ellipse"; } string dotColor() const override { return "blue"; } + // LCOV_EXCL_STOP }; class Graph final : public V3Graph { @@ -97,8 +99,9 @@ class Graph final : public V3Graph { AstNode* const logicp = lvtxp->logicp(); std::cerr << logicp->fileline()->warnOther() << " Example path: " << logicp->typeName() << endl; - } - if (VarVertex* const vvtxp = dynamic_cast(vtxp)) { + } else { + VarVertex* const vvtxp = dynamic_cast(vtxp); + UASSERT(vvtxp, "Cannot be anything else"); AstVarScope* const vscp = vvtxp->vscp(); std::cerr << vscp->fileline()->warnOther() << " Example path: " << vscp->prettyName() << endl; @@ -369,11 +372,7 @@ LogicByScope fixCuts(AstNetlist* netlistp, const std::vector& cutVer for (AstVarScope* const vscp : lvtx2Cuts[lvtxp]) { AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::READ}; AstSenItem* const nextp = new AstSenItem{flp, VEdgeType::ET_HYBRID, refp}; - if (!senItemsp) { - senItemsp = nextp; - } else { - senItemsp->addNext(nextp); - } + senItemsp = VN_AS(AstNode::addNext(senItemsp, nextp), SenItem); } AstSenTree* const senTree = new AstSenTree{flp, senItemsp}; // Add logic to result with new sensitivity diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 2f3afc2f1..88125ee35 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -59,6 +59,8 @@ public: SchedSenVertex(V3Graph* graphp, const AstSenItem* senItemp) : V3GraphVertex{graphp} , m_senItemp{senItemp} {} + + // LCOV_EXCL_START // Debug code string name() const override { std::ostringstream os; V3EmitV::verilogForTree(const_cast(m_senItemp), os); @@ -66,6 +68,7 @@ public: } string dotShape() const override { return "doubleoctagon"; } string dotColor() const override { return "red"; } + // LCOV_EXCL_STOP }; class SchedLogicVertex final : public V3GraphVertex { @@ -83,10 +86,12 @@ public: AstSenTree* senTreep() const { return m_senTreep; } AstNode* logicp() const { return m_logicp; } + // LCOV_EXCL_START // Debug code string name() const override { return m_logicp->typeName() + ("\n" + m_logicp->fileline()->ascii()); }; string dotShape() const override { return "rectangle"; } + // LCOV_EXCL_STOP }; class SchedVarVertex final : public V3GraphVertex { @@ -96,6 +101,8 @@ public: SchedVarVertex(V3Graph* graphp, AstVarScope* vscp) : V3GraphVertex{graphp} , m_vscp{vscp} {} + + // LCOV_EXCL_START // Debug code string name() const override { return m_vscp->name(); } string dotShape() const override { return m_vscp->scopep()->isTop() && m_vscp->varp()->isNonOutput() ? "invhouse" : "ellipse"; @@ -103,6 +110,7 @@ public: string dotColor() const override { return m_vscp->scopep()->isTop() && m_vscp->varp()->isNonOutput() ? "green" : "black"; } + // LCOV_EXCL_STOP }; class SchedGraphBuilder final : public VNVisitor { @@ -229,18 +237,23 @@ class SchedGraphBuilder final : public VNVisitor { virtual void visit(AstAssignPost* nodep) override {} virtual void visit(AstAlwaysPost* nodep) override {} + // LCOV_EXCL_START // Ignore - virtual void visit(AstInitialStatic* nodep) override { // LCOV_EXCL_START + virtual void visit(AstInitialStatic* nodep) override { nodep->v3fatalSrc("Should not need ordering"); } - virtual void visit(AstInitial* nodep) override { + virtual void visit(AstInitial* nodep) override { // nodep->v3fatalSrc("Should not need ordering"); } - virtual void visit(AstFinal* nodep) override { + virtual void visit(AstFinal* nodep) override { // nodep->v3fatalSrc("Should not need ordering"); - } // LCOV_EXCL_STOP + } - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + // Default - Any other AstActive content not handled above will hit this + virtual void visit(AstNode* nodep) override { // + nodep->v3fatalSrc("Should behandled above"); + } + // LCOV_EXCL_STOP SchedGraphBuilder(const LogicByScope& clockedLogic, const LogicByScope& combinationalLogic, const LogicByScope& hybridLogic) { diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 694764f37..78a548ea9 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -70,7 +70,7 @@ public: m_drivingRegions = static_cast(m_drivingRegions | regions); } - // For graph dumping + // LCOV_EXCL_START // Debug code string dotColor() const override { switch (static_cast(m_drivingRegions)) { case NONE: return "black"; @@ -81,9 +81,10 @@ public: case INPUT | NBA: return "magenta"; case ACTIVE | NBA: return "cyan"; case INPUT | ACTIVE | NBA: return "gray80"; // don't want white on white background - default: v3fatal("There are only 3 region bits"); return ""; // LCOV_EXCL_LINE + default: v3fatal("There are only 3 region bits"); return ""; } } + // LCOV_EXCL_STOP }; class LogicVertex final : public Vertex { diff --git a/test_regress/t/t_do_not_convert_to_comb.pl b/test_regress/t/t_do_not_convert_to_comb.pl new file mode 100755 index 000000000..c6db81d38 --- /dev/null +++ b/test_regress/t/t_do_not_convert_to_comb.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env 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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt_all => 1); + +compile( + verilator_flags2 => ["--stats"], + ); + +# We must not convert these blocks into combinational blocks +file_grep($Self->{stats}, qr/Scheduling, size of class: combinational\s+(\d+)/i, 0); + +ok(1); +1; diff --git a/test_regress/t/t_do_not_convert_to_comb.v b/test_regress/t/t_do_not_convert_to_comb.v new file mode 100644 index 000000000..f80ce544b --- /dev/null +++ b/test_regress/t/t_do_not_convert_to_comb.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +module t ( + clk, + input wire i, + output reg o_0, + output reg o_1, + output reg o_2, + output reg o_3, + output reg o_4, + output reg o_5 + ); + + input clk; + + reg a = 0; + reg b = 0; + + event e; + + // We must not convert these blocks into combinational blocks + + always @(i) begin + a <= ~a; + o_0 = i; + end + + always @(i) begin + force b = 1; + o_1 = i; + end + + always @(i) begin + release b; + o_2 = i; + end + + always @(i) begin + -> e; + o_3 = i; + end + + always @(i) begin + ->> e; + o_4 = i; + end + + always @(i) begin + $display("Hello"); + o_5 = i; + end + +endmodule