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: Gate optimizations, such as wire elimination
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 16:18:39 +01:00
|
|
|
// Copyright 2003-2023 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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Gate's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Extract a graph of the *entire* netlist with cells expanded
|
|
|
|
|
// Perform constant optimization across the graph
|
|
|
|
|
// Create VARSCOPEs for any variables we can rip out
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
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
|
|
|
|
|
|
|
|
#include "V3Gate.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Ast.h"
|
2022-08-01 13:22:56 +02:00
|
|
|
#include "V3AstUserAllocator.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Const.h"
|
2021-05-21 02:41:46 +02:00
|
|
|
#include "V3DupFinder.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Stats.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <list>
|
2022-08-01 13:22:56 +02:00
|
|
|
#include <unordered_map>
|
2020-08-15 16:03:34 +02:00
|
|
|
#include <unordered_set>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2022-07-31 19:36:56 +02:00
|
|
|
class GateDedupeVarVisitor;
|
|
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using GateVarRefList = std::list<AstNodeVarRef*>;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-08-16 20:19:12 +02:00
|
|
|
constexpr int GATE_DEDUP_MAX_DEPTH = 20;
|
2016-02-05 02:47:55 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2013-02-21 02:14:15 +01:00
|
|
|
class GateLogicVertex;
|
|
|
|
|
class GateVarVertex;
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateGraphBaseVisitor VL_NOT_FINAL {
|
2013-02-21 02:14:15 +01:00
|
|
|
public:
|
2019-12-07 22:41:34 +01:00
|
|
|
V3Graph* m_graphp; // Graph this class is visiting
|
2020-04-16 03:47:37 +02:00
|
|
|
explicit GateGraphBaseVisitor(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_graphp{graphp} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~GateGraphBaseVisitor() = default;
|
2022-09-16 01:58:01 +02:00
|
|
|
virtual VNUser visit(GateLogicVertex* vertexp, VNUser vu = VNUser{0}) = 0;
|
|
|
|
|
virtual VNUser visit(GateVarVertex* vertexp, VNUser vu = VNUser{0}) = 0;
|
2013-02-21 02:14:15 +01:00
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Support classes
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstScope* const m_scopep; // Scope vertex refers to
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_reducible = true; // True if this node should be able to be eliminated
|
|
|
|
|
bool m_dedupable = true; // True if this node should be able to be deduped
|
|
|
|
|
bool m_consumed = false; // Output goes to something meaningful
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
GateEitherVertex(V3Graph* graphp, AstScope* scopep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_scopep{scopep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateEitherVertex() override = default;
|
2013-02-16 14:07:18 +01:00
|
|
|
// ACCESSORS
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotStyle() const override { return m_consumed ? "" : "dotted"; }
|
2006-08-26 13:35:28 +02:00
|
|
|
AstScope* scopep() const { return m_scopep; }
|
|
|
|
|
bool reducible() const { return m_reducible; }
|
2013-02-21 02:14:15 +01:00
|
|
|
bool dedupable() const { return m_dedupable; }
|
2022-07-30 16:01:25 +02:00
|
|
|
void setConsumed(const char* /*consumedReason*/) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_consumed = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(0, "\t\tSetConsumed "<<consumedReason<<" "<<this<<endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
bool consumed() const { return m_consumed; }
|
2022-07-30 16:01:25 +02:00
|
|
|
void clearReducible(const char* /*nonReducibleReason*/) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_reducible = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(0, " NR: "<<nonReducibleReason<<" "<<name()<<endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-07-30 16:01:25 +02:00
|
|
|
void clearDedupable(const char* /*nonDedupableReason*/) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_dedupable = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(0, " ND: "<<nonDedupableReason<<" "<<name()<<endl);
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
void clearReducibleAndDedupable(const char* nonReducibleReason) {
|
2019-05-19 22:13:13 +02:00
|
|
|
clearReducible(nonReducibleReason);
|
|
|
|
|
clearDedupable(nonReducibleReason);
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2022-09-16 01:58:01 +02:00
|
|
|
virtual VNUser accept(GateGraphBaseVisitor& v, VNUser vu = VNUser{0}) = 0;
|
2013-02-21 02:14:15 +01:00
|
|
|
// Returns only the result from the LAST vertex iterated over
|
2022-09-16 01:58:01 +02:00
|
|
|
VNUser iterateInEdges(GateGraphBaseVisitor& v, VNUser vu = VNUser{0}) {
|
|
|
|
|
VNUser ret{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3GraphEdge* edgep = inBeginp(); edgep; edgep = edgep->inNextp()) {
|
2022-09-02 12:29:02 +02:00
|
|
|
ret = static_cast<GateEitherVertex*>(edgep->fromp())->accept(v, vu);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return ret;
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2017-05-09 14:05:21 +02:00
|
|
|
// Returns only the result from the LAST vertex iterated over
|
|
|
|
|
// Note: This behaves differently than iterateInEdges() in that it will traverse
|
2019-05-19 22:13:13 +02:00
|
|
|
// all edges that exist when it is initially called, whereas
|
|
|
|
|
// iterateInEdges() will stop traversing edges if one is deleted
|
2022-09-16 01:58:01 +02:00
|
|
|
VNUser iterateCurrentOutEdges(GateGraphBaseVisitor& v, VNUser vu = VNUser{0}) {
|
|
|
|
|
VNUser ret{0};
|
2020-08-15 16:12:55 +02:00
|
|
|
V3GraphEdge* next_edgep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3GraphEdge* edgep = outBeginp(); edgep; edgep = next_edgep) {
|
|
|
|
|
// Need to find the next edge before visiting in case the edge is deleted
|
|
|
|
|
next_edgep = edgep->outNextp();
|
2022-09-02 12:29:02 +02:00
|
|
|
ret = static_cast<GateEitherVertex*>(edgep->top())->accept(v, vu);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return ret;
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateVarVertex final : public GateEitherVertex {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const m_varScp;
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_isTop = false;
|
|
|
|
|
bool m_isClock = false;
|
|
|
|
|
AstNode* m_rstSyncNodep = nullptr; // Used as reset and not in SenItem, in clocked always
|
|
|
|
|
AstNode* m_rstAsyncNodep = nullptr; // Used as reset and in SenItem, in clocked always
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
GateVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: GateEitherVertex{graphp, scopep}
|
|
|
|
|
, m_varScp{varScp} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateVarVertex() override = default;
|
2013-02-16 14:07:18 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstVarScope* varScp() const { return m_varScp; }
|
2023-03-18 01:24:15 +01:00
|
|
|
string name() const override VL_MT_STABLE {
|
|
|
|
|
return (cvtToHex(m_varScp) + " " + varScp()->name());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return "blue"; }
|
2006-08-26 13:35:28 +02:00
|
|
|
bool isTop() const { return m_isTop; }
|
2010-12-31 13:51:14 +01:00
|
|
|
void setIsTop() { m_isTop = true; }
|
2006-08-26 13:35:28 +02:00
|
|
|
bool isClock() const { return m_isClock; }
|
2020-04-15 13:58:34 +02:00
|
|
|
void setIsClock() {
|
|
|
|
|
m_isClock = true;
|
|
|
|
|
setConsumed("isclk");
|
|
|
|
|
}
|
2010-12-31 13:51:14 +01:00
|
|
|
AstNode* rstSyncNodep() const { return m_rstSyncNodep; }
|
2019-05-19 22:13:13 +02:00
|
|
|
void rstSyncNodep(AstNode* nodep) { m_rstSyncNodep = nodep; }
|
2010-12-31 13:51:14 +01:00
|
|
|
AstNode* rstAsyncNodep() const { return m_rstAsyncNodep; }
|
2019-05-19 22:13:13 +02:00
|
|
|
void rstAsyncNodep(AstNode* nodep) { m_rstAsyncNodep = nodep; }
|
2013-02-16 14:07:18 +01:00
|
|
|
// METHODS
|
|
|
|
|
void propagateAttrClocksFrom(GateVarVertex* fromp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Propagate clock and general attribute onto this node
|
|
|
|
|
varScp()->varp()->propagateAttrFrom(fromp->varScp()->varp());
|
|
|
|
|
if (fromp->isClock()) {
|
|
|
|
|
varScp()->varp()->usedClock(true);
|
|
|
|
|
setIsClock();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser accept(GateGraphBaseVisitor& v, VNUser vu = VNUser{0}) override {
|
2020-08-15 17:44:10 +02:00
|
|
|
return v.visit(this, vu);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateLogicVertex final : public GateEitherVertex {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const m_nodep;
|
|
|
|
|
AstActive* const m_activep; // Under what active; nullptr is ok (under cfunc or such)
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool m_slow; // In slow block
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2020-04-15 13:58:34 +02:00
|
|
|
GateLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstActive* activep,
|
|
|
|
|
bool slow)
|
2020-08-16 15:55:36 +02:00
|
|
|
: GateEitherVertex{graphp, scopep}
|
|
|
|
|
, m_nodep{nodep}
|
|
|
|
|
, m_activep{activep}
|
|
|
|
|
, m_slow{slow} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateLogicVertex() override = default;
|
2013-02-16 14:07:18 +01:00
|
|
|
// ACCESSORS
|
2023-03-18 01:24:15 +01:00
|
|
|
string name() const override VL_MT_STABLE {
|
|
|
|
|
return (cvtToHex(m_nodep) + "@" + scopep()->prettyName());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return "purple"; }
|
|
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
AstActive* activep() const { return m_activep; }
|
2020-04-15 13:58:34 +02:00
|
|
|
bool slow() const { return m_slow; }
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser accept(GateGraphBaseVisitor& v, VNUser vu = VNUser{0}) override {
|
2020-08-15 17:44:10 +02:00
|
|
|
return v.visit(this, vu);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Is this a simple expression with a single input and single output?
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class GateOkVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// RETURN STATE
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_isSimple = true; // Set false when we know it isn't simple
|
2020-04-15 13:58:34 +02:00
|
|
|
GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNode* m_substTreep = nullptr; // What to replace the variable with
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks)
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeVarRef* m_lhsVarRef
|
|
|
|
|
= nullptr; // VarRef on lhs of assignment (what we're replacing)
|
2020-04-15 13:58:34 +02:00
|
|
|
bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable
|
2020-08-15 19:11:27 +02:00
|
|
|
int m_ops = 0; // Operation count
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void clearSimple(const char* because) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_isSimple) {
|
|
|
|
|
m_isSimple = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Clear simple " << because << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2018-01-27 21:06:51 +01:00
|
|
|
++m_ops;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// We only allow a LHS ref for the var being set, and a RHS ref for
|
|
|
|
|
// something else being read.
|
|
|
|
|
if (nodep->varScopep()->varp()->isSc()) {
|
|
|
|
|
clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's
|
|
|
|
|
}
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isRW()) {
|
|
|
|
|
clearSimple("R/W");
|
|
|
|
|
} else if (nodep->access().isWriteOrRW()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_lhsVarRef) clearSimple(">1 lhs varRefs");
|
|
|
|
|
m_lhsVarRef = nodep;
|
|
|
|
|
} else {
|
2020-04-04 04:31:54 +02:00
|
|
|
if (m_rhsVarRefs.size() > 1) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstNodeVarRef* const lastRefp = m_rhsVarRefs.back();
|
2020-04-04 04:31:54 +02:00
|
|
|
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
|
|
|
|
|
if (!nodep->varScopep()->varp()->gateMultiInputOptimizable()
|
|
|
|
|
// We didn't check multiInput on the first varref, so check it here
|
|
|
|
|
|| !lastRefp->varScopep()->varp()->gateMultiInputOptimizable()) {
|
|
|
|
|
clearSimple("!gateMultiInputOptimizable");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_rhsVarRefs.push_back(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_substTreep = nodep->rhsp();
|
2018-10-15 00:39:33 +02:00
|
|
|
if (!VN_IS(nodep->lhsp(), NodeVarRef)) {
|
|
|
|
|
clearSimple("ASSIGN(non-VARREF)");
|
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
|
|
|
} else if (nodep->isTimingControl()) {
|
|
|
|
|
clearSimple("Timing control");
|
2018-10-15 00:39:33 +02:00
|
|
|
} else {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// We don't push logic other then assignments/NOTs into SenItems
|
|
|
|
|
// This avoids a mess in computing what exactly a POSEDGE is
|
|
|
|
|
// V3Const cleans up any NOTs by flipping the edges for us
|
|
|
|
|
if (m_buffersOnly
|
2021-10-22 14:56:48 +02:00
|
|
|
&& !(
|
|
|
|
|
VN_IS(nodep->rhsp(), VarRef)
|
|
|
|
|
// Avoid making non-clocked logic into clocked,
|
|
|
|
|
// as it slows down the verilator_sim_benchmark
|
|
|
|
|
|| (VN_IS(nodep->rhsp(), Not) && VN_IS(VN_AS(nodep->rhsp(), Not)->lhsp(), VarRef)
|
|
|
|
|
&& VN_AS(VN_AS(nodep->rhsp(), Not)->lhsp(), VarRef)->varp()->isUsedClock()))) {
|
2019-05-19 22:13:13 +02:00
|
|
|
clearSimple("Not a buffer (goes to a clock)");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// *** Special iterator
|
|
|
|
|
if (!m_isSimple) return; // Fastpath
|
2021-02-22 03:25:21 +01:00
|
|
|
if (++m_ops > v3Global.opt.gateStmts()) clearSimple("--gate-stmts exceeded");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!(m_dedupe ? nodep->isGateDedupable() : nodep->isGateOptimizable()) //
|
|
|
|
|
|| !nodep->isPure() || nodep->isBrancher()) {
|
|
|
|
|
UINFO(5, "Non optimizable type: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
clearSimple("Non optimizable type");
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
|
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2013-02-21 02:14:15 +01:00
|
|
|
GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_buffersOnly = buffersOnly;
|
|
|
|
|
m_dedupe = dedupe;
|
|
|
|
|
// Iterate
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check results
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!m_substTreep) clearSimple("No assignment found\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin(); it != m_rhsVarRefs.end();
|
|
|
|
|
++it) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_lhsVarRef && m_lhsVarRef->varScopep() == (*it)->varScopep()) {
|
2022-12-10 03:06:27 +01:00
|
|
|
clearSimple("Circular logic\n"); // Oh my, we'll get an UNOPTFLAT much later
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9 && !m_isSimple) nodep->dumpTree("- gate!Ok: ");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateOkVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
// PUBLIC METHODS
|
|
|
|
|
bool isSimple() const { return m_isSimple; }
|
|
|
|
|
AstNode* substTree() const { return m_substTreep; }
|
2020-04-15 13:58:34 +02:00
|
|
|
const GateVarRefList& rhsVarRefs() const { return m_rhsVarRefs; }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Replace refs to 'varscp' with 'substp' in 'consumerp'
|
2022-07-31 19:36:56 +02:00
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
static void eliminate(AstNode* logicp,
|
|
|
|
|
const std::unordered_map<AstVarScope*, AstNode*>& substitutions,
|
2022-07-31 19:36:56 +02:00
|
|
|
GateDedupeVarVisitor* varVisp);
|
|
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Gate class functions
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class GateVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
// Entire netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user1p -> GateVarVertex* for usage var, 0=not set yet
|
2022-08-01 13:22:56 +02:00
|
|
|
// {logic}Node::user1 -> map of substitutions, via m_substitutions
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user2 -> bool: Signal used in SenItem in *this* always statement
|
|
|
|
|
// AstVar::user2 -> bool: Warned about SYNCASYNCNET
|
2018-06-22 12:35:27 +02:00
|
|
|
// AstNodeVarRef::user2 -> bool: ConcatOffset visited
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
2007-01-31 22:49:13 +01:00
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// Variable substitutions to apply to a given logic block
|
|
|
|
|
AstUser1Allocator<AstNode, std::unordered_map<AstVarScope*, AstNode*>> m_substitutions;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
2020-08-16 15:55:36 +02:00
|
|
|
GateLogicVertex* m_logicVertexp = nullptr; // Current statement being tracked, nullptr=ignored
|
|
|
|
|
AstScope* m_scopep = nullptr; // Current scope being processed
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeModule* m_modp = nullptr; // Current module
|
2020-08-16 15:55:36 +02:00
|
|
|
AstActive* m_activep = nullptr; // Current active
|
|
|
|
|
bool m_activeReducible = true; // Is activation block reducible?
|
|
|
|
|
bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks
|
|
|
|
|
bool m_inSlow = false; // Inside a slow structure
|
2022-07-31 20:28:54 +02:00
|
|
|
std::vector<AstNode*> m_optimized; // Logic blocks optimized
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
VDouble0 m_statSigs; // Statistic tracking
|
|
|
|
|
VDouble0 m_statRefs; // Statistic tracking
|
|
|
|
|
VDouble0 m_statDedupLogic; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAssignMerged; // Statistic tracking
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
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
|
|
|
void checkTimingControl(AstNode* nodep) {
|
|
|
|
|
if (nodep->isTimingControl() && m_logicVertexp) {
|
|
|
|
|
m_logicVertexp->clearReducibleAndDedupable("TimingControl");
|
|
|
|
|
m_logicVertexp->setConsumed("TimingControl");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason,
|
|
|
|
|
const char* consumeReason) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_scopep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " STMT " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// m_activep is null under AstCFunc's, that's ok.
|
2022-11-20 23:40:38 +01:00
|
|
|
m_logicVertexp = new GateLogicVertex{&m_graph, m_scopep, nodep, m_activep, m_inSlow};
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nonReducibleReason) {
|
|
|
|
|
m_logicVertexp->clearReducibleAndDedupable(nonReducibleReason);
|
|
|
|
|
} else if (!m_activeReducible) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// Sequential logic is dedupable
|
|
|
|
|
m_logicVertexp->clearReducible("Block Unreducible");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep, SenItem)) m_logicVertexp->setConsumed("senItem");
|
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
|
|
|
checkTimingControl(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_logicVertexp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GateVarVertex* makeVarVertex(AstVarScope* varscp) {
|
2018-10-15 00:39:33 +02:00
|
|
|
GateVarVertex* vertexp = reinterpret_cast<GateVarVertex*>(varscp->user1p());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vertexp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "New vertex " << varscp << endl);
|
2022-11-20 23:40:38 +01:00
|
|
|
vertexp = new GateVarVertex{&m_graph, m_scopep, varscp};
|
2019-05-19 22:13:13 +02:00
|
|
|
varscp->user1p(vertexp);
|
|
|
|
|
if (varscp->varp()->isSigPublic()) {
|
|
|
|
|
// Public signals shouldn't be changed, pli code might be messing with them
|
|
|
|
|
vertexp->clearReducibleAndDedupable("SigPublic");
|
|
|
|
|
vertexp->setConsumed("SigPublic");
|
|
|
|
|
}
|
|
|
|
|
if (varscp->varp()->isIO() && varscp->scopep()->isTop()) {
|
|
|
|
|
// We may need to convert to/from sysc/reg sigs
|
|
|
|
|
vertexp->setIsTop();
|
|
|
|
|
vertexp->clearReducibleAndDedupable("isTop");
|
|
|
|
|
vertexp->setConsumed("isTop");
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return vertexp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-31 19:36:56 +02:00
|
|
|
void optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 5) consumerp->dumpTree("- elimUsePre: ");
|
2022-08-01 13:22:56 +02:00
|
|
|
if (!m_substitutions.tryGet(consumerp)) m_optimized.push_back(consumerp);
|
|
|
|
|
m_substitutions(consumerp).emplace(varscp, substp->cloneTree(false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void commitElimVar(AstNode* logicp) {
|
|
|
|
|
if (auto* const substitutionsp = m_substitutions.tryGet(logicp)) {
|
|
|
|
|
if (!substitutionsp->empty()) {
|
|
|
|
|
eliminate(logicp, *substitutionsp, nullptr);
|
|
|
|
|
AstNode* const foldedp = V3Const::constifyEdit(logicp);
|
|
|
|
|
UASSERT_OBJ(foldedp == logicp, foldedp, "Should not remove whole logic");
|
|
|
|
|
for (const auto& pair : *substitutionsp) pair.second->deleteTree();
|
|
|
|
|
substitutionsp->clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-31 19:36:56 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void optimizeSignals(bool allowMultiIn);
|
2015-02-12 01:36:34 +01:00
|
|
|
bool elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor);
|
2010-12-31 13:51:14 +01:00
|
|
|
void warnSignals();
|
2006-08-26 13:35:28 +02:00
|
|
|
void consumedMark();
|
|
|
|
|
void consumedMarkRecurse(GateEitherVertex* vertexp);
|
|
|
|
|
void consumedMove();
|
2013-02-21 02:14:15 +01:00
|
|
|
void dedupe();
|
2014-11-06 03:09:35 +01:00
|
|
|
void mergeAssigns();
|
2017-05-09 14:05:21 +02:00
|
|
|
void decomposeClkVectors();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_pre");
|
2019-05-19 22:13:13 +02:00
|
|
|
warnSignals(); // Before loss of sync/async pointers
|
|
|
|
|
// Decompose clock vectors -- need to do this before removing redundant edges
|
|
|
|
|
decomposeClkVectors();
|
|
|
|
|
m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue);
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_simp");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Find gate interconnect and optimize
|
2020-04-15 13:58:34 +02:00
|
|
|
m_graph.userClearVertices(); // vertex->user(): bool. Indicates we've set it as consumed
|
2019-05-19 22:13:13 +02:00
|
|
|
// Get rid of buffers first,
|
|
|
|
|
optimizeSignals(false);
|
|
|
|
|
// Then propagate more complicated equations
|
|
|
|
|
optimizeSignals(true);
|
2022-08-01 13:22:56 +02:00
|
|
|
// Commit substitutions on the optimized logic
|
|
|
|
|
for (AstNode* const logicp : m_optimized) commitElimVar(logicp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Remove redundant logic
|
2022-06-04 02:43:16 +02:00
|
|
|
if (v3Global.opt.fDedupe()) {
|
2019-12-07 22:41:34 +01:00
|
|
|
dedupe();
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup");
|
2019-12-07 22:41:34 +01:00
|
|
|
}
|
2022-06-04 02:43:16 +02:00
|
|
|
if (v3Global.opt.fAssemble()) {
|
2019-12-07 22:41:34 +01:00
|
|
|
mergeAssigns();
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_assm");
|
2019-12-07 22:41:34 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// Consumption warnings
|
|
|
|
|
consumedMark();
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_opt");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Rewrite assignments
|
|
|
|
|
consumedMove();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_activeReducible = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " SCOPE " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_scopep = nodep;
|
2020-08-15 16:12:55 +02:00
|
|
|
m_logicVertexp = nullptr;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_scopep = nullptr;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstActive* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Create required blocks and add to module
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " BLOCK " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
|
|
|
|
|
m_activep = nodep;
|
|
|
|
|
AstNode::user2ClearTree();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode::user2ClearTree();
|
2020-08-15 16:12:55 +02:00
|
|
|
m_activep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activeReducible = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_scopep) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const varscp = nodep->varScopep();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
2021-11-13 19:50:44 +01:00
|
|
|
GateVarVertex* const vvertexp = makeVarVertex(varscp);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " VARREF to " << varscp << endl);
|
2020-02-04 05:21:56 +01:00
|
|
|
if (m_inSenItem) {
|
|
|
|
|
vvertexp->setIsClock();
|
|
|
|
|
// For SYNCASYNCNET
|
|
|
|
|
varscp->user2(true);
|
2020-11-01 22:59:23 +01:00
|
|
|
} else if (m_activep && m_activep->hasClocked() && nodep->access().isReadOnly()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varscp->user2()) {
|
|
|
|
|
if (!vvertexp->rstAsyncNodep()) vvertexp->rstAsyncNodep(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
if (!vvertexp->rstSyncNodep()) vvertexp->rstSyncNodep(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// We use weight of one; if we ref the var more than once, when we simplify,
|
|
|
|
|
// the weight will increase
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW()) {
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, m_logicVertexp, vvertexp, 1};
|
2020-11-07 16:37:55 +01:00
|
|
|
}
|
|
|
|
|
if (nodep->access().isReadOrRW()) {
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, vvertexp, m_logicVertexp, 1};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPublic* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_inSlow);
|
|
|
|
|
{
|
|
|
|
|
m_inSlow = true;
|
|
|
|
|
iterateNewStmt(nodep, "AlwaysPublic", nullptr);
|
|
|
|
|
}
|
2010-04-06 02:01:17 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
iterateNewStmt(nodep, "User C Function", "User C Function");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_inSenItem = true;
|
|
|
|
|
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else { // Standalone item, probably right under a SenTree
|
2020-08-15 16:12:55 +02:00
|
|
|
iterateNewStmt(nodep, nullptr, nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_inSenItem = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_inSlow);
|
|
|
|
|
{
|
2020-11-29 15:50:30 +01:00
|
|
|
m_inSlow = VN_IS(nodep, Initial) || VN_IS(nodep, Final);
|
2020-08-25 03:10:43 +02:00
|
|
|
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt() ? nullptr : "Multiple Stmts"),
|
|
|
|
|
nullptr);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignAlias* nodep) override { //
|
2020-08-15 16:12:55 +02:00
|
|
|
iterateNewStmt(nodep, nullptr, nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignW* nodep) override { //
|
2020-08-15 16:12:55 +02:00
|
|
|
iterateNewStmt(nodep, nullptr, nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverToggle* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
iterateNewStmt(nodep, "CoverToggle", "CoverToggle");
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTraceDecl* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_inSlow);
|
|
|
|
|
{
|
|
|
|
|
m_inSlow = true;
|
|
|
|
|
iterateNewStmt(nodep, "Tracing", "Tracing");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConcat* nodep) override {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!(VN_IS(nodep->backp(), NodeAssign)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_AS(nodep->backp(), NodeAssign)->lhsp() == nodep),
|
2019-07-06 18:57:50 +02:00
|
|
|
nodep, "Concat on LHS of assignment; V3Const should have deleted it");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter");
|
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
|
|
|
checkTimingControl(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-10-08 01:39:59 +02:00
|
|
|
explicit GateVisitor(AstNode* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateVisitor() override {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
|
|
|
|
|
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
|
|
|
|
|
V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic);
|
|
|
|
|
V3Stats::addStat("Optimizations, Gate assign merged", m_statAssignMerged);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2022-07-31 21:04:39 +02:00
|
|
|
GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp);
|
|
|
|
|
|
|
|
|
|
// Consider "inlining" variables
|
|
|
|
|
if (!vvertexp) continue;
|
|
|
|
|
|
|
|
|
|
if (vvertexp->inEmpty()) { // Can't deal with no sources
|
|
|
|
|
vvertexp->clearReducibleAndDedupable("inEmpty");
|
|
|
|
|
} else if (!vvertexp->inSize1()) { // Can't deal with more than one src
|
|
|
|
|
vvertexp->clearReducibleAndDedupable("size!1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reduce it?
|
|
|
|
|
if (!vvertexp->reducible()) continue;
|
|
|
|
|
|
|
|
|
|
// Grab the driving logic
|
|
|
|
|
GateLogicVertex* const logicVertexp
|
|
|
|
|
= static_cast<GateLogicVertex*>(vvertexp->inBeginp()->fromp());
|
|
|
|
|
if (!logicVertexp->reducible()) continue;
|
|
|
|
|
AstNode* const logicp = logicVertexp->nodep();
|
2022-08-01 13:22:56 +02:00
|
|
|
|
2022-12-23 17:32:38 +01:00
|
|
|
// Commit pending optimizations to driving logic, as we will re-analyze
|
2022-08-01 13:22:56 +02:00
|
|
|
commitElimVar(logicp);
|
2022-07-31 20:28:54 +02:00
|
|
|
|
|
|
|
|
// Can we eliminate?
|
2022-07-31 21:04:39 +02:00
|
|
|
const GateOkVisitor okVisitor{logicp, vvertexp->isClock(), false};
|
|
|
|
|
|
|
|
|
|
// Was it ok?
|
|
|
|
|
if (!okVisitor.isSimple()) continue;
|
|
|
|
|
|
|
|
|
|
// Does it read multiple source variables?
|
|
|
|
|
if (okVisitor.rhsVarRefs().size() > 1) {
|
|
|
|
|
if (!allowMultiIn) continue;
|
|
|
|
|
// Do it if not used, or used only once, ignoring traces
|
|
|
|
|
int n = 0;
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
const GateLogicVertex* const consumeVertexp
|
|
|
|
|
= static_cast<GateLogicVertex*>(edgep->top());
|
|
|
|
|
// Ignore tracing or other slow path junk, or if the destination is not used
|
|
|
|
|
if (!consumeVertexp->slow() && consumeVertexp->outBeginp()) n += edgep->weight();
|
|
|
|
|
if (n > 1) break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-07-31 21:04:39 +02:00
|
|
|
|
|
|
|
|
if (n > 1) continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process it
|
|
|
|
|
AstNode* const substp = okVisitor.substTree();
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 5) logicp->dumpTree("- elimVar: ");
|
|
|
|
|
if (debug() >= 5) substp->dumpTree("- subst: ");
|
2022-07-31 21:04:39 +02:00
|
|
|
++m_statSigs;
|
|
|
|
|
bool removedAllUsages = true;
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep;) {
|
|
|
|
|
GateLogicVertex* const consumeVertexp = static_cast<GateLogicVertex*>(edgep->top());
|
|
|
|
|
AstNode* const consumerp = consumeVertexp->nodep();
|
|
|
|
|
if (!elimLogicOkOutputs(consumeVertexp, okVisitor /*ref*/)) {
|
|
|
|
|
// Cannot optimize this replacement
|
|
|
|
|
removedAllUsages = false;
|
|
|
|
|
edgep = edgep->outNextp();
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-07-31 21:04:39 +02:00
|
|
|
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
|
|
|
|
|
// If the new replacement referred to a signal,
|
|
|
|
|
// Correct the graph to point to this new generating variable
|
|
|
|
|
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
|
|
|
|
for (AstNodeVarRef* const refp : rhsVarRefs) {
|
|
|
|
|
AstVarScope* const newvarscp = refp->varScopep();
|
|
|
|
|
GateVarVertex* const varvertexp = makeVarVertex(newvarscp);
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, varvertexp, consumeVertexp, 1};
|
2022-07-31 21:04:39 +02:00
|
|
|
// Propagate clock attribute onto generating node
|
|
|
|
|
varvertexp->propagateAttrClocksFrom(vvertexp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-07-31 21:04:39 +02:00
|
|
|
// Remove the edge
|
|
|
|
|
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
|
|
|
|
++m_statRefs;
|
|
|
|
|
edgep = vvertexp->outBeginp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (removedAllUsages) {
|
|
|
|
|
// Remove input links
|
|
|
|
|
while (V3GraphEdge* const edgep = vvertexp->inBeginp()) {
|
|
|
|
|
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-09-02 17:38:06 +02:00
|
|
|
// Mark the vertex so we don't mark it as being unconsumed in the next step
|
2022-07-31 21:04:39 +02:00
|
|
|
vvertexp->user(true);
|
|
|
|
|
logicVertexp->user(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp,
|
|
|
|
|
const GateOkVisitor& okVisitor) {
|
2015-02-12 01:36:34 +01:00
|
|
|
// Return true if can optimize
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return false if the consuming logic has an output signal that the
|
|
|
|
|
// replacement logic has as an input
|
2021-03-12 23:26:53 +01:00
|
|
|
|
2015-02-12 01:36:34 +01:00
|
|
|
// Use map to find duplicates between two lists
|
2021-03-12 23:26:53 +01:00
|
|
|
std::unordered_set<AstVarScope*> varscopes;
|
2015-02-12 01:36:34 +01:00
|
|
|
// Replacement logic usually has shorter input list, so faster to build list based on it
|
|
|
|
|
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
2020-04-15 13:58:34 +02:00
|
|
|
for (GateVarRefList::const_iterator it = rhsVarRefs.begin(); it != rhsVarRefs.end(); ++it) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const vscp = (*it)->varScopep();
|
2020-02-04 05:21:56 +01:00
|
|
|
varscopes.insert(vscp);
|
2015-02-12 01:36:34 +01:00
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* edgep = consumeVertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
2022-09-02 12:29:02 +02:00
|
|
|
const GateVarVertex* const consVVertexp = static_cast<GateVarVertex*>(edgep->top());
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const vscp = consVVertexp->varScp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varscopes.find(vscp) != varscopes.end()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " Block-unopt, insertion generates input vscp " << vscp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2015-02-12 01:36:34 +01:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMark() {
|
|
|
|
|
// Propagate consumed signals backwards to all producers into a consumed node
|
|
|
|
|
m_graph.userClearVertices();
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
|
|
|
|
vertexp = vertexp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
GateEitherVertex* const evertexp = static_cast<GateEitherVertex*>(vertexp);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!evertexp->user() && evertexp->consumed()) consumedMarkRecurse(evertexp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMarkRecurse(GateEitherVertex* vertexp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vertexp->user()) return; // Already marked
|
2006-08-26 13:35:28 +02:00
|
|
|
vertexp->user(true);
|
|
|
|
|
if (!vertexp->consumed()) vertexp->setConsumed("propagated");
|
|
|
|
|
// Walk sources and mark them too
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
GateEitherVertex* const eFromVertexp = static_cast<GateEitherVertex*>(edgep->fromp());
|
2019-05-19 22:13:13 +02:00
|
|
|
consumedMarkRecurse(eFromVertexp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMove() {
|
|
|
|
|
// Remove unused logic (logic that doesn't hit a combo block or a display statement)
|
|
|
|
|
// We need the "usually" block logic to do a better job at this
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
|
|
|
|
vertexp = vertexp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vvertexp->consumed() && !vvertexp->user()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, "Unconsumed " << vvertexp->varScp() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-09-02 12:29:02 +02:00
|
|
|
} else {
|
|
|
|
|
const GateLogicVertex* const lvertexp = static_cast<GateLogicVertex*>(vertexp);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nodep = lvertexp->nodep();
|
|
|
|
|
const AstActive* const oldactp = lvertexp->activep(); // nullptr under cfunc
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lvertexp->consumed() && oldactp) {
|
|
|
|
|
// Eventually: Move the statement to a new active block
|
|
|
|
|
// with "tracing-on" sensitivity
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " Remove unconsumed " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-31 13:51:14 +01:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::warnSignals() {
|
|
|
|
|
AstNode::user2ClearTree();
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
const AstVarScope* const vscp = vvertexp->varScp();
|
|
|
|
|
const AstNode* const sp = vvertexp->rstSyncNodep();
|
|
|
|
|
const AstNode* const ap = vvertexp->rstAsyncNodep();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (ap && sp && !vscp->varp()->user2()) {
|
|
|
|
|
// This is somewhat wrong, as marking one flop as ok for sync
|
|
|
|
|
// may mean a different flop now fails. However it's a pain to
|
|
|
|
|
// then report a warning in a new place - we should report them all at once.
|
|
|
|
|
// Instead we'll disable if any disabled
|
|
|
|
|
if (!vscp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)
|
|
|
|
|
&& !ap->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)
|
2020-04-15 13:58:34 +02:00
|
|
|
&& !sp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
vscp->varp()->user2(true); // Warn only once per signal
|
2020-04-15 13:58:34 +02:00
|
|
|
vscp->v3warn(SYNCASYNCNET,
|
|
|
|
|
"Signal flopped as both synchronous and async: "
|
2020-11-19 03:03:23 +01:00
|
|
|
<< vscp->prettyNameQ() << '\n'
|
|
|
|
|
<< ap->warnOther() << "... Location of async usage\n"
|
|
|
|
|
<< ap->warnContextPrimary() << '\n'
|
|
|
|
|
<< sp->warnOther() << "... Location of sync usage\n"
|
2020-04-15 13:58:34 +02:00
|
|
|
<< sp->warnContextSecondary());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-12-31 13:51:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-21 02:14:15 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Auxiliary hash class for GateDedupeVarVisitor
|
|
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
class GateDedupeHash final : public V3DupFinderUserSame {
|
2013-02-21 02:14:15 +01:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
|
2019-08-04 01:05:59 +02:00
|
|
|
// Ast*::user3p -> AstActive* of assign, for isSame() in test for duplicate
|
2020-08-15 16:12:55 +02:00
|
|
|
// Set to nullptr if this assign's tree was later replaced
|
2019-08-04 01:05:59 +02:00
|
|
|
// Ast*::user5p -> AstNode* of assign if condition, for isSame() in test for duplicate
|
2020-08-15 16:12:55 +02:00
|
|
|
// Set to nullptr if this assign's tree was later replaced
|
2022-01-02 19:56:40 +01:00
|
|
|
// VNUser1InUse m_inuser1; (Allocated for use in GateVisitor)
|
|
|
|
|
// VNUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
|
|
|
|
const VNUser3InUse m_inuser3;
|
|
|
|
|
// VNUser4InUse m_inuser4; (Allocated for use in V3Hasher via V3DupFinder)
|
|
|
|
|
const VNUser5InUse m_inuser5;
|
2019-08-04 03:49:39 +02:00
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
V3DupFinder m_dupFinder; // Duplicate finder for rhs of assigns
|
2021-03-12 23:26:53 +01:00
|
|
|
std::unordered_set<AstNode*> m_nodeDeleteds; // Any node in this hash was deleted
|
2019-08-04 03:49:39 +02:00
|
|
|
|
2016-11-27 15:40:12 +01:00
|
|
|
bool same(AstNode* node1p, AstNode* node2p) {
|
2022-12-03 00:46:38 +01:00
|
|
|
// Regarding the complexity of this function 'same':
|
2021-05-21 15:34:27 +02:00
|
|
|
// Applying this comparison function to a a set of n trees pairwise is O(n^2) in the
|
|
|
|
|
// number of comparisons (number of pairs). AstNode::sameTree itself, is O(sizeOfTree) in
|
|
|
|
|
// the worst case, which happens if the operands of sameTree are indeed identical copies,
|
|
|
|
|
// which means this line is O(n^2*sizeOfTree), iff you are comparing identical copies of
|
|
|
|
|
// the same tree. In practice the identity comparison over the pointers, and the short
|
|
|
|
|
// circuiting in sameTree means that for comparing the same tree instance to itself, or
|
|
|
|
|
// trees of different types/shapes is a lot closer to O(1), so this 'same' function is
|
|
|
|
|
// Omega(n^2) and O(n^2*sizeOfTree), and in practice as we are mostly comparing the same
|
|
|
|
|
// instance to itself or different trees, the complexity should be closer to the lower
|
|
|
|
|
// bound.
|
|
|
|
|
//
|
|
|
|
|
// Also if you see where this 'same' function is used within isSame, it's only ever
|
|
|
|
|
// comparing AstActive nodes, which are very likely not to compare equals (and for the
|
|
|
|
|
// purposes of V3Gate, we probably only care about them either being identical instances,
|
|
|
|
|
// or having the same sensitivities anyway, so if this becomes a problem, it can be
|
2022-10-12 11:19:21 +02:00
|
|
|
// improved which should also speed things up), and AstNodeExpr for if conditions, which
|
2021-05-21 15:34:27 +02:00
|
|
|
// are hopefully small, and to be safe they should probably be only considered same when
|
|
|
|
|
// identical instances (otherwise if writing the condition between 2 ifs don't really
|
|
|
|
|
// merge).
|
|
|
|
|
return node1p == node2p || (node1p && node1p->sameTree(node2p));
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2013-02-21 02:14:15 +01:00
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
GateDedupeHash() = default;
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateDedupeHash() override {
|
2019-08-04 03:49:39 +02:00
|
|
|
if (v3Global.opt.debugCheck()) check();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// About to replace a node; any node we're tracking refers to oldp, change it to newp.
|
|
|
|
|
// This might be a variable on the lhs of the duplicate tree,
|
|
|
|
|
// or could be a rhs variable in a tree we're not replacing (or not yet anyways)
|
|
|
|
|
void hashReplace(AstNode* oldp, AstNode* newp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "replacing " << (void*)oldp << " with " << (void*)newp << endl);
|
2019-08-04 03:49:39 +02:00
|
|
|
// We could update the user3p and user5p but the resulting node
|
|
|
|
|
// still has incorrect hash. We really need to remove all hash on
|
|
|
|
|
// the whole hash entry tree involving the replaced node and
|
|
|
|
|
// rehash. That's complicated and this is rare, so just remove it
|
|
|
|
|
// from consideration.
|
|
|
|
|
m_nodeDeleteds.insert(oldp);
|
|
|
|
|
}
|
|
|
|
|
bool isReplaced(AstNode* nodep) {
|
|
|
|
|
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
|
|
|
|
|
UASSERT_OBJ(!VN_IS(nodep, NodeAssign), nodep, "Dedup attempt on non-assign");
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const extra1p = nodep->user3p();
|
|
|
|
|
AstNode* const extra2p = nodep->user5p();
|
2019-08-04 03:49:39 +02:00
|
|
|
return ((extra1p && m_nodeDeleteds.find(extra1p) != m_nodeDeleteds.end())
|
|
|
|
|
|| (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end()));
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
// Callback from V3DupFinder::findDuplicate
|
2022-09-16 12:22:11 +02:00
|
|
|
bool isSame(AstNode* node1p, AstNode* node2p) override {
|
2019-08-04 03:49:39 +02:00
|
|
|
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
|
|
|
|
|
if (isReplaced(node1p) || isReplaced(node2p)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, "isSame hit on replaced "<<(void*)node1p<<" "<<(void*)node2p<<endl);
|
2019-08-04 03:49:39 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
return same(node1p->user3p(), node2p->user3p()) && same(node1p->user5p(), node2p->user5p())
|
|
|
|
|
&& node1p->user2p()->type() == node2p->user2p()->type();
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p,
|
|
|
|
|
AstNode* extra2p) {
|
2020-08-15 16:12:55 +02:00
|
|
|
// Legal for extra1p/2p to be nullptr, we'll compare with other assigns with extras also
|
|
|
|
|
// nullptr
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const rhsp = assignp->rhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
rhsp->user2p(assignp);
|
|
|
|
|
rhsp->user3p(extra1p);
|
|
|
|
|
rhsp->user5p(extra2p);
|
|
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
const auto inserted = m_dupFinder.insert(rhsp);
|
|
|
|
|
const auto dupit = m_dupFinder.findDuplicate(rhsp, this);
|
|
|
|
|
// Even though rhsp was just inserted, V3DupFinder::findDuplicate doesn't
|
|
|
|
|
// return anything in the hash that has the same pointer (V3DupFinder::findDuplicate)
|
2019-05-19 22:13:13 +02:00
|
|
|
// So dupit is either a different, duplicate rhsp, or the end of the hash.
|
2021-05-21 02:41:46 +02:00
|
|
|
if (dupit != m_dupFinder.end()) {
|
|
|
|
|
m_dupFinder.erase(inserted);
|
2021-10-22 14:56:48 +02:00
|
|
|
return VN_AS(dupit->second->user2p(), NodeAssign);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2019-08-04 03:49:39 +02:00
|
|
|
// Retain new inserted information
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2019-08-04 03:49:39 +02:00
|
|
|
|
|
|
|
|
void check() {
|
2021-05-21 02:41:46 +02:00
|
|
|
for (const auto& itr : m_dupFinder) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nodep = itr.second;
|
|
|
|
|
const AstNode* const activep = nodep->user3p();
|
|
|
|
|
const AstNode* const condVarp = nodep->user5p();
|
2019-08-04 03:49:39 +02:00
|
|
|
if (!isReplaced(nodep)) {
|
|
|
|
|
// This class won't break if activep isn't an active, or
|
|
|
|
|
// ifVar isn't a var, but this is checking the caller's construction.
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(!activep || (!VN_DELETED(activep) && VN_IS(activep, Active)), nodep,
|
2021-05-21 02:41:46 +02:00
|
|
|
"V3DupFinder check failed, lost active pointer");
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp), nodep,
|
2021-05-21 02:41:46 +02:00
|
|
|
"V3DupFinder check failed, lost if pointer");
|
2019-08-04 03:49:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Have we seen the rhs of this assign before?
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class GateDedupeVarVisitor final : public VNVisitor {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Given a node, it is visited to try to find the AstNodeAssign under
|
|
|
|
|
// it that can used for dedupe.
|
2013-02-21 02:26:53 +01:00
|
|
|
// Right now, only the following node trees are supported for dedupe.
|
2013-02-21 02:14:15 +01:00
|
|
|
// 1. AstNodeAssign
|
|
|
|
|
// 2. AstAlways -> AstNodeAssign
|
2013-02-21 02:26:53 +01:00
|
|
|
// (Note, the assign must also be the only node under the always)
|
2013-02-21 02:14:15 +01:00
|
|
|
// 3. AstAlways -> AstNodeIf -> AstNodeAssign
|
2013-02-21 02:26:53 +01:00
|
|
|
// (Note, the IF must be the only node under the always,
|
|
|
|
|
// and the assign must be the only node under the if, other than the ifcond)
|
|
|
|
|
// Any other ordering or node type, except for an AstComment, makes it not dedupable
|
2013-02-21 02:14:15 +01:00
|
|
|
private:
|
|
|
|
|
// STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
GateDedupeHash m_ghash; // Hash used to find dupes of rhs of assign
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNodeAssign* m_assignp = nullptr; // Assign found for dedupe
|
|
|
|
|
AstNode* m_ifCondp = nullptr; // IF condition that assign is under
|
|
|
|
|
bool m_always = false; // Assign is under an always
|
|
|
|
|
bool m_dedupable = true; // Determined the assign to be dedupable
|
2013-02-21 02:14:15 +01:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* assignp) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_dedupable) {
|
|
|
|
|
// I think we could safely dedupe an always block with multiple
|
|
|
|
|
// non-blocking statements, but erring on side of caution here
|
|
|
|
|
if (!m_assignp) {
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlways* alwaysp) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_dedupable) {
|
|
|
|
|
if (!m_always) {
|
|
|
|
|
m_always = true;
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(alwaysp->stmtsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
// Ugly support for latches of the specific form -
|
|
|
|
|
// always @(...)
|
|
|
|
|
// if (...)
|
|
|
|
|
// foo = ...; // or foo <= ...;
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* ifp) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_dedupable) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_always && !m_ifCondp && !ifp->elsesp()) {
|
|
|
|
|
// we're under an always, this is the first IF, and there's no else
|
2019-05-19 22:13:13 +02:00
|
|
|
m_ifCondp = ifp->condp();
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(ifp->thensp());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2013-02-21 02:26:53 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstComment*) override {} // NOP
|
2013-02-21 02:14:15 +01:00
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode*) override { //
|
2019-05-19 22:13:13 +02:00
|
|
|
m_dedupable = false;
|
2013-02-21 02:26:53 +01:00
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
GateDedupeVarVisitor() = default;
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateDedupeVarVisitor() override = default;
|
2013-02-21 02:14:15 +01:00
|
|
|
// PUBLIC METHODS
|
|
|
|
|
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
|
2020-08-15 16:12:55 +02:00
|
|
|
m_assignp = nullptr;
|
|
|
|
|
m_ifCondp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_always = false;
|
|
|
|
|
m_dedupable = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_dedupable && m_assignp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* const lhsp = m_assignp->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Possible todo, handle more complex lhs expressions
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstNodeVarRef* const lhsVarRefp = VN_CAST(lhsp, NodeVarRef)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(lhsVarRefp->varScopep() == consumerVarScopep, consumerVarScopep,
|
|
|
|
|
"Consumer doesn't match lhs of assign");
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstNodeAssign* const dup
|
|
|
|
|
= m_ghash.hashAndFindDupe(m_assignp, activep, m_ifCondp)) {
|
2018-10-15 00:39:33 +02:00
|
|
|
return static_cast<AstNodeVarRef*>(dup->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
void hashReplace(AstNode* oldp, AstNode* newp) { m_ghash.hashReplace(oldp, newp); }
|
2013-02-21 02:14:15 +01:00
|
|
|
};
|
|
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// ######################################################################
|
2019-08-04 03:49:39 +02:00
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
static void eliminate(AstNode* logicp,
|
|
|
|
|
const std::unordered_map<AstVarScope*, AstNode*>& substitutions,
|
2022-07-31 19:36:56 +02:00
|
|
|
GateDedupeVarVisitor* varVisp) {
|
|
|
|
|
|
2022-09-02 20:31:13 +02:00
|
|
|
// Recursion filter holding already replaced variables
|
|
|
|
|
std::unordered_set<const AstVarScope*> replaced(substitutions.size() * 2);
|
|
|
|
|
|
|
|
|
|
const std::function<void(AstNodeVarRef*)> visit = [&, varVisp](AstNodeVarRef* nodep) -> void {
|
2022-08-01 13:22:56 +02:00
|
|
|
// See if this variable has a substitution
|
2022-09-02 20:31:13 +02:00
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
|
|
|
|
const auto& it = substitutions.find(vscp);
|
2022-08-01 13:22:56 +02:00
|
|
|
if (it == substitutions.end()) return;
|
2022-09-02 20:31:13 +02:00
|
|
|
|
|
|
|
|
// Do not substitute circular logic
|
|
|
|
|
if (!replaced.insert(vscp).second) return;
|
|
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
AstNode* const substp = it->second;
|
2022-07-31 19:36:56 +02:00
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// Substitute in the new tree
|
2022-07-31 19:36:56 +02:00
|
|
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
|
|
|
|
"Can't replace lvalue assignments with const var");
|
|
|
|
|
UASSERT_OBJ(!(VN_IS(substp, NodeVarRef) && nodep->same(substp)),
|
|
|
|
|
// Prevent an infinite loop...
|
|
|
|
|
substp, "Replacing node with itself; perhaps circular logic?");
|
|
|
|
|
// The replacement
|
|
|
|
|
AstNode* const newp = substp->cloneTree(false);
|
|
|
|
|
// Which fileline() to use? If replacing with logic, an error/warning is likely to want
|
|
|
|
|
// to point to the logic IE what we're replacing with. However, a VARREF should point
|
|
|
|
|
// to the original as it's otherwise confusing to throw warnings that point to a PIN
|
|
|
|
|
// rather than where the pin us used.
|
|
|
|
|
if (VN_IS(newp, VarRef)) newp->fileline(nodep->fileline());
|
|
|
|
|
// Make the newp an rvalue like nodep. This facilitates the hashing in dedupe.
|
|
|
|
|
if (AstNodeVarRef* const varrefp = VN_CAST(newp, NodeVarRef)) {
|
|
|
|
|
varrefp->access(VAccess::READ);
|
|
|
|
|
}
|
|
|
|
|
// Update hash?
|
|
|
|
|
if (varVisp) varVisp->hashReplace(nodep, newp);
|
|
|
|
|
// Replace the node
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2022-08-01 13:22:56 +02:00
|
|
|
// Recursively substitute the new tree
|
2022-10-20 14:48:44 +02:00
|
|
|
newp->foreach(visit);
|
2022-09-02 20:31:13 +02:00
|
|
|
|
|
|
|
|
// Remove from recursion filter
|
|
|
|
|
replaced.erase(vscp);
|
2022-08-01 13:22:56 +02:00
|
|
|
};
|
2022-07-31 19:36:56 +02:00
|
|
|
|
2022-10-20 14:48:44 +02:00
|
|
|
logicp->foreach(visit);
|
2019-08-04 03:49:39 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 13:22:56 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Recurse through the graph, looking for duplicate expressions on the rhs of an assign
|
2013-02-21 02:14:15 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateDedupeGraphVisitor final : public GateGraphBaseVisitor {
|
2013-02-21 02:14:15 +01:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user2p -> bool: already visited
|
2022-01-02 19:56:40 +01:00
|
|
|
// VNUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
2020-04-15 13:58:34 +02:00
|
|
|
VDouble0 m_numDeduped; // Statistic tracking
|
|
|
|
|
GateDedupeVarVisitor m_varVisitor; // Looks for a dupe of the logic
|
2020-08-15 19:11:27 +02:00
|
|
|
int m_depth = 0; // Iteration depth
|
2013-02-21 02:14:15 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateVarVertex* vvertexp, VNUser) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check that we haven't been here before
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_depth > GATE_DEDUP_MAX_DEPTH)
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0}; // Break loops; before user2 set so hit this vertex later
|
|
|
|
|
if (vvertexp->varScp()->user2()) return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
vvertexp->varScp()->user2(true);
|
2013-02-21 02:14:15 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
m_depth++;
|
|
|
|
|
if (vvertexp->inSize1()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNodeVarRef* const dupVarRefp = static_cast<AstNodeVarRef*>(
|
2022-09-16 01:58:01 +02:00
|
|
|
vvertexp->iterateInEdges(*this, VNUser{vvertexp}).toNodep());
|
2019-07-05 02:07:46 +02:00
|
|
|
if (dupVarRefp) { // visit(GateLogicVertex*...) returned match
|
2021-11-26 23:55:36 +01:00
|
|
|
const V3GraphEdge* edgep = vvertexp->inBeginp();
|
2021-11-13 19:50:44 +01:00
|
|
|
GateLogicVertex* const lvertexp = static_cast<GateLogicVertex*>(edgep->fromp());
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(vvertexp->dedupable(), vvertexp->varScp(),
|
2020-08-15 16:12:55 +02:00
|
|
|
"GateLogicVertex* visit should have returned nullptr "
|
2020-04-15 13:58:34 +02:00
|
|
|
"if consumer var vertex is not dedupable.");
|
2021-11-26 23:55:36 +01:00
|
|
|
const GateOkVisitor okVisitor{lvertexp->nodep(), false, true};
|
2019-05-19 22:13:13 +02:00
|
|
|
if (okVisitor.isSimple()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVarScope* const dupVarScopep = dupVarRefp->varScopep();
|
|
|
|
|
GateVarVertex* const dupVvertexp
|
2018-10-15 00:39:33 +02:00
|
|
|
= reinterpret_cast<GateVarVertex*>(dupVarScopep->user1p());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "replacing " << vvertexp << " with " << dupVvertexp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
++m_numDeduped;
|
|
|
|
|
// Replace all of this varvertex's consumers with dupVarRefp
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphEdge* outedgep = vvertexp->outBeginp(); outedgep;) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const GateLogicVertex* const consumeVertexp
|
2022-09-02 12:29:02 +02:00
|
|
|
= static_cast<GateLogicVertex*>(outedgep->top());
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const consumerp = consumeVertexp->nodep();
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9,
|
|
|
|
|
"elim src vtx" << lvertexp << " node " << lvertexp->nodep() << endl);
|
|
|
|
|
UINFO(9,
|
|
|
|
|
"elim cons vtx" << consumeVertexp << " node " << consumerp << endl);
|
|
|
|
|
UINFO(9, "elim var vtx " << vvertexp << " node " << vvertexp->varScp()
|
|
|
|
|
<< endl);
|
|
|
|
|
UINFO(9, "replace with " << dupVarRefp << endl);
|
2019-12-07 22:49:11 +01:00
|
|
|
if (lvertexp == consumeVertexp) {
|
|
|
|
|
UINFO(9, "skipping as self-recirculates\n");
|
|
|
|
|
} else {
|
2022-08-01 13:22:56 +02:00
|
|
|
eliminate(consumerp, {std::make_pair(vvertexp->varScp(), dupVarRefp)},
|
|
|
|
|
&m_varVisitor);
|
2019-12-07 22:49:11 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
outedgep = outedgep->relinkFromp(dupVvertexp);
|
|
|
|
|
}
|
|
|
|
|
// Propagate attributes
|
|
|
|
|
dupVvertexp->propagateAttrClocksFrom(vvertexp);
|
|
|
|
|
// Remove inputs links
|
2021-11-26 23:55:36 +01:00
|
|
|
while (V3GraphEdge* const inedgep = vvertexp->inBeginp()) {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(inedgep->unlinkDelete(), inedgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
vvertexp->user(true);
|
|
|
|
|
lvertexp->user(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_depth--;
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
2016-11-27 15:40:12 +01:00
|
|
|
// Given iterated logic, starting at vu which was consumer's GateVarVertex
|
2020-08-15 16:12:55 +02:00
|
|
|
// Returns a varref that has the same logic input; or nullptr if none
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateLogicVertex* lvertexp, VNUser vu) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
lvertexp->iterateInEdges(*this);
|
2013-02-21 02:14:15 +01:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
const GateVarVertex* const consumerVvertexpp
|
|
|
|
|
= static_cast<GateVarVertex*>(vu.toGraphVertex());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lvertexp->dedupable() && consumerVvertexpp->dedupable()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nodep = lvertexp->nodep();
|
|
|
|
|
AstVarScope* const consumerVarScopep = consumerVvertexpp->varScp();
|
2019-12-07 22:41:34 +01:00
|
|
|
// TODO: Doing a simple pointer comparison of activep won't work
|
2019-05-19 22:13:13 +02:00
|
|
|
// optimally for statements under generated clocks. Statements under
|
|
|
|
|
// different generated clocks will never compare as equal, even if the
|
|
|
|
|
// generated clocks are deduped into one clock.
|
2021-11-13 19:50:44 +01:00
|
|
|
AstActive* const activep = lvertexp->activep();
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{m_varVisitor.findDupe(nodep, consumerVarScopep, activep)};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-12-07 22:41:34 +01:00
|
|
|
explicit GateDedupeGraphVisitor(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: GateGraphBaseVisitor{graphp} {}
|
2019-12-07 22:41:34 +01:00
|
|
|
void dedupeTree(GateVarVertex* vvertexp) { vvertexp->accept(*this); }
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 numDeduped() { return m_numDeduped; }
|
2013-02-21 02:14:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::dedupe() {
|
|
|
|
|
AstNode::user2ClearTree();
|
2021-07-12 00:42:01 +02:00
|
|
|
GateDedupeGraphVisitor deduper{&m_graph};
|
2013-02-21 02:14:15 +01:00
|
|
|
// Traverse starting from each of the clocks
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Gate dedupe() clocks:\n");
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (vvertexp->isClock()) deduper.dedupeTree(vvertexp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
// Traverse starting from each of the outputs
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Gate dedupe() outputs:\n");
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isWritable()) {
|
|
|
|
|
deduper.dedupeTree(vvertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 02:14:15 +01:00
|
|
|
}
|
|
|
|
|
m_statDedupLogic += deduper.numDeduped();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-06 03:09:35 +01:00
|
|
|
//######################################################################
|
2017-09-12 01:18:58 +02:00
|
|
|
// Recurse through the graph, try to merge assigns
|
2014-11-06 03:09:35 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateMergeAssignsGraphVisitor final : public GateGraphBaseVisitor {
|
2014-11-06 03:09:35 +01:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeAssign* m_assignp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstActive* m_activep = nullptr;
|
2020-08-16 15:55:36 +02:00
|
|
|
GateLogicVertex* m_logicvp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
VDouble0 m_numMergedAssigns; // Statistic tracking
|
2014-11-06 03:09:35 +01:00
|
|
|
|
2017-09-12 01:18:58 +02:00
|
|
|
// assemble two Sel into one if possible
|
2014-11-06 03:09:35 +01:00
|
|
|
AstSel* merge(AstSel* pre, AstSel* cur) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVarRef* const preVarRefp = VN_CAST(pre->fromp(), VarRef);
|
|
|
|
|
AstVarRef* const curVarRefp = VN_CAST(cur->fromp(), VarRef);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) {
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr; // not the same var
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstConst* const pstart = VN_CAST(pre->lsbp(), Const);
|
|
|
|
|
const AstConst* const pwidth = VN_CAST(pre->widthp(), Const);
|
|
|
|
|
const AstConst* const cstart = VN_CAST(cur->lsbp(), Const);
|
|
|
|
|
const AstConst* const cwidth = VN_CAST(cur->widthp(), Const);
|
2020-08-15 16:12:55 +02:00
|
|
|
if (!pstart || !pwidth || !cstart || !cwidth) return nullptr; // too complicated
|
2020-04-15 13:58:34 +02:00
|
|
|
if (cur->lsbConst() + cur->widthConst() == pre->lsbConst()) {
|
2022-11-20 23:40:38 +01:00
|
|
|
return new AstSel{curVarRefp->fileline(), curVarRefp->cloneTree(false),
|
|
|
|
|
cur->lsbConst(), pre->widthConst() + cur->widthConst()};
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2014-11-06 03:09:35 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateVarVertex* vvertexp, VNUser) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;) {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3GraphEdge* oldedgep = edgep;
|
|
|
|
|
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
|
2021-11-13 19:50:44 +01:00
|
|
|
if (GateLogicVertex* const lvertexp
|
|
|
|
|
= dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
|
|
|
|
|
if (AstNodeAssign* const assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
|
2021-10-22 14:56:48 +02:00
|
|
|
UINFO(9, "assing to the nodep[" << VN_AS(assignp->lhsp(), Sel)->lsbConst()
|
|
|
|
|
<< "]" << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// first assign with Sel-lhs
|
|
|
|
|
if (!m_activep) m_activep = lvertexp->activep();
|
|
|
|
|
if (!m_logicvp) m_logicvp = lvertexp;
|
|
|
|
|
if (!m_assignp) m_assignp = assignp;
|
|
|
|
|
|
|
|
|
|
// not under the same active
|
|
|
|
|
if (m_activep != lvertexp->activep()) {
|
|
|
|
|
m_activep = lvertexp->activep();
|
|
|
|
|
m_logicvp = lvertexp;
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-11-06 03:09:35 +01:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstSel* const preselp = VN_CAST(m_assignp->lhsp(), Sel);
|
|
|
|
|
AstSel* const curselp = VN_CAST(assignp->lhsp(), Sel);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!preselp || !curselp) continue;
|
|
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstSel* const newselp = merge(preselp, curselp)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "assemble to new sel: " << newselp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// replace preSel with newSel
|
|
|
|
|
preselp->replaceWith(newselp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(preselp->deleteTree(), preselp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// create new rhs for pre assignment
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newrhsp = new AstConcat{
|
2021-11-13 19:50:44 +01:00
|
|
|
m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false),
|
2022-11-20 23:40:38 +01:00
|
|
|
assignp->rhsp()->cloneTree(false)};
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const oldrhsp = m_assignp->rhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
oldrhsp->replaceWith(newrhsp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(oldrhsp->deleteTree(), oldrhsp);
|
2020-04-15 13:58:34 +02:00
|
|
|
m_assignp->dtypeChgWidthSigned(m_assignp->width() + assignp->width(),
|
|
|
|
|
m_assignp->width() + assignp->width(),
|
2020-04-20 03:19:09 +02:00
|
|
|
VSigning::SIGNED);
|
2019-05-19 22:13:13 +02:00
|
|
|
// don't need to delete, will be handled
|
2020-04-15 13:58:34 +02:00
|
|
|
// assignp->unlinkFrBack(); VL_DO_DANGLING(assignp->deleteTree(),
|
|
|
|
|
// assignp);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// update the graph
|
|
|
|
|
{
|
|
|
|
|
// delete all inedges to lvertexp
|
|
|
|
|
if (!lvertexp->inEmpty()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep;) {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3GraphEdge* oedgep = ledgep;
|
|
|
|
|
ledgep = ledgep->inNextp();
|
2021-11-13 19:50:44 +01:00
|
|
|
GateEitherVertex* const fromvp
|
2022-09-02 12:29:02 +02:00
|
|
|
= static_cast<GateEitherVertex*>(oedgep->fromp());
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{m_graphp, fromvp, m_logicvp, 1};
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(oedgep->unlinkDelete(), oedgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// delete all outedges to lvertexp, only one
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(oldedgep->unlinkDelete(), oldedgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
++m_numMergedAssigns;
|
|
|
|
|
} else {
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
m_logicvp = lvertexp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2014-11-06 03:09:35 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateLogicVertex*, VNUser) override { //
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2014-11-06 03:09:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-12-07 22:41:34 +01:00
|
|
|
explicit GateMergeAssignsGraphVisitor(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: GateGraphBaseVisitor{graphp} {
|
|
|
|
|
m_graphp = graphp; // In base
|
2014-11-06 03:09:35 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
void mergeAssignsTree(GateVarVertex* vvertexp) { vvertexp->accept(*this); }
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 numMergedAssigns() { return m_numMergedAssigns; }
|
2014-11-06 03:09:35 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::mergeAssigns() {
|
2019-08-04 03:49:39 +02:00
|
|
|
UINFO(6, "mergeAssigns\n");
|
2021-07-12 00:42:01 +02:00
|
|
|
GateMergeAssignsGraphVisitor merger{&m_graph};
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
merger.mergeAssignsTree(vvertexp);
|
|
|
|
|
}
|
2014-11-06 03:09:35 +01:00
|
|
|
}
|
|
|
|
|
m_statAssignMerged += merger.numMergedAssigns();
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 14:05:21 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Find a var's offset in a concatenation
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class GateConcatVisitor final : public VNVisitor {
|
2017-05-09 14:05:21 +02:00
|
|
|
private:
|
|
|
|
|
// STATE
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarScope* m_vscp = nullptr; // Varscope we're trying to find
|
2020-08-15 19:11:27 +02:00
|
|
|
int m_offset = 0; // Current offset of varscope
|
|
|
|
|
int m_found_offset = 0; // Found offset of varscope
|
|
|
|
|
bool m_found = false; // Offset found
|
2017-05-09 14:05:21 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Concat search var (off = " << m_offset << ") - " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->varScopep() == m_vscp && !nodep->user2() && !m_found) {
|
|
|
|
|
// A concatenation may use the same var multiple times
|
|
|
|
|
// But the graph will initially have an edge per instance
|
|
|
|
|
nodep->user2(true);
|
|
|
|
|
m_found_offset = m_offset;
|
|
|
|
|
m_found = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Concat found var (off = " << m_offset << ") - " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_offset += nodep->dtypep()->width();
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConcat* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Concat search (off = " << m_offset << ") - " << nodep << endl);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep->rhsp());
|
|
|
|
|
iterate(nodep->lhsp());
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2017-05-09 14:05:21 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
GateConcatVisitor() = default;
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateConcatVisitor() override = default;
|
2017-05-09 14:05:21 +02:00
|
|
|
// PUBLIC METHODS
|
|
|
|
|
bool concatOffset(AstConcat* concatp, AstVarScope* vscp, int& offsetr) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_vscp = vscp;
|
|
|
|
|
m_offset = 0;
|
|
|
|
|
m_found = false;
|
|
|
|
|
// Iterate
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(concatp);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Concat Offset (found = " << m_found << ") (" << m_found_offset
|
|
|
|
|
<< ") - " << concatp << " : " << vscp
|
|
|
|
|
<< endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
offsetr = m_found_offset;
|
|
|
|
|
return m_found;
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Recurse through the graph, looking for clock vectors to bypass
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateClkDecompState final {
|
2017-05-09 14:05:21 +02:00
|
|
|
public:
|
2021-11-26 23:55:36 +01:00
|
|
|
const int m_offset;
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const m_last_vsp;
|
2020-08-15 19:11:27 +02:00
|
|
|
GateClkDecompState(int offset, AstVarScope* vsp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_offset{offset}
|
|
|
|
|
, m_last_vsp{vsp} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~GateClkDecompState() = default;
|
2017-05-09 14:05:21 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class GateClkDecompGraphVisitor final : public GateGraphBaseVisitor {
|
2017-05-09 14:05:21 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user2p -> bool: already visited
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_seen_clk_vectors = 0;
|
|
|
|
|
AstVarScope* m_clk_vsp = nullptr;
|
|
|
|
|
GateVarVertex* m_clk_vvertexp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
GateConcatVisitor m_concat_visitor;
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_total_seen_clk_vectors = 0;
|
|
|
|
|
int m_total_decomposed_clk_vectors = 0;
|
2017-05-09 14:05:21 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateVarVertex* vvertexp, VNUser vu) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check that we haven't been here before
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const vsp = vvertexp->varScp();
|
2022-09-16 01:58:01 +02:00
|
|
|
if (vsp->user2SetOnce()) return VNUser{0};
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Var - " << vvertexp << " : " << vsp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vsp->varp()->width() > 1) {
|
|
|
|
|
m_seen_clk_vectors++;
|
|
|
|
|
m_total_seen_clk_vectors++;
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
const GateClkDecompState* const currState = reinterpret_cast<GateClkDecompState*>(vu.c());
|
2022-11-20 23:40:38 +01:00
|
|
|
GateClkDecompState nextState{currState->m_offset, vsp};
|
2022-09-16 01:58:01 +02:00
|
|
|
vvertexp->iterateCurrentOutEdges(*this, VNUser{&nextState});
|
2020-04-15 13:58:34 +02:00
|
|
|
if (vsp->varp()->width() > 1) --m_seen_clk_vectors;
|
2019-05-19 22:13:13 +02:00
|
|
|
vsp->user2(false);
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0}; // Unused
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
VNUser visit(GateLogicVertex* lvertexp, VNUser vu) override {
|
2021-11-26 23:55:36 +01:00
|
|
|
const GateClkDecompState* const currState = reinterpret_cast<GateClkDecompState*>(vu.c());
|
2019-05-19 22:13:13 +02:00
|
|
|
int clk_offset = currState->m_offset;
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstAssignW* const assignp = VN_CAST(lvertexp->nodep(), AssignW)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Logic (off = " << clk_offset << ") - " << lvertexp << " : "
|
|
|
|
|
<< m_clk_vsp << endl);
|
2020-05-01 01:22:58 +02:00
|
|
|
// RHS
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstSel* const rselp = VN_CAST(assignp->rhsp(), Sel)) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(rselp->lsbp(), Const) && VN_IS(rselp->widthp(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (clk_offset < rselp->lsbConst() || clk_offset > rselp->msbConst()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Sel [ " << rselp->msbConst() << " : "
|
|
|
|
|
<< rselp->lsbConst() << " ] dropped clock ("
|
|
|
|
|
<< clk_offset << ")" << endl);
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
clk_offset -= rselp->lsbConst();
|
|
|
|
|
} else {
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstConcat* const catp = VN_CAST(assignp->rhsp(), Concat)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Concat searching - " << assignp->lhsp() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
int concat_offset;
|
2020-05-01 01:22:58 +02:00
|
|
|
if (!m_concat_visitor.concatOffset(catp, currState->m_last_vsp,
|
|
|
|
|
concat_offset /*ref*/)) {
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
clk_offset += concat_offset;
|
2020-05-01 01:22:58 +02:00
|
|
|
} else if (VN_IS(assignp->rhsp(), VarRef)) {
|
|
|
|
|
UINFO(9, "CLK DECOMP VarRef searching - " << assignp->lhsp() << endl);
|
|
|
|
|
} else {
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-05-01 01:22:58 +02:00
|
|
|
// LHS
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstSel* const lselp = VN_CAST(assignp->lhsp(), Sel)) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(lselp->lsbp(), Const) && VN_IS(lselp->widthp(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
clk_offset += lselp->lsbConst();
|
|
|
|
|
} else {
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstVarRef* const vrp = VN_CAST(assignp->lhsp(), VarRef)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vrp->dtypep()->width() == 1 && m_seen_clk_vectors) {
|
|
|
|
|
if (clk_offset != 0) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Should only make it here with clk_offset = 0" << endl);
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-05-01 01:22:58 +02:00
|
|
|
UINFO(9, "CLK DECOMP Connecting - " << assignp->lhsp() << endl);
|
|
|
|
|
UINFO(9, " to - " << m_clk_vsp << endl);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const rhsp = assignp->rhsp();
|
2022-11-20 23:40:38 +01:00
|
|
|
rhsp->replaceWith(new AstVarRef{rhsp->fileline(), m_clk_vsp, VAccess::READ});
|
2021-11-26 23:55:36 +01:00
|
|
|
while (V3GraphEdge* const edgep = lvertexp->inBeginp()) {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{m_graphp, m_clk_vvertexp, lvertexp, 1};
|
2019-05-19 22:13:13 +02:00
|
|
|
m_total_decomposed_clk_vectors++;
|
|
|
|
|
}
|
2020-05-01 01:22:58 +02:00
|
|
|
} else {
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
GateClkDecompState nextState{clk_offset, currState->m_last_vsp};
|
2022-09-16 01:58:01 +02:00
|
|
|
return lvertexp->iterateCurrentOutEdges(*this, VNUser{&nextState});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-09-16 01:58:01 +02:00
|
|
|
return VNUser{0};
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-12-07 22:41:34 +01:00
|
|
|
explicit GateClkDecompGraphVisitor(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: GateGraphBaseVisitor{graphp} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~GateClkDecompGraphVisitor() override {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Optimizations, Clocker seen vectors", m_total_seen_clk_vectors);
|
2020-04-15 13:58:34 +02:00
|
|
|
V3Stats::addStat("Optimizations, Clocker decomposed vectors",
|
|
|
|
|
m_total_decomposed_clk_vectors);
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
void clkDecomp(GateVarVertex* vvertexp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP Starting Var - " << vvertexp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_seen_clk_vectors = 0;
|
|
|
|
|
m_clk_vsp = vvertexp->varScp();
|
|
|
|
|
m_clk_vvertexp = vvertexp;
|
2022-11-20 23:40:38 +01:00
|
|
|
GateClkDecompState nextState{0, m_clk_vsp};
|
2022-09-16 01:58:01 +02:00
|
|
|
vvertexp->accept(*this, VNUser{&nextState});
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void GateVisitor::decomposeClkVectors() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Starting clock decomposition" << endl);
|
2017-05-09 14:05:21 +02:00
|
|
|
AstNode::user2ClearTree();
|
2021-07-12 00:42:01 +02:00
|
|
|
GateClkDecompGraphVisitor decomposer{&m_graph};
|
2020-04-15 13:58:34 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (GateVarVertex* const vertp = dynamic_cast<GateVarVertex*>(itp)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarScope* const vsp = vertp->varScp();
|
2019-10-05 13:54:14 +02:00
|
|
|
if (vsp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vsp->varp()->width() > 1) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "Clocker > 1 bit, not decomposing: " << vsp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "CLK DECOMP - " << vertp << " : " << vsp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
decomposer.clkDecomp(vertp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-09 14:05:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2022-07-31 19:36:56 +02:00
|
|
|
// Gate class functions
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-07-31 19:36:56 +02:00
|
|
|
void V3Gate::gateAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
{ const GateVisitor visitor{nodep}; } // Destruct before checking
|
2022-09-18 21:53:42 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("gate", 0, dumpTree() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|