2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Block code ordering
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Order's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Compute near optimal scheduling of always/wire statements
|
|
|
|
|
// Make a graph of the entire netlist
|
|
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// For seq logic
|
|
|
|
|
// Add logic_sensitive_vertex for this list of SenItems
|
|
|
|
|
// Add edge for each sensitive_var->logic_sensitive_vertex
|
|
|
|
|
// For AssignPre's
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_consumed_var_PREVAR->logic_vertex
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
// Add edge logic_vertex->generated_var_PREORDER
|
|
|
|
|
// Cutable dependency to attempt to order dlyed
|
|
|
|
|
// assignments to avoid saving state, thus we prefer
|
|
|
|
|
// a <= b ... As the opposite order would
|
|
|
|
|
// b <= c ... require the old value of b.
|
2020-10-24 00:30:10 +02:00
|
|
|
// Add edge consumed_var_POST->logic_vertex
|
|
|
|
|
// This prevents a consumer of the "early" value to be
|
|
|
|
|
// scheduled after we've changed to the next-cycle value
|
2019-05-19 22:13:13 +02:00
|
|
|
// For Logic
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_generated_var_PREORDER->logic_vertex
|
|
|
|
|
// This ensures the AssignPre gets scheduled before this logic
|
|
|
|
|
// Add edge logic_vertex->consumed_var_PREVAR
|
|
|
|
|
// Add edge logic_vertex->consumed_var_POSTVAR
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
// For AssignPost's
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex (same as if comb)
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
2020-10-24 00:30:10 +02:00
|
|
|
// Add edge consumed_var_POST->logic_vertex (same as if comb)
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// For comb logic
|
|
|
|
|
// For comb logic
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var
|
|
|
|
|
// Mark it cutable, as circular logic may require
|
|
|
|
|
// the generated signal to become a primary input again.
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Rank the graph starting at INPUTS (see V3Graph)
|
|
|
|
|
//
|
|
|
|
|
// Visit the graph's logic vertices in ranked order
|
2019-05-19 22:13:13 +02:00
|
|
|
// For all logic vertices with all inputs already ordered
|
|
|
|
|
// Make ordered block for this module
|
|
|
|
|
// For all ^^ in same domain
|
|
|
|
|
// Move logic to ordered activation
|
|
|
|
|
// When we have no more choices, we move to the next module
|
|
|
|
|
// and make a new block. Add that new activation block to the list of calls to make.
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Order.h"
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Ast.h"
|
2021-06-19 15:40:31 +02:00
|
|
|
#include "V3AstUserAllocator.h"
|
2018-07-23 02:54:28 +02:00
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3EmitV.h"
|
|
|
|
|
#include "V3File.h"
|
|
|
|
|
#include "V3Global.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Graph.h"
|
2018-07-23 02:54:28 +02:00
|
|
|
#include "V3GraphStream.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3List.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3OrderGraph.h"
|
2022-08-24 18:17:57 +02:00
|
|
|
#include "V3OrderMoveGraph.h"
|
2018-07-23 02:54:28 +02:00
|
|
|
#include "V3Partition.h"
|
|
|
|
|
#include "V3PartitionGraph.h"
|
2022-08-05 13:15:59 +02:00
|
|
|
#include "V3Sched.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3SenTree.h"
|
2020-02-29 01:15:08 +01:00
|
|
|
#include "V3SplitVar.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <deque>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <sstream>
|
2020-08-15 16:03:34 +02:00
|
|
|
#include <unordered_map>
|
2022-08-05 11:56:57 +02:00
|
|
|
#include <vector>
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2015-03-13 00:20:46 +01:00
|
|
|
//######################################################################
|
2021-10-17 12:40:44 +02:00
|
|
|
// Order information stored under each AstNode::user1p()...
|
|
|
|
|
|
|
|
|
|
class OrderUser final {
|
|
|
|
|
// Stored in AstVarScope::user1p, a list of all the various vertices
|
|
|
|
|
// that can exist for one given scoped variable
|
|
|
|
|
public:
|
|
|
|
|
// TYPES
|
|
|
|
|
enum class VarVertexType : uint8_t { // Types of vertices we can create
|
|
|
|
|
STD = 0,
|
|
|
|
|
PRE = 1,
|
|
|
|
|
PORD = 2,
|
|
|
|
|
POST = 3
|
|
|
|
|
};
|
2018-06-22 05:08:56 +02:00
|
|
|
|
2015-03-13 00:20:46 +01:00
|
|
|
private:
|
2021-10-17 12:40:44 +02:00
|
|
|
// Vertex of each type (if non nullptr)
|
|
|
|
|
std::array<OrderVarVertex*, static_cast<size_t>(VarVertexType::POST) + 1> m_vertexps;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
2022-08-24 18:17:57 +02:00
|
|
|
OrderVarVertex* getVarVertex(OrderGraph* graphp, AstVarScope* varscp, VarVertexType type) {
|
2021-10-17 12:40:44 +02:00
|
|
|
const unsigned idx = static_cast<unsigned>(type);
|
|
|
|
|
OrderVarVertex* vertexp = m_vertexps[idx];
|
|
|
|
|
if (!vertexp) {
|
|
|
|
|
switch (type) {
|
2022-05-25 21:16:19 +02:00
|
|
|
case VarVertexType::STD: vertexp = new OrderVarStdVertex{graphp, varscp}; break;
|
|
|
|
|
case VarVertexType::PRE: vertexp = new OrderVarPreVertex{graphp, varscp}; break;
|
|
|
|
|
case VarVertexType::PORD: vertexp = new OrderVarPordVertex{graphp, varscp}; break;
|
|
|
|
|
case VarVertexType::POST: vertexp = new OrderVarPostVertex{graphp, varscp}; break;
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
|
|
|
|
m_vertexps[idx] = vertexp;
|
|
|
|
|
}
|
|
|
|
|
return vertexp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
OrderUser() { m_vertexps.fill(nullptr); }
|
|
|
|
|
~OrderUser() = default;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// OrderBuildVisitor builds the ordering graph of the entire netlist, and
|
|
|
|
|
// removes any nodes that are no longer required once the graph is built
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class OrderBuildVisitor final : public VNVisitor {
|
2021-10-17 12:40:44 +02:00
|
|
|
// TYPES
|
|
|
|
|
enum VarUsage : uint8_t { VU_CON = 0x1, VU_GEN = 0x2 };
|
|
|
|
|
using VarVertexType = OrderUser::VarVertexType;
|
|
|
|
|
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstVarScope::user1 -> OrderUser instance for variable (via m_orderUser)
|
|
|
|
|
// AstVarScope::user2 -> VarUsage within logic blocks
|
2022-05-15 17:03:32 +02:00
|
|
|
// AstVarScope::user3 -> bool: Hybrid sensitivity
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse user1InUse;
|
|
|
|
|
const VNUser2InUse user2InUse;
|
2022-05-15 17:03:32 +02:00
|
|
|
const VNUser3InUse user3InUse;
|
2021-10-17 12:40:44 +02:00
|
|
|
AstUser1Allocator<AstVarScope, OrderUser> m_orderUser;
|
|
|
|
|
|
|
|
|
|
// STATE
|
2022-07-14 13:35:44 +02:00
|
|
|
OrderGraph* const m_graphp = new OrderGraph; // The ordering graph built by this visitor
|
|
|
|
|
OrderLogicVertex* m_logicVxp = nullptr; // Current logic block being analyzed
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Map from Trigger reference AstSenItem to the original AstSenTree
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& m_trigToSen;
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
// Current AstScope being processed
|
|
|
|
|
AstScope* m_scopep = nullptr;
|
2022-12-03 00:46:38 +01:00
|
|
|
// Sensitivity list for clocked logic, nullptr for combinational and hybrid logic
|
2021-10-17 12:40:44 +02:00
|
|
|
AstSenTree* m_domainp = nullptr;
|
2022-05-15 17:03:32 +02:00
|
|
|
// Sensitivity list for hybrid logic, nullptr for everything else
|
|
|
|
|
AstSenTree* m_hybridp = nullptr;
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
bool m_inClocked = false; // Underneath clocked AstActive
|
|
|
|
|
bool m_inPre = false; // Underneath AstAssignPre
|
|
|
|
|
bool m_inPost = false; // Underneath AstAssignPost/AstAlwaysPost
|
2022-05-15 17:03:32 +02:00
|
|
|
std::function<bool(const AstVarScope*)> m_readTriggersCombLogic;
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2015-03-13 00:20:46 +01:00
|
|
|
// METHODS
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
void iterateLogic(AstNode* nodep) {
|
|
|
|
|
UASSERT_OBJ(!m_logicVxp, nodep, "Should not nest");
|
|
|
|
|
// Reset VarUsage
|
|
|
|
|
AstNode::user2ClearTree();
|
|
|
|
|
// Create LogicVertex for this logic node
|
2022-05-15 17:03:32 +02:00
|
|
|
m_logicVxp = new OrderLogicVertex{m_graphp, m_scopep, m_domainp, m_hybridp, nodep};
|
2021-10-17 12:40:44 +02:00
|
|
|
// Gather variable dependencies based on usage
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Finished with this logic
|
|
|
|
|
m_logicVxp = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OrderVarVertex* getVarVertex(AstVarScope* varscp, VarVertexType type) {
|
2022-05-25 21:16:19 +02:00
|
|
|
return m_orderUser(varscp).getVarVertex(m_graphp, varscp, type);
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstActive* nodep) override {
|
2021-10-17 12:40:44 +02:00
|
|
|
UASSERT_OBJ(!nodep->sensesStorep(), nodep,
|
|
|
|
|
"AstSenTrees should have been made global in V3ActiveTop");
|
|
|
|
|
UASSERT_OBJ(m_scopep, nodep, "AstActive not under AstScope");
|
|
|
|
|
UASSERT_OBJ(!m_logicVxp, nodep, "AstActive under logic");
|
2022-10-03 05:04:55 +02:00
|
|
|
UASSERT_OBJ(!m_inClocked && !m_domainp && !m_hybridp, nodep, "Should not nest");
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// This is the original sensitivity of the block (i.e.: not the ref into the TRIGGERVEC)
|
|
|
|
|
|
|
|
|
|
const AstSenTree* const senTreep = nodep->sensesp()->hasCombo()
|
|
|
|
|
? nodep->sensesp()
|
|
|
|
|
: m_trigToSen.at(nodep->sensesp()->sensesp());
|
|
|
|
|
|
|
|
|
|
m_inClocked = senTreep->hasClocked();
|
|
|
|
|
|
|
|
|
|
// Note: We don't need to analyse the sensitivity list, as currently all sensitivity
|
|
|
|
|
// lists simply reference an entry in a trigger vector, which are all set external to
|
|
|
|
|
// the code being ordered.
|
|
|
|
|
|
|
|
|
|
// Combinational and hybrid logic will have it's domain assigned based on the driver
|
|
|
|
|
// domains. For clocked logic, we already know its domain.
|
|
|
|
|
if (!senTreep->hasCombo() && !senTreep->hasHybrid()) m_domainp = nodep->sensesp();
|
|
|
|
|
|
|
|
|
|
// Hybrid logic also includes additional sensitivities
|
|
|
|
|
if (senTreep->hasHybrid()) {
|
|
|
|
|
m_hybridp = nodep->sensesp();
|
|
|
|
|
// Mark AstVarScopes that are explicit sensitivities
|
|
|
|
|
AstNode::user3ClearTree();
|
2022-10-20 14:48:44 +02:00
|
|
|
senTreep->foreach([](const AstVarRef* refp) { //
|
2022-05-15 17:03:32 +02:00
|
|
|
refp->varScopep()->user3(true);
|
|
|
|
|
});
|
|
|
|
|
m_readTriggersCombLogic = [](const AstVarScope* vscp) { return !vscp->user3(); };
|
|
|
|
|
} else {
|
|
|
|
|
// Always triggers
|
|
|
|
|
m_readTriggersCombLogic = [](const AstVarScope*) { return true; };
|
|
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
// Analyze logic underneath
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
m_inClocked = false;
|
|
|
|
|
m_domainp = nullptr;
|
2022-05-15 17:03:32 +02:00
|
|
|
m_hybridp = nullptr;
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2021-10-17 12:40:44 +02:00
|
|
|
// As we explicitly not visit (see ignored nodes below) any subtree that is not relevant
|
|
|
|
|
// for ordering, we should be able to assert this:
|
|
|
|
|
UASSERT_OBJ(m_scopep, nodep, "AstVarRef not under scope");
|
2022-05-15 17:03:32 +02:00
|
|
|
UASSERT_OBJ(m_logicVxp, nodep, "AstVarRef not under logic");
|
2021-10-17 12:40:44 +02:00
|
|
|
AstVarScope* const varscp = nodep->varScopep();
|
|
|
|
|
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
|
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Variable reference in logic. Add data dependency.
|
|
|
|
|
|
|
|
|
|
// Check whether this variable was already generated/consumed in the same logic. We
|
|
|
|
|
// don't want to add extra edges if the logic has many usages of the same variable,
|
|
|
|
|
// so only proceed on first encounter.
|
|
|
|
|
const bool prevGen = varscp->user2() & VU_GEN;
|
|
|
|
|
const bool prevCon = varscp->user2() & VU_CON;
|
|
|
|
|
|
|
|
|
|
// Compute whether the variable is produced (written) here
|
2022-10-13 21:04:43 +02:00
|
|
|
bool gen = !prevGen && nodep->access().isWriteOrRW();
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Compute whether the value is consumed (read) here
|
|
|
|
|
bool con = false;
|
|
|
|
|
if (!prevCon && nodep->access().isReadOrRW()) {
|
|
|
|
|
con = true;
|
|
|
|
|
if (prevGen && !m_inClocked) {
|
|
|
|
|
// Dangerous assumption:
|
|
|
|
|
// If a variable is consumed in the same combinational process that produced it
|
|
|
|
|
// earlier, consider it something like:
|
|
|
|
|
// foo = 1
|
|
|
|
|
// foo = foo + 1
|
|
|
|
|
// and still optimize. Note this will break though:
|
|
|
|
|
// if (sometimes) foo = 1
|
|
|
|
|
// foo = foo + 1
|
|
|
|
|
// TODO: Do this properly with liveness analysis (i.e.: if live, it's consumed)
|
|
|
|
|
// Note however that this construct is not nicely synthesizable (yields
|
|
|
|
|
// latch?).
|
|
|
|
|
con = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-08-24 18:17:57 +02:00
|
|
|
// Note: See V3OrderGraph.h about the roles of the various vertex types
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Variable is produced
|
|
|
|
|
if (gen) {
|
|
|
|
|
// Update VarUsage
|
|
|
|
|
varscp->user2(varscp->user2() | VU_GEN);
|
|
|
|
|
// Add edges for produced variables
|
|
|
|
|
if (!m_inClocked || m_inPost) {
|
|
|
|
|
// Combinational logic
|
|
|
|
|
OrderVarVertex* const varVxp = getVarVertex(varscp, VarVertexType::STD);
|
|
|
|
|
// Add edge from producing LogicVertex -> produced VarStdVertex
|
|
|
|
|
if (m_inPost) {
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addSoftEdge(m_logicVxp, varVxp, WEIGHT_COMBO);
|
2021-10-17 12:40:44 +02:00
|
|
|
} else {
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, varVxp, WEIGHT_NORMAL);
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Add edge from produced VarPostVertex -> to producing LogicVertex
|
|
|
|
|
|
|
|
|
|
// For m_inPost:
|
|
|
|
|
// Add edge consumed_var_POST->logic_vertex
|
|
|
|
|
// This prevents a consumer of the "early" value to be scheduled
|
|
|
|
|
// after we've changed to the next-cycle value
|
|
|
|
|
// ALWAYS do it:
|
|
|
|
|
// There maybe a wire a=b; between the two blocks
|
|
|
|
|
OrderVarVertex* const postVxp = getVarVertex(varscp, VarVertexType::POST);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(postVxp, m_logicVxp, WEIGHT_POST);
|
2022-05-15 17:03:32 +02:00
|
|
|
} else if (m_inPre) { // AstAssignPre
|
|
|
|
|
// Add edge from producing LogicVertex -> produced VarPordVertex
|
|
|
|
|
OrderVarVertex* const ordVxp = getVarVertex(varscp, VarVertexType::PORD);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, ordVxp, WEIGHT_NORMAL);
|
2022-05-15 17:03:32 +02:00
|
|
|
// Add edge from producing LogicVertex -> produced VarStdVertex
|
|
|
|
|
OrderVarVertex* const varVxp = getVarVertex(varscp, VarVertexType::STD);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, varVxp, WEIGHT_NORMAL);
|
2022-05-15 17:03:32 +02:00
|
|
|
} else {
|
|
|
|
|
// Sequential (clocked) logic
|
|
|
|
|
// Add edge from produced VarPordVertex -> to producing LogicVertex
|
|
|
|
|
OrderVarVertex* const ordVxp = getVarVertex(varscp, VarVertexType::PORD);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(ordVxp, m_logicVxp, WEIGHT_NORMAL);
|
2022-05-15 17:03:32 +02:00
|
|
|
// Add edge from producing LogicVertex-> to produced VarStdVertex
|
|
|
|
|
OrderVarVertex* const varVxp = getVarVertex(varscp, VarVertexType::STD);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, varVxp, WEIGHT_NORMAL);
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Variable is consumed
|
|
|
|
|
if (con) {
|
|
|
|
|
// Update VarUsage
|
|
|
|
|
varscp->user2(varscp->user2() | VU_CON);
|
|
|
|
|
// Add edges
|
|
|
|
|
if (!m_inClocked || m_inPost) {
|
|
|
|
|
// Combinational logic
|
|
|
|
|
if (m_readTriggersCombLogic(varscp)) {
|
|
|
|
|
// Ignore explicit sensitivities
|
2021-10-17 12:40:44 +02:00
|
|
|
OrderVarVertex* const varVxp = getVarVertex(varscp, VarVertexType::STD);
|
|
|
|
|
// Add edge from consumed VarStdVertex -> to consuming LogicVertex
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(varVxp, m_logicVxp, WEIGHT_MEDIUM);
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
} else if (m_inPre) {
|
|
|
|
|
// AstAssignPre logic
|
|
|
|
|
// Add edge from consumed VarPreVertex -> to consuming LogicVertex
|
|
|
|
|
// This one is cutable (vs the producer) as there's only one such consumer,
|
|
|
|
|
// but may be many producers
|
|
|
|
|
OrderVarVertex* const preVxp = getVarVertex(varscp, VarVertexType::PRE);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addSoftEdge(preVxp, m_logicVxp, WEIGHT_PRE);
|
2022-05-15 17:03:32 +02:00
|
|
|
} else {
|
|
|
|
|
// Sequential (clocked) logic
|
|
|
|
|
// Add edge from consuming LogicVertex -> to consumed VarPreVertex
|
|
|
|
|
// Generation of 'pre' because we want to indicate it should be before
|
|
|
|
|
// AstAssignPre
|
|
|
|
|
OrderVarVertex* const preVxp = getVarVertex(varscp, VarVertexType::PRE);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, preVxp, WEIGHT_NORMAL);
|
2022-05-15 17:03:32 +02:00
|
|
|
// Add edge from consuming LogicVertex -> to consumed VarPostVertex
|
|
|
|
|
OrderVarVertex* const postVxp = getVarVertex(varscp, VarVertexType::POST);
|
2022-08-24 18:17:57 +02:00
|
|
|
m_graphp->addHardEdge(m_logicVxp, postVxp, WEIGHT_POST);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-06-23 01:46:27 +02:00
|
|
|
}
|
2015-03-13 00:20:46 +01:00
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCCall* nodep) override { iterateChildren(nodep); }
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
//--- Logic akin to SystemVerilog Processes (AstNodeProcedure)
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstInitial* nodep) override { // LCOV_EXCL_START
|
2022-05-15 17:03:32 +02:00
|
|
|
nodep->v3fatalSrc("AstInitial should not need ordering");
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstInitialStatic* nodep) override { // LCOV_EXCL_START
|
2022-05-15 17:03:32 +02:00
|
|
|
nodep->v3fatalSrc("AstInitialStatic should not need ordering");
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitialAutomatic* nodep) override { //
|
2022-01-02 18:35:44 +01:00
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlways* nodep) override { //
|
2021-10-17 12:40:44 +02:00
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPost* nodep) override {
|
2021-10-17 12:40:44 +02:00
|
|
|
UASSERT_OBJ(!m_inPost, nodep, "Should not nest");
|
|
|
|
|
m_inPost = true;
|
|
|
|
|
iterateLogic(nodep);
|
|
|
|
|
m_inPost = false;
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFinal* nodep) override { // LCOV_EXCL_START
|
2022-05-15 17:03:32 +02:00
|
|
|
nodep->v3fatalSrc("AstFinal should not need ordering");
|
2021-10-17 12:40:44 +02:00
|
|
|
} // LCOV_EXCL_STOP
|
|
|
|
|
|
|
|
|
|
//--- Logic akin go SystemVerilog continuous assignments
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignAlias* nodep) override { //
|
2021-10-17 12:40:44 +02:00
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstAssignW* nodep) override { iterateLogic(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignPre* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
UASSERT_OBJ(!m_inPre, nodep, "Should not nest");
|
2022-11-19 21:23:37 +01:00
|
|
|
VL_RESTORER(m_inPre);
|
2021-10-17 12:40:44 +02:00
|
|
|
m_inPre = true;
|
|
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignPost* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
UASSERT_OBJ(!m_inPost, nodep, "Should not nest");
|
2022-11-19 21:23:37 +01:00
|
|
|
VL_RESTORER(m_inPost);
|
2021-10-17 12:40:44 +02:00
|
|
|
m_inPost = true;
|
|
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- Verilator concoctions
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPublic* nodep) override { //
|
2021-10-17 12:40:44 +02:00
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverToggle* nodep) override { //
|
2021-10-17 12:40:44 +02:00
|
|
|
iterateLogic(nodep);
|
2015-03-13 00:20:46 +01:00
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
//--- Ignored nodes
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar*) override {}
|
|
|
|
|
void visit(AstVarScope* nodep) override {}
|
|
|
|
|
void visit(AstCell*) override {} // Only interested in the respective AstScope
|
|
|
|
|
void visit(AstTypeTable*) override {}
|
|
|
|
|
void visit(AstConstPool*) override {}
|
|
|
|
|
void visit(AstClass*) override {}
|
|
|
|
|
void visit(AstCFunc*) override {
|
2021-10-17 12:40:44 +02:00
|
|
|
// Calls to DPI exports handled with AstCCall. /* verilator public */ functions are
|
|
|
|
|
// ignored for now (and hence potentially mis-ordered), but could use the same or
|
|
|
|
|
// similar mechanism as DPI exports. Every other impure function (including those
|
|
|
|
|
// that may set a non-local variable) must have been inlined in V3Task.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
// CONSTRUCTOR
|
2022-07-30 18:49:30 +02:00
|
|
|
OrderBuildVisitor(AstNetlist* /*nodep*/, const std::vector<V3Sched::LogicByScope*>& coll,
|
2022-05-15 17:03:32 +02:00
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen)
|
|
|
|
|
: m_trigToSen{trigToSen} {
|
2022-09-22 18:28:42 +02:00
|
|
|
// Build the graph
|
2022-05-15 17:03:32 +02:00
|
|
|
for (const V3Sched::LogicByScope* const lbsp : coll) {
|
|
|
|
|
for (const auto& pair : *lbsp) {
|
|
|
|
|
m_scopep = pair.first;
|
|
|
|
|
iterate(pair.second);
|
|
|
|
|
m_scopep = nullptr;
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-30 16:01:25 +02:00
|
|
|
~OrderBuildVisitor() override = default;
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2015-03-13 00:20:46 +01:00
|
|
|
public:
|
2021-10-17 12:40:44 +02:00
|
|
|
// Process the netlist and return the constructed ordering graph. It's 'process' because
|
|
|
|
|
// this visitor does change the tree (removes some nodes related to DPI export trigger).
|
2022-05-15 17:03:32 +02:00
|
|
|
static std::unique_ptr<OrderGraph>
|
|
|
|
|
process(AstNetlist* nodep, const std::vector<V3Sched::LogicByScope*>& coll,
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen) {
|
|
|
|
|
return std::unique_ptr<OrderGraph>{OrderBuildVisitor{nodep, coll, trigToSen}.m_graphp};
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class OrderProcess;
|
|
|
|
|
|
|
|
|
|
class OrderMoveDomScope final {
|
|
|
|
|
// Information stored for each unique loop, domain & scope trifecta
|
|
|
|
|
public:
|
|
|
|
|
V3ListEnt<OrderMoveDomScope*> m_readyDomScopeE; // List of next ready dom scope
|
|
|
|
|
V3List<OrderMoveVertex*> m_readyVertices; // Ready vertices with same domain & scope
|
|
|
|
|
private:
|
|
|
|
|
bool m_onReadyList = false; // True if DomScope is already on list of ready dom/scopes
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstSenTree* const m_domainp; // Domain all vertices belong to
|
|
|
|
|
const AstScope* const m_scopep; // Scope all vertices belong to
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
using DomScopeKey = std::pair<const AstSenTree*, const AstScope*>;
|
|
|
|
|
using DomScopeMap = std::map<DomScopeKey, OrderMoveDomScope*>;
|
|
|
|
|
static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
OrderMoveDomScope(const AstSenTree* domainp, const AstScope* scopep)
|
|
|
|
|
: m_domainp{domainp}
|
|
|
|
|
, m_scopep{scopep} {}
|
|
|
|
|
OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); }
|
|
|
|
|
const AstSenTree* domainp() const { return m_domainp; }
|
|
|
|
|
const AstScope* scopep() const { return m_scopep; }
|
|
|
|
|
// Check the domScope is on ready list, add if not
|
|
|
|
|
void ready(OrderProcess* opp);
|
|
|
|
|
// Mark one vertex as finished, remove from ready list if done
|
|
|
|
|
void movedVertex(OrderProcess* opp, OrderMoveVertex* vertexp);
|
|
|
|
|
// STATIC MEMBERS (for lookup)
|
|
|
|
|
static void clear() {
|
|
|
|
|
for (const auto& itr : s_dsMap) delete itr.second;
|
|
|
|
|
s_dsMap.clear();
|
|
|
|
|
}
|
|
|
|
|
V3List<OrderMoveVertex*>& readyVertices() { return m_readyVertices; }
|
|
|
|
|
static OrderMoveDomScope* findCreate(const AstSenTree* domainp, const AstScope* scopep) {
|
|
|
|
|
const DomScopeKey key = std::make_pair(domainp, scopep);
|
|
|
|
|
const auto iter = s_dsMap.find(key);
|
|
|
|
|
if (iter != s_dsMap.end()) {
|
|
|
|
|
return iter->second;
|
|
|
|
|
} else {
|
2022-11-20 19:11:01 +01:00
|
|
|
OrderMoveDomScope* domScopep = new OrderMoveDomScope{domainp, scopep};
|
2021-10-17 12:40:44 +02:00
|
|
|
s_dsMap.emplace(key, domScopep);
|
|
|
|
|
return domScopep;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
string name() const {
|
|
|
|
|
return (string("MDS:") + " d=" + cvtToHex(domainp()) + " s=" + cvtToHex(scopep()));
|
|
|
|
|
}
|
2015-03-13 00:20:46 +01:00
|
|
|
};
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap;
|
|
|
|
|
|
2022-09-16 14:17:38 +02:00
|
|
|
std::ostream& operator<<(std::ostream& lhs, const OrderMoveDomScope& rhs) {
|
2021-10-17 12:40:44 +02:00
|
|
|
lhs << rhs.name();
|
|
|
|
|
return lhs;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// ProcessMoveBuildGraph
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
template <class T_MoveVertex>
|
|
|
|
|
class ProcessMoveBuildGraph final {
|
2022-08-25 16:17:38 +02:00
|
|
|
// ProcessMoveBuildGraph takes as input the fine-grained bipartite OrderGraph of
|
|
|
|
|
// OrderLogicVertex and OrderVarVertex vertices. It produces a slightly coarsened graph to
|
|
|
|
|
// drive the code scheduling.
|
2018-06-23 05:01:50 +02:00
|
|
|
//
|
2018-07-23 02:54:28 +02:00
|
|
|
// * For the serial code scheduler, the new graph contains
|
|
|
|
|
// nodes of type OrderMoveVertex.
|
|
|
|
|
//
|
|
|
|
|
// * For the threaded code scheduler, the new graph contains
|
|
|
|
|
// nodes of type MTaskMoveVertex.
|
2018-06-23 05:01:50 +02:00
|
|
|
//
|
|
|
|
|
// * The difference in output type is abstracted away by the
|
2018-07-23 02:54:28 +02:00
|
|
|
// 'T_MoveVertex' template parameter; ProcessMoveBuildGraph otherwise
|
|
|
|
|
// works the same way for both cases.
|
2018-06-23 05:01:50 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// AstSenTree::user1p() -> AstSenTree: Original AstSenTree for trigger
|
|
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
// TYPES
|
2022-08-25 16:17:38 +02:00
|
|
|
using DomainMap = std::map<const AstSenTree*, T_MoveVertex*>;
|
2018-06-23 05:01:50 +02:00
|
|
|
|
|
|
|
|
public:
|
2020-11-19 03:32:16 +01:00
|
|
|
class MoveVertexMaker VL_NOT_FINAL {
|
2018-06-23 05:01:50 +02:00
|
|
|
public:
|
|
|
|
|
// Clients of ProcessMoveBuildGraph must supply MoveVertexMaker
|
|
|
|
|
// which creates new T_MoveVertex's. Each new vertex wraps lvertexp
|
2020-08-15 16:12:55 +02:00
|
|
|
// (which may be nullptr.)
|
2022-05-25 21:16:19 +02:00
|
|
|
virtual T_MoveVertex* makeVertexp(OrderLogicVertex* lvertexp,
|
|
|
|
|
const OrderEitherVertex* varVertexp,
|
|
|
|
|
const AstSenTree* domainp)
|
2020-04-15 13:58:34 +02:00
|
|
|
= 0;
|
2018-06-23 05:01:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// MEMBERS
|
2022-08-25 16:17:38 +02:00
|
|
|
const OrderGraph* const m_graphp; // Input OrderGraph
|
|
|
|
|
V3Graph* const m_outGraphp; // Output graph of T_MoveVertex vertices
|
2022-05-15 17:03:32 +02:00
|
|
|
// Map from Trigger reference AstSenItem to the original AstSenTree
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& m_trigToSen;
|
2021-11-26 23:55:36 +01:00
|
|
|
MoveVertexMaker* const m_vxMakerp; // Factory class for T_MoveVertex's
|
2022-08-25 16:17:38 +02:00
|
|
|
// Storage for domain -> T_MoveVertex, maps held in OrderVarVertex::userp()
|
|
|
|
|
std::deque<DomainMap> m_domainMaps;
|
2018-06-23 05:01:50 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-05-15 17:03:32 +02:00
|
|
|
ProcessMoveBuildGraph(
|
2022-08-25 16:17:38 +02:00
|
|
|
const OrderGraph* logicGraphp, // Input graph of OrderLogicVertex etc.
|
2022-05-15 17:03:32 +02:00
|
|
|
V3Graph* outGraphp, // Output graph of T_MoveVertex's
|
2022-08-25 16:17:38 +02:00
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen,
|
2022-05-15 17:03:32 +02:00
|
|
|
MoveVertexMaker* vxMakerp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_graphp{logicGraphp}
|
|
|
|
|
, m_outGraphp{outGraphp}
|
2022-08-25 16:17:38 +02:00
|
|
|
, m_trigToSen{trigToSen}
|
2020-08-16 15:55:36 +02:00
|
|
|
, m_vxMakerp{vxMakerp} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~ProcessMoveBuildGraph() = default;
|
2018-06-23 05:01:50 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void build() {
|
|
|
|
|
// How this works:
|
|
|
|
|
// - Create a T_MoveVertex for each OrderLogicVertex.
|
|
|
|
|
// - Following each OrderLogicVertex, search forward in the context of
|
|
|
|
|
// its domain...
|
|
|
|
|
// * If we encounter another OrderLogicVertex in non-exclusive
|
|
|
|
|
// domain, make the T_MoveVertex->T_MoveVertex edge.
|
|
|
|
|
// * If we encounter an OrderVarVertex, make a Vertex for the
|
|
|
|
|
// (OrderVarVertex, domain) pair and continue to search
|
|
|
|
|
// forward in the context of the same domain. Unless we
|
|
|
|
|
// already created that pair, in which case, we've already
|
|
|
|
|
// done the forward search, so stop.
|
|
|
|
|
|
2022-08-25 16:17:38 +02:00
|
|
|
// For each logic vertex, make a T_MoveVertex, for each variable vertex, allocate storage
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2022-08-25 16:17:38 +02:00
|
|
|
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(itp)) {
|
|
|
|
|
lvtxp->userp(m_vxMakerp->makeVertexp(lvtxp, nullptr, lvtxp->domainp()));
|
|
|
|
|
} else {
|
|
|
|
|
// This is an OrderVarVertex
|
|
|
|
|
m_domainMaps.emplace_back();
|
|
|
|
|
itp->userp(&m_domainMaps.back());
|
2018-06-23 05:01:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Build edges between logic vertices
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2022-08-25 16:17:38 +02:00
|
|
|
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(itp)) {
|
|
|
|
|
iterateLogicVertex(lvtxp);
|
2018-06-23 05:01:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2022-05-15 17:03:32 +02:00
|
|
|
// Returns the AstSenItem that originally corresponds to this AstSenTree, or nullptr if no
|
|
|
|
|
// original AstSenTree, or if the original AstSenTree had multiple AstSenItems.
|
|
|
|
|
const AstSenItem* getOrigSenItem(AstSenTree* senTreep) {
|
|
|
|
|
if (!senTreep->user1p()) {
|
|
|
|
|
// Find the original simple AstSenTree, if any
|
|
|
|
|
AstNode* const origp = [&]() -> AstSenItem* {
|
|
|
|
|
// If more than one AstSenItems, then not a simple AstSenTree
|
|
|
|
|
if (senTreep->sensesp()->nextp()) return nullptr;
|
|
|
|
|
|
|
|
|
|
// Find the original AstSenTree
|
|
|
|
|
auto it = m_trigToSen.find(senTreep->sensesp());
|
|
|
|
|
if (it == m_trigToSen.end()) return nullptr;
|
|
|
|
|
|
|
|
|
|
// If more than one AstSenItems on the original, then not a simple AstSenTree
|
|
|
|
|
if (it->second->sensesp()->nextp()) return nullptr;
|
|
|
|
|
|
|
|
|
|
// Else we found it.
|
|
|
|
|
return it->second->sensesp();
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
// We use the node itself as a sentinel to denote 'no original node'
|
|
|
|
|
senTreep->user1p(origp ? origp : senTreep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return senTreep->user1p() == senTreep ? nullptr : VN_AS(senTreep->user1p(), SenItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool domainsExclusive(AstSenTree* fromp, AstSenTree* top) {
|
|
|
|
|
// Return 'true' if we can prove that both 'from' and 'to' cannot both
|
|
|
|
|
// be active on the same evaluation, or false if we can't prove this.
|
|
|
|
|
//
|
|
|
|
|
// This detects the case of 'always @(posedge clk)'
|
|
|
|
|
// and 'always @(negedge clk)' being exclusive.
|
|
|
|
|
//
|
|
|
|
|
// Are there any other cases we need to handle? Maybe not,
|
|
|
|
|
// because these are not exclusive:
|
|
|
|
|
// always @(posedge A or posedge B)
|
|
|
|
|
// always @(negedge A)
|
|
|
|
|
//
|
|
|
|
|
// ... unless you know more about A and B, which sounds hard.
|
|
|
|
|
|
|
|
|
|
const AstSenItem* const fromSenItemp = getOrigSenItem(fromp);
|
|
|
|
|
if (!fromSenItemp) return false;
|
|
|
|
|
const AstSenItem* const toSenItemp = getOrigSenItem(top);
|
|
|
|
|
if (!toSenItemp) return false;
|
|
|
|
|
|
|
|
|
|
const AstNodeVarRef* const fromVarrefp = fromSenItemp->varrefp();
|
|
|
|
|
if (!fromVarrefp) return false;
|
|
|
|
|
const AstNodeVarRef* const toVarrefp = toSenItemp->varrefp();
|
|
|
|
|
if (!toVarrefp) return false;
|
|
|
|
|
|
|
|
|
|
// We know nothing about the relationship between different clocks here,
|
|
|
|
|
// so only proceed if strictly the same clock.
|
|
|
|
|
if (fromVarrefp->varScopep() != toVarrefp->varScopep()) return false;
|
|
|
|
|
|
|
|
|
|
return fromSenItemp->edgeType().exclusiveEdge(toSenItemp->edgeType());
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 16:17:38 +02:00
|
|
|
void iterateLogicVertex(const OrderLogicVertex* lvtxp) {
|
|
|
|
|
AstSenTree* const domainp = lvtxp->domainp();
|
|
|
|
|
T_MoveVertex* const lMoveVtxp = static_cast<T_MoveVertex*>(lvtxp->userp());
|
|
|
|
|
// Search forward from lvtxp, making new edges from lMoveVtxp forward
|
|
|
|
|
for (V3GraphEdge* edgep = lvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
if (edgep->weight() == 0) continue; // Was cut
|
2018-06-23 05:01:50 +02:00
|
|
|
|
2022-08-25 16:17:38 +02:00
|
|
|
// OrderGraph is a bipartite graph, so we know it's an OrderVarVertex
|
|
|
|
|
const OrderVarVertex* const vvtxp = static_cast<const OrderVarVertex*>(edgep->top());
|
|
|
|
|
|
|
|
|
|
// Look up T_MoveVertex for this domain on this variable
|
|
|
|
|
DomainMap& mapp = *static_cast<DomainMap*>(vvtxp->userp());
|
|
|
|
|
const auto pair = mapp.emplace(domainp, nullptr);
|
|
|
|
|
// Reference to the mapped T_MoveVertex
|
|
|
|
|
T_MoveVertex*& vMoveVtxp = pair.first->second;
|
|
|
|
|
|
|
|
|
|
// On first encounter, visit downstream logic dependent on this (var, domain)
|
|
|
|
|
if (pair.second) vMoveVtxp = iterateVarVertex(vvtxp, domainp);
|
|
|
|
|
|
|
|
|
|
// If no downstream dependents from this variable, then there is no need to add this
|
|
|
|
|
// variable as a dependent.
|
|
|
|
|
if (!vMoveVtxp) continue;
|
|
|
|
|
|
|
|
|
|
// Add this (variable, domain) as dependent of the logic that writes it.
|
|
|
|
|
new V3GraphEdge{m_outGraphp, lMoveVtxp, vMoveVtxp, 1};
|
2018-06-23 05:01:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-08-25 16:17:38 +02:00
|
|
|
|
|
|
|
|
// Return the T_MoveVertex for this (var, domain) pair, iff it has downstream dependencies,
|
|
|
|
|
// otherwise return nullptr.
|
|
|
|
|
T_MoveVertex* iterateVarVertex(const OrderVarVertex* vvtxp, AstSenTree* domainp) {
|
|
|
|
|
T_MoveVertex* vMoveVtxp = nullptr;
|
|
|
|
|
// Search forward from vvtxp, making new edges from vMoveVtxp forward
|
|
|
|
|
for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
if (edgep->weight() == 0) continue; // Was cut
|
|
|
|
|
|
|
|
|
|
// OrderGraph is a bipartite graph, so we know it's an OrderLogicVertex
|
|
|
|
|
const OrderLogicVertex* const lvtxp
|
|
|
|
|
= static_cast<const OrderLogicVertex*>(edgep->top());
|
|
|
|
|
|
|
|
|
|
// Do not construct dependencies across exclusive domains.
|
|
|
|
|
if (domainsExclusive(domainp, lvtxp->domainp())) continue;
|
|
|
|
|
|
|
|
|
|
// there is a path from this vvtx to a logic vertex. Add the new edge.
|
|
|
|
|
if (!vMoveVtxp) vMoveVtxp = m_vxMakerp->makeVertexp(nullptr, vvtxp, domainp);
|
|
|
|
|
T_MoveVertex* const lMoveVxp = static_cast<T_MoveVertex*>(lvtxp->userp());
|
|
|
|
|
new V3GraphEdge{m_outGraphp, vMoveVtxp, lMoveVxp, 1};
|
|
|
|
|
}
|
|
|
|
|
return vMoveVtxp;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
VL_UNCOPYABLE(ProcessMoveBuildGraph);
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-25 16:17:38 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// OrderMoveVertexMaker and related
|
2018-06-23 05:01:50 +02:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class OrderMoveVertexMaker final : public ProcessMoveBuildGraph<OrderMoveVertex>::MoveVertexMaker {
|
2018-06-23 05:01:50 +02:00
|
|
|
// MEMBERS
|
|
|
|
|
V3Graph* m_pomGraphp;
|
|
|
|
|
V3List<OrderMoveVertex*>* m_pomWaitingp;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-04-15 13:58:34 +02:00
|
|
|
OrderMoveVertexMaker(V3Graph* pomGraphp, V3List<OrderMoveVertex*>* pomWaitingp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_pomGraphp{pomGraphp}
|
|
|
|
|
, m_pomWaitingp{pomWaitingp} {}
|
2018-06-23 05:01:50 +02:00
|
|
|
// METHODS
|
2022-09-16 12:22:11 +02:00
|
|
|
OrderMoveVertex* makeVertexp(OrderLogicVertex* lvertexp, const OrderEitherVertex*,
|
2022-09-16 17:15:10 +02:00
|
|
|
const AstSenTree* domainp) override {
|
2022-11-20 19:11:01 +01:00
|
|
|
OrderMoveVertex* const resultp = new OrderMoveVertex{m_pomGraphp, lvertexp};
|
2022-05-25 21:16:19 +02:00
|
|
|
AstScope* const scopep = lvertexp ? lvertexp->scopep() : nullptr;
|
2018-06-23 05:01:50 +02:00
|
|
|
resultp->domScopep(OrderMoveDomScope::findCreate(domainp, scopep));
|
|
|
|
|
resultp->m_pomWaitingE.pushBack(*m_pomWaitingp, resultp);
|
|
|
|
|
return resultp;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
private:
|
|
|
|
|
VL_UNCOPYABLE(OrderMoveVertexMaker);
|
|
|
|
|
};
|
2013-02-27 04:26:47 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class OrderMTaskMoveVertexMaker final
|
|
|
|
|
: public ProcessMoveBuildGraph<MTaskMoveVertex>::MoveVertexMaker {
|
2018-07-23 02:54:28 +02:00
|
|
|
V3Graph* m_pomGraphp;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
public:
|
|
|
|
|
explicit OrderMTaskMoveVertexMaker(V3Graph* pomGraphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_pomGraphp{pomGraphp} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
MTaskMoveVertex* makeVertexp(OrderLogicVertex* lvertexp, const OrderEitherVertex* varVertexp,
|
2022-09-16 17:15:10 +02:00
|
|
|
const AstSenTree* domainp) override {
|
2022-09-17 14:56:41 +02:00
|
|
|
return new MTaskMoveVertex{m_pomGraphp, lvertexp, varVertexp, domainp};
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
private:
|
|
|
|
|
VL_UNCOPYABLE(OrderMTaskMoveVertexMaker);
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class OrderVerticesByDomainThenScope final {
|
2018-07-23 02:54:28 +02:00
|
|
|
PartPtrIdMap m_ids;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
public:
|
2022-08-26 19:14:53 +02:00
|
|
|
bool operator()(const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const {
|
2022-09-02 12:29:02 +02:00
|
|
|
const MTaskMoveVertex* const l_vxp = static_cast<const MTaskMoveVertex*>(lhsp);
|
|
|
|
|
const MTaskMoveVertex* const r_vxp = static_cast<const MTaskMoveVertex*>(rhsp);
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t l_id = m_ids.findId(l_vxp->domainp());
|
|
|
|
|
uint64_t r_id = m_ids.findId(r_vxp->domainp());
|
2018-07-23 02:54:28 +02:00
|
|
|
if (l_id < r_id) return true;
|
|
|
|
|
if (l_id > r_id) return false;
|
|
|
|
|
l_id = m_ids.findId(l_vxp->scopep());
|
|
|
|
|
r_id = m_ids.findId(r_vxp->scopep());
|
|
|
|
|
return l_id < r_id;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-26 19:14:53 +02:00
|
|
|
struct MTaskVxIdLessThan final {
|
2018-07-23 02:54:28 +02:00
|
|
|
// Sort vertex's, which must be AbstractMTask's, into a deterministic
|
|
|
|
|
// order by comparing their serial IDs.
|
2022-08-26 19:14:53 +02:00
|
|
|
bool operator()(const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const {
|
2022-09-02 12:29:02 +02:00
|
|
|
const AbstractMTask* const lmtaskp = static_cast<const AbstractLogicMTask*>(lhsp);
|
|
|
|
|
const AbstractMTask* const rmtaskp = static_cast<const AbstractLogicMTask*>(rhsp);
|
2018-07-23 02:54:28 +02:00
|
|
|
return lmtaskp->id() < rmtaskp->id();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2021-10-17 12:40:44 +02:00
|
|
|
// OrderProcess class
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-01-02 16:32:35 +01:00
|
|
|
class OrderProcess final : VNDeleter {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
2022-02-11 18:03:12 +01:00
|
|
|
// AstNode::user4 -> Used by V3Const::constifyExpensiveEdit
|
2021-06-19 15:40:31 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2021-10-17 12:40:44 +02:00
|
|
|
OrderGraph& m_graph; // The ordering graph
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Map from Trigger reference AstSenItem to the original AstSenTree
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& m_trigToSen;
|
|
|
|
|
|
2022-07-14 12:06:20 +02:00
|
|
|
// This is a function provided by the invoker of the ordering that can provide additional
|
2022-05-15 17:03:32 +02:00
|
|
|
// sensitivity expression that when triggered indicates the passed AstVarScope might have
|
|
|
|
|
// changed external to the code being ordered.
|
2022-07-14 12:06:20 +02:00
|
|
|
const V3Order::ExternalDomainsProvider m_externalDomains;
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
SenTreeFinder m_finder; // Global AstSenTree manager
|
|
|
|
|
AstSenTree* const m_deleteDomainp; // Dummy AstSenTree indicating needs deletion
|
2022-12-03 00:46:38 +01:00
|
|
|
const string m_tag; // Substring to add to generated names
|
2022-05-15 17:03:32 +02:00
|
|
|
const bool m_slow; // Ordering slow code
|
|
|
|
|
std::vector<AstNode*> m_result; // The result nodes (~statements) in their sequential order
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2020-08-15 19:11:27 +02:00
|
|
|
AstCFunc* m_pomNewFuncp = nullptr; // Current function being created
|
|
|
|
|
int m_pomNewStmts = 0; // Statements in function being created
|
2020-04-15 13:58:34 +02:00
|
|
|
V3Graph m_pomGraph; // Graph of logic elements to move
|
|
|
|
|
V3List<OrderMoveVertex*> m_pomWaiting; // List of nodes needing inputs to become ready
|
2006-08-26 13:35:28 +02:00
|
|
|
friend class OrderMoveDomScope;
|
2020-04-15 13:58:34 +02:00
|
|
|
V3List<OrderMoveDomScope*> m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId
|
2022-02-11 18:03:12 +01:00
|
|
|
std::map<std::pair<AstNodeModule*, std::string>, unsigned> m_funcNums; // Function ordinals
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
void process(bool multiThreaded);
|
2006-08-26 13:35:28 +02:00
|
|
|
void processDomains();
|
|
|
|
|
void processDomainsIterate(OrderEitherVertex* vertexp);
|
|
|
|
|
void processEdgeReport();
|
|
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
// processMove* routines schedule serial execution
|
2006-08-26 13:35:28 +02:00
|
|
|
void processMove();
|
|
|
|
|
void processMoveClear();
|
|
|
|
|
void processMoveBuildGraph();
|
|
|
|
|
void processMovePrepReady();
|
|
|
|
|
void processMoveReadyOne(OrderMoveVertex* vertexp);
|
|
|
|
|
void processMoveDoneOne(OrderMoveVertex* vertexp);
|
2022-11-22 03:40:49 +01:00
|
|
|
void processMoveOne(OrderMoveVertex* vertexp, const OrderMoveDomScope* domScopep, int level);
|
2020-04-15 13:58:34 +02:00
|
|
|
AstActive* processMoveOneLogic(const OrderLogicVertex* lvertexp, AstCFunc*& newFuncpr,
|
|
|
|
|
int& newStmtsr);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
// processMTask* routines schedule threaded execution
|
|
|
|
|
struct MTaskState {
|
2020-08-15 19:11:27 +02:00
|
|
|
AstMTaskBody* m_mtaskBodyp = nullptr;
|
2021-03-12 23:26:53 +01:00
|
|
|
std::list<const OrderLogicVertex*> m_logics;
|
2020-08-15 19:11:27 +02:00
|
|
|
ExecMTask* m_execMTaskp = nullptr;
|
2020-11-17 01:56:16 +01:00
|
|
|
MTaskState() = default;
|
2018-07-23 02:54:28 +02:00
|
|
|
};
|
|
|
|
|
void processMTasks();
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
string cfuncName(AstNodeModule* modp, AstSenTree* domainp, AstScope* scopep,
|
|
|
|
|
AstNode* forWhatp) {
|
2022-05-15 17:03:32 +02:00
|
|
|
string name = "_" + m_tag;
|
|
|
|
|
name += domainp->isMulti() ? "_comb" : "_sequent";
|
2022-02-11 18:03:12 +01:00
|
|
|
name = name + "__" + scopep->nameDotless();
|
|
|
|
|
const unsigned funcnum = m_funcNums.emplace(std::make_pair(modp, name), 0).first->second++;
|
|
|
|
|
name = name + "__" + cvtToStr(funcnum);
|
2018-05-20 15:12:29 +02:00
|
|
|
if (v3Global.opt.profCFuncs()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
name += "__PROF__" + forWhatp->fileline()->profileFuncname();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return name;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Make a domain that merges the two domains
|
|
|
|
|
AstSenTree* combineDomains(AstSenTree* ap, AstSenTree* bp) {
|
2022-07-14 12:06:20 +02:00
|
|
|
if (ap == m_deleteDomainp) return bp;
|
|
|
|
|
UASSERT_OBJ(bp != m_deleteDomainp, bp, "Should not be delete domain");
|
2022-05-15 17:03:32 +02:00
|
|
|
AstSenTree* const senTreep = ap->cloneTree(false);
|
|
|
|
|
senTreep->addSensesp(bp->sensesp()->cloneTree(true));
|
|
|
|
|
V3Const::constifyExpensiveEdit(senTreep); // Remove duplicates
|
|
|
|
|
senTreep->multi(true); // Comment that it was made from 2 domains
|
|
|
|
|
AstSenTree* const resultp = m_finder.getSenTree(senTreep);
|
|
|
|
|
VL_DO_DANGLING(senTreep->deleteTree(), senTreep); // getSenTree clones, so delete this
|
|
|
|
|
return resultp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
|
|
|
|
// Only for member initialization in constructor
|
|
|
|
|
static AstSenTree* makeDeleteDomainSenTree(FileLine* fl) {
|
2022-05-15 17:03:32 +02:00
|
|
|
return new AstSenTree{fl, new AstSenItem{fl, AstSenItem::Illegal{}}};
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
// CONSTRUCTOR
|
2022-05-15 17:03:32 +02:00
|
|
|
OrderProcess(AstNetlist* netlistp, OrderGraph& graph,
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen,
|
2022-08-02 16:47:14 +02:00
|
|
|
const string& tag, bool slow,
|
|
|
|
|
const V3Order::ExternalDomainsProvider& externalDomains)
|
2021-10-17 12:40:44 +02:00
|
|
|
: m_graph{graph}
|
2022-05-15 17:03:32 +02:00
|
|
|
, m_trigToSen{trigToSen}
|
2022-07-14 12:06:20 +02:00
|
|
|
, m_externalDomains{externalDomains}
|
2021-10-17 12:40:44 +02:00
|
|
|
, m_finder{netlistp}
|
|
|
|
|
, m_deleteDomainp{makeDeleteDomainSenTree(netlistp->fileline())}
|
2022-05-15 17:03:32 +02:00
|
|
|
, m_tag{tag}
|
|
|
|
|
, m_slow{slow} {
|
2021-10-17 12:40:44 +02:00
|
|
|
pushDeletep(m_deleteDomainp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-10-17 12:40:44 +02:00
|
|
|
|
2022-07-30 16:26:16 +02:00
|
|
|
~OrderProcess() override = default;
|
2018-06-22 05:08:56 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
public:
|
|
|
|
|
// Order the logic
|
2022-05-15 17:03:32 +02:00
|
|
|
static std::vector<AstNode*>
|
|
|
|
|
main(AstNetlist* netlistp, OrderGraph& graph,
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen,
|
|
|
|
|
const string& tag, bool parallel, bool slow,
|
2022-08-02 16:47:14 +02:00
|
|
|
const V3Order::ExternalDomainsProvider& externalDomains) {
|
2022-07-14 12:06:20 +02:00
|
|
|
OrderProcess visitor{netlistp, graph, trigToSen, tag, slow, externalDomains};
|
2022-05-15 17:03:32 +02:00
|
|
|
visitor.process(parallel);
|
|
|
|
|
return std::move(visitor.m_result);
|
2021-10-17 12:40:44 +02:00
|
|
|
}
|
|
|
|
|
};
|
2018-06-22 05:08:56 +02:00
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// OrderMoveDomScope methods
|
|
|
|
|
|
|
|
|
|
// Check the domScope is on ready list, add if not
|
2022-09-16 14:17:38 +02:00
|
|
|
void OrderMoveDomScope::ready(OrderProcess* opp) {
|
2018-06-23 05:01:50 +02:00
|
|
|
if (!m_onReadyList) {
|
|
|
|
|
m_onReadyList = true;
|
2021-10-17 12:40:44 +02:00
|
|
|
m_readyDomScopeE.pushBack(opp->m_pomReadyDomScope, this);
|
2018-06-23 05:01:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark one vertex as finished, remove from ready list if done
|
2022-09-16 14:17:38 +02:00
|
|
|
void OrderMoveDomScope::movedVertex(OrderProcess* opp, OrderMoveVertex* vertexp) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_onReadyList, vertexp,
|
|
|
|
|
"Moving vertex from ready when nothing was on que as ready.");
|
2018-08-23 11:09:12 +02:00
|
|
|
if (m_readyVertices.empty()) { // Else more work to get to later
|
2018-06-23 05:01:50 +02:00
|
|
|
m_onReadyList = false;
|
2021-10-17 12:40:44 +02:00
|
|
|
m_readyDomScopeE.unlink(opp->m_pomReadyDomScope, this);
|
2018-06-23 05:01:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 05:08:56 +02:00
|
|
|
//######################################################################
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processDomains() {
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
OrderEitherVertex* const vertexp = dynamic_cast<OrderEitherVertex*>(itp);
|
2018-06-14 00:05:00 +02:00
|
|
|
UASSERT(vertexp, "Null or vertex not derived from EitherVertex");
|
2019-05-19 22:13:13 +02:00
|
|
|
processDomainsIterate(vertexp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// The graph routines have already sorted the vertexes and edges into best->worst order
|
|
|
|
|
// Assign clock domains to each signal.
|
|
|
|
|
// Sequential logic is forced into the same sequential domain.
|
|
|
|
|
// Combo logic may be pushed into a seq domain if all its inputs are the same domain,
|
|
|
|
|
// else, if all inputs are from flops, it's end-of-sequential code
|
|
|
|
|
// else, it's full combo code
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vertexp->domainp()) return; // Already processed, or sequential logic
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " pdi: " << vertexp << endl);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstSenTree* domainp = nullptr;
|
2022-05-15 17:03:32 +02:00
|
|
|
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
|
|
|
|
|
domainp = lvtxp->hybridp();
|
|
|
|
|
}
|
2022-07-14 12:06:20 +02:00
|
|
|
|
|
|
|
|
std::vector<AstSenTree*> externalDomainps;
|
|
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
OrderEitherVertex* const fromVertexp = static_cast<OrderEitherVertex*>(edgep->fromp());
|
|
|
|
|
if (edgep->weight() && fromVertexp->domainMatters()) {
|
|
|
|
|
AstSenTree* fromDomainp = fromVertexp->domainp();
|
2022-05-16 21:02:49 +02:00
|
|
|
UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains");
|
|
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
if (OrderVarVertex* const varVtxp = dynamic_cast<OrderVarVertex*>(fromVertexp)) {
|
2022-05-16 21:02:49 +02:00
|
|
|
AstVarScope* const vscp = varVtxp->vscp();
|
2022-07-14 12:06:20 +02:00
|
|
|
// Add in any external domains
|
|
|
|
|
externalDomainps.clear();
|
|
|
|
|
m_externalDomains(vscp, externalDomainps);
|
|
|
|
|
for (AstSenTree* const externalDomainp : externalDomainps) {
|
|
|
|
|
UASSERT_OBJ(!externalDomainp->hasCombo(), vscp,
|
|
|
|
|
"There should be no need for combinational domains");
|
|
|
|
|
fromDomainp = combineDomains(fromDomainp, externalDomainp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Irrelevant input vertex (never triggered)
|
|
|
|
|
if (fromDomainp == m_deleteDomainp) continue;
|
|
|
|
|
|
|
|
|
|
// First input to this vertex
|
|
|
|
|
if (!domainp) domainp = fromDomainp;
|
|
|
|
|
|
|
|
|
|
// Make a domain that merges the two domains
|
|
|
|
|
if (domainp != fromDomainp) domainp = combineDomains(domainp, fromDomainp);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2022-05-16 21:02:49 +02:00
|
|
|
// If nothing triggers this vertex, we can delete the corresponding logic
|
|
|
|
|
if (!domainp) domainp = m_deleteDomainp;
|
|
|
|
|
|
|
|
|
|
// 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);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2018-06-22 05:08:56 +02:00
|
|
|
// OrderVisitor - Move graph construction
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processEdgeReport() {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Make report of all signal names and what clock edges they have
|
2022-05-15 17:03:32 +02:00
|
|
|
const string filename = v3Global.debugFilename(m_tag + "_order_edges.txt");
|
2021-07-12 00:42:01 +02:00
|
|
|
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
|
2020-04-15 13:58:34 +02:00
|
|
|
if (logp->fail()) v3fatal("Can't write " << filename);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-02-02 03:24:41 +01:00
|
|
|
std::deque<string> report;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Rebuild the trigger to original AstSenTree map using equality key comparison, as
|
|
|
|
|
// merging domains have created new AstSenTree instances which are not in the map
|
|
|
|
|
std::unordered_map<VNRef<const AstSenItem>, const AstSenTree*> trigToSen;
|
|
|
|
|
for (const auto& pair : m_trigToSen) trigToSen.emplace(*pair.first, pair.second);
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (OrderVarVertex* const vvertexp = dynamic_cast<OrderVarVertex*>(itp)) {
|
2022-05-16 21:02:49 +02:00
|
|
|
string name(vvertexp->vscp()->prettyName());
|
2020-04-15 13:58:34 +02:00
|
|
|
if (dynamic_cast<OrderVarPreVertex*>(itp)) {
|
|
|
|
|
name += " {PRE}";
|
|
|
|
|
} else if (dynamic_cast<OrderVarPostVertex*>(itp)) {
|
|
|
|
|
name += " {POST}";
|
|
|
|
|
} else if (dynamic_cast<OrderVarPordVertex*>(itp)) {
|
|
|
|
|
name += " {PORD}";
|
|
|
|
|
}
|
2018-02-02 03:24:41 +01:00
|
|
|
std::ostringstream os;
|
|
|
|
|
os.setf(std::ios::left);
|
2022-05-16 21:02:49 +02:00
|
|
|
os << " " << cvtToHex(vvertexp->vscp()) << " " << std::setw(50) << name << " ";
|
2022-05-15 17:03:32 +02:00
|
|
|
AstSenTree* const senTreep = vvertexp->domainp();
|
|
|
|
|
if (senTreep == m_deleteDomainp) {
|
|
|
|
|
os << "DELETED";
|
|
|
|
|
} else {
|
|
|
|
|
for (AstSenItem* senItemp = senTreep->sensesp(); senItemp;
|
|
|
|
|
senItemp = VN_AS(senItemp->nextp(), SenItem)) {
|
|
|
|
|
if (senItemp != senTreep->sensesp()) os << " or ";
|
|
|
|
|
const auto it = trigToSen.find(*senItemp);
|
|
|
|
|
if (it != trigToSen.end()) {
|
|
|
|
|
V3EmitV::verilogForTree(it->second, os);
|
|
|
|
|
} else {
|
|
|
|
|
V3EmitV::verilogForTree(senItemp, os);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
report.push_back(os.str());
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-19 03:03:23 +01:00
|
|
|
*logp << "Signals and their clock domains:\n";
|
2013-05-24 02:19:51 +02:00
|
|
|
stable_sort(report.begin(), report.end());
|
2020-11-19 03:03:23 +01:00
|
|
|
for (const string& i : report) *logp << i << '\n';
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMoveClear() {
|
2006-08-26 13:35:28 +02:00
|
|
|
OrderMoveDomScope::clear();
|
|
|
|
|
m_pomWaiting.reset();
|
|
|
|
|
m_pomReadyDomScope.reset();
|
|
|
|
|
m_pomGraph.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMoveBuildGraph() {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Build graph of only vertices
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " MoveBuildGraph\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
processMoveClear();
|
2021-10-17 12:40:44 +02:00
|
|
|
// Vertex::user->OrderMoveVertex*, last edge added or nullptr=none
|
|
|
|
|
m_pomGraph.userClearVertices();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-06-23 05:01:50 +02:00
|
|
|
OrderMoveVertexMaker createOrderMoveVertex(&m_pomGraph, &m_pomWaiting);
|
2022-08-25 16:17:38 +02:00
|
|
|
ProcessMoveBuildGraph<OrderMoveVertex> serialPMBG(&m_graph, &m_pomGraph, m_trigToSen,
|
2020-04-15 13:58:34 +02:00
|
|
|
&createOrderMoveVertex);
|
2018-06-23 05:01:50 +02:00
|
|
|
serialPMBG.build();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2018-06-22 05:08:56 +02:00
|
|
|
// OrderVisitor - Moving
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMove() {
|
2006-08-26 13:35:28 +02:00
|
|
|
// The graph routines have already sorted the vertexes and edges into best->worst order
|
|
|
|
|
// Make a new waiting graph with only OrderLogicVertex's
|
|
|
|
|
// (Order is preserved in the recreation so the sorting is preserved)
|
|
|
|
|
// Move any node with all inputs ready to a "ready" graph mapped by domain and then scope
|
2019-05-19 22:13:13 +02:00
|
|
|
// While waiting graph ! empty (and also known: something in ready graph)
|
2006-08-26 13:35:28 +02:00
|
|
|
// For all scopes in domain of top ready vertex
|
|
|
|
|
// For all vertexes in domain&scope of top ready vertex
|
2019-05-19 22:13:13 +02:00
|
|
|
// Make ordered activation block for this module
|
|
|
|
|
// Add that new activation to the list of calls to make.
|
|
|
|
|
// Move logic to ordered active
|
|
|
|
|
// Any children that have all inputs now ready move from waiting->ready graph
|
|
|
|
|
// (This may add nodes the for loop directly above needs to detext)
|
2006-08-26 13:35:28 +02:00
|
|
|
processMovePrepReady();
|
|
|
|
|
|
|
|
|
|
// New domain... another loop
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " MoveIterate\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
while (!m_pomReadyDomScope.empty()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Start with top node on ready list's domain & scope
|
|
|
|
|
OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin();
|
2021-11-26 23:55:36 +01:00
|
|
|
OrderMoveVertex* const topVertexp
|
|
|
|
|
= domScopep->readyVertices().begin(); // lintok-begin-on-ref
|
2019-05-19 22:13:13 +02:00
|
|
|
UASSERT(topVertexp, "domScope on ready list without any nodes ready under it");
|
|
|
|
|
// Work on all scopes ready inside this domain
|
|
|
|
|
while (domScopep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, " MoveDomain l=" << domScopep->domainp() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Process all nodes ready under same domain & scope
|
2020-08-15 16:12:55 +02:00
|
|
|
m_pomNewFuncp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
while (OrderMoveVertex* vertexp
|
|
|
|
|
= domScopep->readyVertices().begin()) { // lintok-begin-on-ref
|
2019-05-19 22:13:13 +02:00
|
|
|
processMoveOne(vertexp, domScopep, 1);
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
// Done with scope/domain pair, pick new scope under same domain, or nullptr if none
|
|
|
|
|
// left
|
|
|
|
|
OrderMoveDomScope* domScopeNextp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); huntp;
|
|
|
|
|
huntp = huntp->readyDomScopeNextp()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (huntp->domainp() == domScopep->domainp()) {
|
|
|
|
|
domScopeNextp = huntp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
domScopep = domScopeNextp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT(m_pomWaiting.empty(),
|
|
|
|
|
"Didn't converge; nodes waiting, none ready, perhaps some input activations lost.");
|
2006-08-26 13:35:28 +02:00
|
|
|
// Cleanup memory
|
|
|
|
|
processMoveClear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMovePrepReady() {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Make list of ready nodes
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " MovePrepReady\n");
|
|
|
|
|
for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp;) {
|
2021-11-26 23:55:36 +01:00
|
|
|
OrderMoveVertex* const nextp = vertexp->pomWaitingNextp();
|
2021-02-22 03:25:21 +01:00
|
|
|
if (vertexp->isWait() && vertexp->inEmpty()) processMoveReadyOne(vertexp);
|
2019-05-19 22:13:13 +02:00
|
|
|
vertexp = nextp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMoveReadyOne(OrderMoveVertex* vertexp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Recursive!
|
|
|
|
|
// Move one node from waiting to ready list
|
|
|
|
|
vertexp->setReady();
|
|
|
|
|
// Remove node from waiting list
|
|
|
|
|
vertexp->m_pomWaitingE.unlink(m_pomWaiting, vertexp);
|
2018-06-23 05:01:50 +02:00
|
|
|
if (vertexp->logicp()) {
|
|
|
|
|
// Add to ready list (indexed by domain and scope)
|
|
|
|
|
vertexp->m_readyVerticesE.pushBack(vertexp->domScopep()->m_readyVertices, vertexp);
|
|
|
|
|
vertexp->domScopep()->ready(this);
|
|
|
|
|
} else {
|
|
|
|
|
// vertexp represents a non-logic vertex.
|
|
|
|
|
// Recurse to mark its following neighbors ready.
|
|
|
|
|
processMoveDoneOne(vertexp);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMoveDoneOne(OrderMoveVertex* vertexp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Move one node from ready to completion
|
|
|
|
|
vertexp->setMoved();
|
|
|
|
|
// Unlink from ready lists
|
2018-06-23 05:01:50 +02:00
|
|
|
if (vertexp->logicp()) {
|
|
|
|
|
vertexp->m_readyVerticesE.unlink(vertexp->domScopep()->m_readyVertices, vertexp);
|
|
|
|
|
vertexp->domScopep()->movedVertex(this, vertexp);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
// Don't need to add it to another list, as we're done with it
|
|
|
|
|
// Mark our outputs as one closer to ready
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphEdge *edgep = vertexp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = edgep->outNextp();
|
2021-11-26 23:55:36 +01:00
|
|
|
OrderMoveVertex* const toVertexp = static_cast<OrderMoveVertex*>(edgep->top());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " Clear to " << (toVertexp->inEmpty() ? "[EMP] " : " ") << toVertexp
|
|
|
|
|
<< endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Delete this edge
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (toVertexp->inEmpty()) {
|
|
|
|
|
// If destination node now has all inputs resolved; recurse to move that vertex
|
|
|
|
|
// This is thus depth first (before width) which keeps the
|
|
|
|
|
// resulting executable's d-cache happy.
|
|
|
|
|
processMoveReadyOne(toVertexp);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-22 03:40:49 +01:00
|
|
|
void OrderProcess::processMoveOne(OrderMoveVertex* vertexp, const OrderMoveDomScope* domScopep,
|
2020-04-15 13:58:34 +02:00
|
|
|
int level) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(vertexp->domScopep() == domScopep, vertexp, "Domain mismatch; list misbuilt?");
|
2021-11-26 23:55:36 +01:00
|
|
|
const OrderLogicVertex* const lvertexp = vertexp->logicp();
|
|
|
|
|
const AstScope* const scopep = lvertexp->scopep();
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " POSmove l" << std::setw(3) << level << " d=" << cvtToHex(lvertexp->domainp())
|
|
|
|
|
<< " s=" << cvtToHex(scopep) << " " << lvertexp << endl);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstActive* const newActivep
|
2020-04-15 13:58:34 +02:00
|
|
|
= processMoveOneLogic(lvertexp, m_pomNewFuncp /*ref*/, m_pomNewStmts /*ref*/);
|
2022-05-15 17:03:32 +02:00
|
|
|
if (newActivep) m_result.push_back(newActivep);
|
2018-06-23 04:56:58 +02:00
|
|
|
processMoveDoneOne(vertexp);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp,
|
2020-04-15 13:58:34 +02:00
|
|
|
AstCFunc*& newFuncpr, int& newStmtsr) {
|
2020-08-15 16:12:55 +02:00
|
|
|
AstActive* activep = nullptr;
|
2021-05-11 13:44:07 +02:00
|
|
|
AstScope* const scopep = lvertexp->scopep();
|
|
|
|
|
AstSenTree* const domainp = lvertexp->domainp();
|
2006-08-26 13:35:28 +02:00
|
|
|
AstNode* nodep = lvertexp->nodep();
|
2021-10-17 12:40:44 +02:00
|
|
|
AstNodeModule* const modp = scopep->modp();
|
2020-08-15 16:12:55 +02:00
|
|
|
UASSERT(modp, "nullptr");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2022-05-16 21:02:49 +02:00
|
|
|
// We are move the logic into a CFunc, so unlink it from the AstActive
|
|
|
|
|
nodep->unlinkFrBack();
|
2021-05-11 13:44:07 +02:00
|
|
|
|
2022-05-16 21:02:49 +02:00
|
|
|
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
|
|
|
|
|
// procedures. Everything else is handled in one go
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
bool suspendable = false;
|
|
|
|
|
bool slow = m_slow;
|
2022-05-16 21:02:49 +02:00
|
|
|
if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
suspendable = procp->isSuspendable();
|
|
|
|
|
if (suspendable) slow = slow && !VN_IS(procp, Always);
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep = procp->stmtsp();
|
2022-05-16 21:02:49 +02:00
|
|
|
pushDeletep(procp);
|
|
|
|
|
}
|
2021-05-11 13:44:07 +02:00
|
|
|
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
// Put suspendable processes into individual functions on their own
|
|
|
|
|
if (suspendable) newFuncpr = nullptr;
|
|
|
|
|
|
2022-05-16 21:02:49 +02:00
|
|
|
// 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)
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (!suspendable && !v3Global.opt.profCFuncs()
|
2022-05-16 21:02:49 +02:00
|
|
|
&& (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);
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
newFuncpr
|
|
|
|
|
= new AstCFunc{nodep->fileline(), name, scopep, suspendable ? "VlCoroutine" : ""};
|
2022-05-16 21:02:49 +02:00
|
|
|
newFuncpr->isStatic(false);
|
|
|
|
|
newFuncpr->isLoose(true);
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
newFuncpr->slow(slow);
|
2022-05-16 21:02:49 +02:00
|
|
|
newStmtsr = 0;
|
2022-09-15 20:43:56 +02:00
|
|
|
scopep->addBlocksp(newFuncpr);
|
2022-05-16 21:02:49 +02:00
|
|
|
// Create top call to it
|
2022-09-17 14:56:41 +02:00
|
|
|
AstCCall* const callp = new AstCCall{nodep->fileline(), newFuncpr};
|
2022-10-12 11:19:21 +02:00
|
|
|
callp->dtypeSetVoid();
|
2022-05-16 21:02:49 +02:00
|
|
|
// Where will we be adding the call?
|
2022-09-17 14:56:41 +02:00
|
|
|
AstActive* const newActivep = new AstActive{nodep->fileline(), name, domainp};
|
2022-10-12 11:19:21 +02:00
|
|
|
newActivep->addStmtsp(callp->makeStmt());
|
2022-05-16 21:02:49 +02:00
|
|
|
if (!activep) {
|
|
|
|
|
activep = newActivep;
|
2021-05-11 13:44:07 +02:00
|
|
|
} else {
|
2022-05-16 21:02:49 +02:00
|
|
|
activep->addNext(newActivep);
|
2021-05-11 13:44:07 +02:00
|
|
|
}
|
2022-05-16 21:02:49 +02:00
|
|
|
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();
|
2021-05-11 13:44:07 +02:00
|
|
|
|
2022-05-16 21:02:49 +02:00
|
|
|
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();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-05-16 21:02:49 +02:00
|
|
|
|
|
|
|
|
nodep = nextp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
// Put suspendable processes into individual functions on their own
|
|
|
|
|
if (suspendable) newFuncpr = nullptr;
|
2022-05-16 21:02:49 +02:00
|
|
|
|
2018-06-23 04:56:58 +02:00
|
|
|
return activep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-17 12:40:44 +02:00
|
|
|
void OrderProcess::processMTasks() {
|
2018-07-23 02:54:28 +02:00
|
|
|
// For nondeterminism debug:
|
|
|
|
|
V3Partition::hashGraphDebug(&m_graph, "V3Order's m_graph");
|
|
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// We already produced a graph of every var, input, and logic
|
2018-07-23 02:54:28 +02:00
|
|
|
// block and all dependencies; this is 'm_graph'.
|
|
|
|
|
//
|
|
|
|
|
// Now, starting from m_graph, make a slightly-coarsened graph representing
|
|
|
|
|
// only logic, and discarding edges we know we can ignore.
|
|
|
|
|
// This is quite similar to the 'm_pomGraph' of the serial code gen:
|
|
|
|
|
V3Graph logicGraph;
|
|
|
|
|
OrderMTaskMoveVertexMaker create_mtask_vertex(&logicGraph);
|
2022-08-25 16:17:38 +02:00
|
|
|
ProcessMoveBuildGraph<MTaskMoveVertex> mtask_pmbg(&m_graph, &logicGraph, m_trigToSen,
|
2022-05-15 17:03:32 +02:00
|
|
|
&create_mtask_vertex);
|
2018-07-23 02:54:28 +02:00
|
|
|
mtask_pmbg.build();
|
|
|
|
|
|
|
|
|
|
// Needed? We do this for m_pomGraph in serial mode, so do it here too:
|
|
|
|
|
logicGraph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
|
|
|
|
|
// Partition logicGraph into LogicMTask's. The partitioner will annotate
|
|
|
|
|
// each vertex in logicGraph with a 'color' which is really an mtask ID
|
|
|
|
|
// in this context.
|
2022-09-01 15:24:02 +02:00
|
|
|
V3Partition partitioner(&m_graph, &logicGraph);
|
2018-07-23 02:54:28 +02:00
|
|
|
V3Graph mtasks;
|
|
|
|
|
partitioner.go(&mtasks);
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
std::unordered_map<unsigned /*mtask id*/, MTaskState> mtaskStates;
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
// Iterate through the entire logicGraph. For each logic node,
|
|
|
|
|
// attach it to a per-MTask ordered list of logic nodes.
|
|
|
|
|
// This is the order we'll execute logic nodes within the MTask.
|
|
|
|
|
//
|
|
|
|
|
// MTasks may span scopes and domains, so sort by both here:
|
|
|
|
|
GraphStream<OrderVerticesByDomainThenScope> emit_logic(&logicGraph);
|
|
|
|
|
const V3GraphVertex* moveVxp;
|
|
|
|
|
while ((moveVxp = emit_logic.nextp())) {
|
2022-09-02 12:29:02 +02:00
|
|
|
const MTaskMoveVertex* const movep = static_cast<const MTaskMoveVertex*>(moveVxp);
|
2022-09-01 17:29:40 +02:00
|
|
|
// Only care about logic vertices
|
|
|
|
|
if (!movep->logicp()) continue;
|
|
|
|
|
|
2021-06-21 00:32:57 +02:00
|
|
|
const unsigned mtaskId = movep->color();
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT(mtaskId > 0, "Every MTaskMoveVertex should have an mtask assignment >0");
|
2022-09-01 17:29:40 +02:00
|
|
|
|
|
|
|
|
// Add this logic to the per-mtask order
|
|
|
|
|
mtaskStates[mtaskId].m_logics.push_back(movep->logicp());
|
|
|
|
|
|
|
|
|
|
// Since we happen to be iterating over every logic node,
|
|
|
|
|
// take this opportunity to annotate each AstVar with the id's
|
|
|
|
|
// of mtasks that consume it and produce it. We'll use this
|
|
|
|
|
// information in V3EmitC when we lay out var's in memory.
|
|
|
|
|
const OrderLogicVertex* const logicp = movep->logicp();
|
|
|
|
|
for (const V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
const OrderVarVertex* const pre_varp
|
|
|
|
|
= dynamic_cast<const OrderVarVertex*>(edgep->fromp());
|
|
|
|
|
if (!pre_varp) continue;
|
|
|
|
|
AstVar* const varp = pre_varp->vscp()->varp();
|
|
|
|
|
// varp depends on logicp, so logicp produces varp,
|
|
|
|
|
// and vice-versa below
|
|
|
|
|
varp->addProducingMTaskId(mtaskId);
|
|
|
|
|
}
|
|
|
|
|
for (const V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
const OrderVarVertex* const post_varp
|
|
|
|
|
= dynamic_cast<const OrderVarVertex*>(edgep->top());
|
|
|
|
|
if (!post_varp) continue;
|
|
|
|
|
AstVar* const varp = post_varp->vscp()->varp();
|
|
|
|
|
varp->addConsumingMTaskId(mtaskId);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2022-09-01 17:29:40 +02:00
|
|
|
// TODO? We ignore IO vars here, so those will have empty mtask
|
|
|
|
|
// signatures. But we could also give those mtask signatures.
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the AstExecGraph node which represents the execution
|
|
|
|
|
// of the MTask graph.
|
2021-11-26 23:55:36 +01:00
|
|
|
FileLine* const rootFlp = v3Global.rootp()->fileline();
|
2022-09-06 15:59:40 +02:00
|
|
|
AstExecGraph* const execGraphp = new AstExecGraph{rootFlp, m_tag};
|
2022-05-15 17:03:32 +02:00
|
|
|
m_result.push_back(execGraphp);
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
// Create CFuncs and bodies for each MTask.
|
|
|
|
|
GraphStream<MTaskVxIdLessThan> emit_mtasks(&mtasks);
|
|
|
|
|
const V3GraphVertex* mtaskVxp;
|
|
|
|
|
while ((mtaskVxp = emit_mtasks.nextp())) {
|
2022-09-02 12:29:02 +02:00
|
|
|
const AbstractLogicMTask* const mtaskp = static_cast<const AbstractLogicMTask*>(mtaskVxp);
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
// Create a body for this mtask
|
2022-11-20 19:11:01 +01:00
|
|
|
AstMTaskBody* const bodyp = new AstMTaskBody{rootFlp};
|
2018-07-23 02:54:28 +02:00
|
|
|
MTaskState& state = mtaskStates[mtaskp->id()];
|
|
|
|
|
state.m_mtaskBodyp = bodyp;
|
|
|
|
|
|
|
|
|
|
// Create leaf CFunc's to run this mtask's logic,
|
|
|
|
|
// and create a set of AstActive's to call those CFuncs.
|
|
|
|
|
// Add the AstActive's into the AstMTaskBody.
|
2020-08-15 16:12:55 +02:00
|
|
|
const AstSenTree* last_domainp = nullptr;
|
|
|
|
|
AstCFunc* leafCFuncp = nullptr;
|
2018-07-23 02:54:28 +02:00
|
|
|
int leafStmts = 0;
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const OrderLogicVertex* logicp : state.m_logics) {
|
2018-07-23 02:54:28 +02:00
|
|
|
if (logicp->domainp() != last_domainp) {
|
|
|
|
|
// Start a new leaf function.
|
2020-08-15 16:12:55 +02:00
|
|
|
leafCFuncp = nullptr;
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
last_domainp = logicp->domainp();
|
|
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
AstActive* const newActivep
|
2020-04-15 13:58:34 +02:00
|
|
|
= processMoveOneLogic(logicp, leafCFuncp /*ref*/, leafStmts /*ref*/);
|
2018-07-23 02:54:28 +02:00
|
|
|
if (newActivep) bodyp->addStmtsp(newActivep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Translate the LogicMTask graph into the corresponding ExecMTask
|
|
|
|
|
// graph, which will outlive V3Order and persist for the remainder
|
|
|
|
|
// of verilator's processing.
|
|
|
|
|
// - The LogicMTask graph points to MTaskMoveVertex's
|
|
|
|
|
// and OrderLogicVertex's which are ephemeral to V3Order.
|
|
|
|
|
// - The ExecMTask graph and the AstMTaskBody's produced here
|
|
|
|
|
// persist until code generation time.
|
2022-04-10 12:37:41 +02:00
|
|
|
V3Graph* const depGraphp = execGraphp->depGraphp();
|
2022-11-20 19:11:01 +01:00
|
|
|
state.m_execMTaskp = new ExecMTask{depGraphp, bodyp, mtaskp->id()};
|
2018-07-23 02:54:28 +02:00
|
|
|
// Cross-link each ExecMTask and MTaskBody
|
|
|
|
|
// Q: Why even have two objects?
|
|
|
|
|
// A: One is an AstNode, the other is a GraphVertex,
|
|
|
|
|
// to combine them would involve multiple inheritance...
|
|
|
|
|
state.m_mtaskBodyp->execMTaskp(state.m_execMTaskp);
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphEdge* inp = mtaskp->inBeginp(); inp; inp = inp->inNextp()) {
|
2018-07-23 02:54:28 +02:00
|
|
|
const V3GraphVertex* fromVxp = inp->fromp();
|
2021-11-26 23:55:36 +01:00
|
|
|
const AbstractLogicMTask* const fromp
|
2022-09-02 12:29:02 +02:00
|
|
|
= static_cast<const AbstractLogicMTask*>(fromVxp);
|
2021-11-26 23:55:36 +01:00
|
|
|
const MTaskState& fromState = mtaskStates[fromp->id()];
|
2022-11-20 19:11:01 +01:00
|
|
|
new V3GraphEdge{depGraphp, fromState.m_execMTaskp, state.m_execMTaskp, 1};
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2022-09-15 20:43:56 +02:00
|
|
|
execGraphp->addMTaskBodiesp(bodyp);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2018-06-22 05:08:56 +02:00
|
|
|
// OrderVisitor - Top processing
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
void OrderProcess::process(bool multiThreaded) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Dump data
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_pre");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2013-02-27 04:26:47 +01:00
|
|
|
// Break cycles. Each strongly connected subgraph (including cutable
|
|
|
|
|
// edges) will have its own color, and corresponds to a loop in the
|
|
|
|
|
// original graph. However the new graph will be acyclic (the removed
|
|
|
|
|
// edges are actually still there, just with weight 0).
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " Acyclic & Order...\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_acyc");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// Assign ranks so we know what to follow
|
|
|
|
|
// Then, sort vertices and edges by that ordering
|
|
|
|
|
m_graph.order();
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_order");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-06-15 00:59:24 +02:00
|
|
|
// Assign logic vertices to new domains
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " Domains...\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
processDomains();
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_domain");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dump()) processEdgeReport();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
if (!multiThreaded) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " Construct Move Graph...\n");
|
2018-06-18 03:06:11 +02:00
|
|
|
processMoveBuildGraph();
|
2022-09-18 21:53:42 +02:00
|
|
|
// Different prefix (ordermv) as it's not the same graph
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_start");
|
2018-06-18 03:06:11 +02:00
|
|
|
m_pomGraph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_simpl");
|
2018-06-18 03:06:11 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " Move...\n");
|
2018-06-18 03:06:11 +02:00
|
|
|
processMove();
|
2018-07-23 02:54:28 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " Set up mtasks...\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
processMTasks();
|
2018-06-18 03:06:11 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// Dump data
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_done");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
namespace V3Order {
|
|
|
|
|
|
|
|
|
|
AstCFunc* order(AstNetlist* netlistp, //
|
|
|
|
|
const std::vector<V3Sched::LogicByScope*>& logic, //
|
|
|
|
|
const std::unordered_map<const AstSenItem*, const AstSenTree*>& trigToSen,
|
|
|
|
|
const string& tag, //
|
|
|
|
|
bool parallel, //
|
|
|
|
|
bool slow, //
|
2022-08-02 16:47:14 +02:00
|
|
|
const ExternalDomainsProvider& externalDomains) {
|
2022-05-15 17:03:32 +02:00
|
|
|
// Order the code
|
|
|
|
|
const std::unique_ptr<OrderGraph> graph
|
|
|
|
|
= OrderBuildVisitor::process(netlistp, logic, trigToSen);
|
2022-07-30 18:49:30 +02:00
|
|
|
const auto& nodeps
|
|
|
|
|
= OrderProcess::main(netlistp, *graph, trigToSen, tag, parallel, slow, externalDomains);
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Create the result function
|
|
|
|
|
AstScope* const scopeTopp = netlistp->topScopep()->scopep();
|
|
|
|
|
AstCFunc* const funcp = new AstCFunc{netlistp->fileline(), "_eval_" + tag, scopeTopp, ""};
|
|
|
|
|
funcp->dontCombine(true);
|
|
|
|
|
funcp->isStatic(false);
|
|
|
|
|
funcp->isLoose(true);
|
|
|
|
|
funcp->slow(slow);
|
|
|
|
|
funcp->isConst(false);
|
|
|
|
|
funcp->declPrivate(true);
|
2022-09-15 20:43:56 +02:00
|
|
|
scopeTopp->addBlocksp(funcp);
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
// Add ordered statements to the result function
|
|
|
|
|
for (AstNode* const nodep : nodeps) funcp->addStmtsp(nodep);
|
|
|
|
|
|
|
|
|
|
// Dispose of the remnants of the inputs
|
|
|
|
|
for (auto* const lbsp : logic) lbsp->deleteActives();
|
|
|
|
|
|
|
|
|
|
// Done
|
|
|
|
|
return funcp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
|
|
|
|
} // namespace V3Order
|