mirror of
https://github.com/verilator/verilator.git
synced 2026-09-06 00:47:26 +02:00
The OrderGraph used during V3Order step deliberately omits some variable accesses from the dependency graph. E.g.: a read of a variable that is in the reading block's own hybrid sensitivity list emits no edge, nor does a read ignored due to a force/release, nor an access to a variable marked 'ignoreSchedWrite' and friends. For serial mode that is fine, the logic runs one block at a time. In parallel mode two such blocks can run concurrently, and if one writes what the other reads, that is a data race at runtime. These accesses cannot be recovered from the graph edges. They are now collected from the AST while the OrderGraph is built, and held by the OrderLogicVertex performing them. FixDataHazards is reworked around these access lists stored in OrderLogicVertex, so it is now aware of all variable accesses the logic makes, including those not encoded by the dependency graph edges. The previous heuristic of fixing data hazards by merging same-rank MTasks is removed. Additional edges are inserted instead to prescribe a fixed ordering of conflicting MTasks. To insert edges without unduly increasing the critical path, or introducing cycles, new edges are added such that they preserve topological ordering, and they are inserted between vertices sorted by critical path length. See algorithm details in the code. Also add a data hazard checker under '--debug-partition', reporting every unordered accessor pair left in the final MTask graph. This fixes the race demonstrated by t_sched_hybrid_hazard (#7913), which is no longer expected to fail. Under ThreadSanitizer over the vltmt tests: 17 failing before, 3 after, with no regressions. The 3 remaining are different defects.
321 lines
13 KiB
C++
321 lines
13 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Ordering constraint graph
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify it
|
|
// under the terms of either the GNU Lesser General Public License Version 3
|
|
// or the Perl Artistic License Version 2.0.
|
|
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// OrderGraph is a bipartite graph, with the two parts being formed of only OrderLogicVertex and
|
|
// OrderVarVertex vertices respectively (i.e.: edges are always between OrderLogicVertex and
|
|
// OrderVarVertex, and never between two OrderLogicVertex or OrderVarVertex). The graph represents
|
|
// both fine-grained dependencies, and additional ordering constraints between logic blocks and
|
|
// variables. The fact that OrderGraph is bipartite is important and we take advantage of this fact
|
|
// in various algorithms, so this property must be maintained.
|
|
//
|
|
// Both OrderLogicVertex and OrderVarVertex derives from OrderEitherVertex, so OrderGraph is
|
|
// composed only of OrderEitherVertex vertices.
|
|
//
|
|
// OrderLogicVertex holds a 'logic block', which is just some computational construct that is
|
|
// ordered as a single unit. Ordering of these logic blocks is determined by the variables they
|
|
// read and write, which is represented by the edges between OrderLogicVertex and OrderVarVertex
|
|
// instances (and hence the graph is bipartite).
|
|
//
|
|
// OrderVarVertex is abstract, and has various concrete subtypes that represent various ordering
|
|
// constraints imposed by variables accessed by logic blocks. The concrete subtypes and their
|
|
// roles are:
|
|
//
|
|
// OrderVarStdVertex: Data dependencies for combinational logic and delayed assignment
|
|
// updates (AlwaysPost).
|
|
// OrderVarPostVertex: Ensures all sequential logic blocks reading a signal do so before any
|
|
// combinational or delayed assignments update that signal.
|
|
// OrderVarPordVertex: Ensures a _d = _q AlwaysPre used to implement delayed (non-blocking)
|
|
// assignments is the first write of a _d, before any sequential blocks
|
|
// write to that _d.
|
|
// OrderVarPreVertex: This is an optimization. Try to ensure that a _d = _q AlwaysPre is the
|
|
// last read of a _q, after all reads of that _q by sequential logic. The
|
|
// model is still correct if we cannot satisfy this due to other interfering
|
|
// constraints. If respecting this constraint is possible, then combined
|
|
// with the OrderVarPordVertex constraint we get that all writes to _d are
|
|
// after all reads of a _q, which then allows us to eliminate the _d
|
|
// completely and assign to the _q directly. This means these delayed
|
|
// assignments can be implemented without temporary storage (the redundant
|
|
// storage is eliminated in V3LifePost).
|
|
//
|
|
// Ordering constraints are represented by directed edges, where the source of an edge needs to be
|
|
// ordered before the sink of an edge. A constraint can be either hard (must be satisfied),
|
|
// represented by a non-cutable edge, or a constraint can be soft (ideally should be satisfied, but
|
|
// is ok not to if other hard constraints interfere), represented by a cutable edge. Edges
|
|
// otherwise carry no additional information. TODO: what about weight?
|
|
//
|
|
// Note: It is required for hard (non-cutable) constraints to form a DAG, but together with the
|
|
// soft constraints the graph can be arbitrary so long as it remains bipartite.
|
|
//
|
|
//*************************************************************************
|
|
|
|
#ifndef VERILATOR_V3ORDERGRAPH_H_
|
|
#define VERILATOR_V3ORDERGRAPH_H_
|
|
|
|
#include "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Ast.h"
|
|
#include "V3Graph.h"
|
|
|
|
#include <vector>
|
|
|
|
class OrderLogicVertex;
|
|
class OrderVarVertex;
|
|
|
|
//======================================================================
|
|
|
|
enum OrderWeights : uint8_t {
|
|
WEIGHT_COMBO = 1, // Breakable combo logic
|
|
WEIGHT_POST = 2, // Post-delayed used var
|
|
WEIGHT_PRE = 3, // Breakable pre-delayed used var
|
|
WEIGHT_MEDIUM = 8, // Medium weight just so dot graph looks nice
|
|
WEIGHT_NORMAL = 32 // High weight just so dot graph looks nice
|
|
};
|
|
|
|
//======================================================================
|
|
// Graph type
|
|
|
|
class OrderGraph final : public V3Graph {
|
|
public:
|
|
// METHODS
|
|
|
|
// Methods to add edges representing constraints, utilizing the type system to help us ensure
|
|
// the graph remains bipartite.
|
|
inline void addHardEdge(OrderLogicVertex* fromp, OrderVarVertex* top,
|
|
int weight) VL_MT_DISABLED;
|
|
inline void addHardEdge(OrderVarVertex* fromp, OrderLogicVertex* top,
|
|
int weight) VL_MT_DISABLED;
|
|
inline void addSoftEdge(OrderLogicVertex* fromp, OrderVarVertex* top,
|
|
int weight) VL_MT_DISABLED;
|
|
inline void addSoftEdge(OrderVarVertex* fromp, OrderLogicVertex* top,
|
|
int weight) VL_MT_DISABLED;
|
|
};
|
|
|
|
//======================================================================
|
|
// Vertex types
|
|
|
|
class OrderEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
|
VL_RTTI_IMPL(OrderEitherVertex, V3GraphVertex)
|
|
// Event domain of vertex. For OrderLogicVertex this represents the conditions when the logic
|
|
// block must be executed. For OrderVarVertex, this is the union of the domains of all the
|
|
// OrderLogicVertex vertices that drive the variable. If initially set to nullptr (e.g.: all
|
|
// OrderVarVertex and those OrderLogicVertices that represent combinational logic), then the
|
|
// ordering algorithm will compute the domain automatically based on the edges representing
|
|
// data-flow (those between OrderLogicVertex and OrderVarStdVertex), otherwise the domain is
|
|
// as given (e.g.: for those OrderLogicVertices that represent clocked logic).
|
|
AstSenTree* m_domainp;
|
|
|
|
protected:
|
|
// CONSTRUCTOR
|
|
OrderEitherVertex(OrderGraph* graphp, AstSenTree* domainp) VL_MT_DISABLED
|
|
: V3GraphVertex{graphp},
|
|
m_domainp{domainp} {}
|
|
~OrderEitherVertex() override = default;
|
|
|
|
public:
|
|
// METHODS
|
|
virtual bool domainMatters() = 0;
|
|
|
|
// ACCESSORS
|
|
AstSenTree* domainp() const VL_MT_STABLE { return m_domainp; }
|
|
void domainp(AstSenTree* domainp) VL_MT_DISABLED {
|
|
#if VL_DEBUG
|
|
UASSERT(!m_domainp, "Domain should only be set once");
|
|
#endif
|
|
m_domainp = domainp;
|
|
}
|
|
};
|
|
|
|
class OrderLogicVertex final : public OrderEitherVertex {
|
|
VL_RTTI_IMPL(OrderLogicVertex, OrderEitherVertex)
|
|
|
|
public:
|
|
// Variable access record
|
|
struct VarAccess final {
|
|
AstVarScope* m_vscp; // The variable accessed
|
|
VAccess m_access; // The kind of access, as in the AST
|
|
};
|
|
|
|
private:
|
|
AstNode* const m_nodep; // The logic this vertex represents
|
|
AstScope* const m_scopep; // Scope the logic is under
|
|
AstSenTree* const m_hybridp; // Additional sensitivities for hybrid combinational logic
|
|
// Every variable accessed by this logic, in order of first access, at most one record per
|
|
// variable. Only populated for multi-threaded ordering.
|
|
std::vector<VarAccess> m_varAccesses;
|
|
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderLogicVertex(OrderGraph* graphp, AstScope* scopep, AstSenTree* domainp,
|
|
AstSenTree* hybridp, AstNode* nodep) VL_MT_DISABLED
|
|
: OrderEitherVertex{graphp, domainp},
|
|
m_nodep{nodep},
|
|
m_scopep{scopep},
|
|
m_hybridp{hybridp} {
|
|
UASSERT_OBJ(scopep, nodep, "Must not be null");
|
|
UASSERT_OBJ(!(domainp && hybridp), nodep, "Cannot have bot domainp and hybridp set");
|
|
}
|
|
~OrderLogicVertex() override = default;
|
|
|
|
// METHODS
|
|
bool domainMatters() override { return true; }
|
|
|
|
// ACCESSORS
|
|
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
|
AstScope* scopep() const VL_MT_STABLE { return m_scopep; }
|
|
AstSenTree* hybridp() const { return m_hybridp; }
|
|
const std::vector<VarAccess>& varAccesses() const { return m_varAccesses; }
|
|
void addVarAccess(AstVarScope* vscp, VAccess access) {
|
|
m_varAccesses.push_back({vscp, access});
|
|
}
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string name() const override VL_MT_STABLE {
|
|
return (cvtToHex(m_nodep) + "\\n " + cvtToStr(nodep()->typeName()));
|
|
}
|
|
string dotShape() const override { return VN_IS(m_nodep, Active) ? "doubleoctagon" : "rect"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
class OrderVarVertex VL_NOT_FINAL : public OrderEitherVertex {
|
|
VL_RTTI_IMPL(OrderVarVertex, OrderEitherVertex)
|
|
AstVarScope* const m_vscp;
|
|
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderVarVertex(OrderGraph* graphp, AstVarScope* vscp) VL_MT_DISABLED
|
|
: OrderEitherVertex{graphp, nullptr},
|
|
m_vscp{vscp} {}
|
|
~OrderVarVertex() override = default;
|
|
|
|
// ACCESSORS
|
|
AstVarScope* vscp() const { return m_vscp; }
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string dotShape() const override final { return "ellipse"; }
|
|
virtual string nameSuffix() const VL_MT_SAFE = 0;
|
|
string name() const override final VL_MT_STABLE {
|
|
return cvtToHex(m_vscp) + " " + nameSuffix() + "\\n " + m_vscp->name();
|
|
}
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
class OrderVarStdVertex final : public OrderVarVertex {
|
|
VL_RTTI_IMPL(OrderVarStdVertex, OrderVarVertex)
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderVarStdVertex(OrderGraph* graphp, AstVarScope* vscp) VL_MT_DISABLED
|
|
: OrderVarVertex{graphp, vscp} {}
|
|
~OrderVarStdVertex() override = default;
|
|
|
|
// METHODS
|
|
bool domainMatters() override { return true; }
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string nameSuffix() const override VL_MT_SAFE { return ""; }
|
|
string dotColor() const override { return "grey"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
class OrderVarPreVertex final : public OrderVarVertex {
|
|
VL_RTTI_IMPL(OrderVarPreVertex, OrderVarVertex)
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderVarPreVertex(OrderGraph* graphp, AstVarScope* vscp) VL_MT_DISABLED
|
|
: OrderVarVertex{graphp, vscp} {}
|
|
~OrderVarPreVertex() override = default;
|
|
|
|
// METHODS
|
|
bool domainMatters() override { return false; }
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string nameSuffix() const override VL_MT_SAFE { return "PRE"; }
|
|
string dotColor() const override { return "green"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
class OrderVarPostVertex final : public OrderVarVertex {
|
|
VL_RTTI_IMPL(OrderVarPostVertex, OrderVarVertex)
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderVarPostVertex(OrderGraph* graphp, AstVarScope* vscp) VL_MT_DISABLED
|
|
: OrderVarVertex{graphp, vscp} {}
|
|
~OrderVarPostVertex() override = default;
|
|
|
|
// METHODS
|
|
bool domainMatters() override { return false; }
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string nameSuffix() const override VL_MT_SAFE { return "POST"; }
|
|
string dotColor() const override { return "red"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
class OrderVarPordVertex final : public OrderVarVertex {
|
|
VL_RTTI_IMPL(OrderVarPordVertex, OrderVarVertex)
|
|
public:
|
|
// CONSTRUCTOR
|
|
OrderVarPordVertex(OrderGraph* graphp, AstVarScope* vscp) VL_MT_DISABLED
|
|
: OrderVarVertex{graphp, vscp} {}
|
|
~OrderVarPordVertex() override = default;
|
|
|
|
// METHODS
|
|
bool domainMatters() override { return false; }
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string nameSuffix() const override VL_MT_SAFE { return "PORD"; }
|
|
string dotColor() const override { return "blue"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
//======================================================================
|
|
// Edge type
|
|
|
|
class OrderEdge final : public V3GraphEdge {
|
|
VL_RTTI_IMPL(OrderEdge, V3GraphEdge)
|
|
friend class OrderGraph; // Only the OrderGraph can create these
|
|
// CONSTRUCTOR
|
|
OrderEdge(OrderGraph* graphp, OrderEitherVertex* fromp, OrderEitherVertex* top, int weight,
|
|
bool cutable) VL_MT_DISABLED : V3GraphEdge{graphp, fromp, top, weight, cutable} {}
|
|
~OrderEdge() override = default;
|
|
|
|
// LCOV_EXCL_START // Debug code
|
|
string dotColor() const override { return cutable() ? "green" : "red"; }
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
|
|
//======================================================================
|
|
// Inline methods
|
|
|
|
void OrderGraph::addHardEdge(OrderLogicVertex* fromp, OrderVarVertex* top,
|
|
int weight) VL_MT_DISABLED {
|
|
new OrderEdge{this, fromp, top, weight, /* cutable: */ false};
|
|
}
|
|
void OrderGraph::addHardEdge(OrderVarVertex* fromp, OrderLogicVertex* top,
|
|
int weight) VL_MT_DISABLED {
|
|
new OrderEdge{this, fromp, top, weight, /* cutable: */ false};
|
|
}
|
|
void OrderGraph::addSoftEdge(OrderLogicVertex* fromp, OrderVarVertex* top,
|
|
int weight) VL_MT_DISABLED {
|
|
new OrderEdge{this, fromp, top, weight, /* cutable: */ true};
|
|
}
|
|
void OrderGraph::addSoftEdge(OrderVarVertex* fromp, OrderLogicVertex* top,
|
|
int weight) VL_MT_DISABLED {
|
|
new OrderEdge{this, fromp, top, weight, /* cutable: */ true};
|
|
}
|
|
|
|
#endif // Guard
|