2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2009-01-06 17:03:57 +01:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Deals with tristate logic
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2009-01-06 17:03:57 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 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
|
2009-01-06 17:03:57 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Tristate's Transformations:
|
|
|
|
|
//
|
2012-05-05 20:49:43 +02:00
|
|
|
// Modify the design to expand tristate logic into its
|
2013-02-21 03:51:39 +01:00
|
|
|
// corresponding two state representation. At the lowest levels,
|
2012-05-09 03:53:22 +02:00
|
|
|
//
|
|
|
|
|
// In detail:
|
|
|
|
|
//
|
|
|
|
|
// Over each module, from child to parent:
|
|
|
|
|
// Build a graph, connecting signals together so we can propagate tristates
|
|
|
|
|
// Variable becomes tristate with
|
2024-11-26 00:25:36 +01:00
|
|
|
// VAR->isInout
|
2018-10-27 23:29:00 +02:00
|
|
|
// VAR->isPullup/isPulldown (converted to AstPullup/AstPulldown
|
|
|
|
|
// BufIf0/1
|
2012-05-09 03:53:22 +02:00
|
|
|
// All variables on the LHS need to become tristate when there is:
|
2019-05-19 22:13:13 +02:00
|
|
|
// CONST-> with Z value on the RHS of an assignment
|
|
|
|
|
// AstPin with lower connection a tristate
|
|
|
|
|
// A tristate signal on the RHS
|
|
|
|
|
// (this can't generally be determined until that signal is resolved)
|
2012-05-09 03:53:22 +02:00
|
|
|
// When LHS becomes tristate, then mark all RHS nodes as tristate
|
|
|
|
|
// so any tristate varrefs on the right will propagate.
|
|
|
|
|
//
|
|
|
|
|
// Walk graph's tristate indication on each logic block with tristates
|
|
|
|
|
// propagating downstream to every other logic block.
|
|
|
|
|
//
|
|
|
|
|
// Expressions that have Z in them are converted into two state
|
2012-04-22 03:45:28 +02:00
|
|
|
// drivers and corresponding output enable signals are generated.
|
|
|
|
|
// These enable signals get transformed and regenerated through any
|
|
|
|
|
// logic that they may go through until they hit the module level. At
|
|
|
|
|
// the module level, all the output enable signals from what can be
|
|
|
|
|
// many tristate drivers are combined together to produce a single
|
2013-02-21 03:51:39 +01:00
|
|
|
// driver and output enable. If the signal propagates up into higher
|
2012-04-22 03:45:28 +02:00
|
|
|
// modules, then new ports are created with for the signal with
|
|
|
|
|
// suffixes __en and __out. The original port is turned from an inout
|
|
|
|
|
// to an input and the __out port carries the output driver signal and
|
|
|
|
|
// the __en port carried the output enable for that driver.
|
2009-01-06 17:03:57 +01:00
|
|
|
//
|
2013-02-28 04:59:17 +01:00
|
|
|
// Note 1800-2012 adds user defined resolution functions. This suggests
|
|
|
|
|
// long term this code should be scoped-based and resolve all nodes at once
|
2021-12-19 22:06:45 +01:00
|
|
|
// rather than hierarchically. If/when that is done, make sure to avoid
|
|
|
|
|
// duplicating vars and logic that is common between each instance of a
|
|
|
|
|
// module.
|
|
|
|
|
//
|
2022-09-14 13:39:27 +02:00
|
|
|
//
|
|
|
|
|
// Another thing done in this phase is signal strength handling.
|
2022-09-19 10:54:20 +02:00
|
|
|
// Currently they are only supported in assignments and gates parsed as assignments (see verilog.y)
|
2022-09-27 03:21:37 +02:00
|
|
|
// when any of the cases occurs:
|
|
|
|
|
// - it is possible to statically resolve all drivers,
|
|
|
|
|
// - all assignments that passed the static resolution have symmetric strengths (the same strength
|
|
|
|
|
// is related to both 0 and 1 values).
|
|
|
|
|
//
|
|
|
|
|
// It is possible to statically resolve all drivers when the strongest assignment has RHS marked as
|
|
|
|
|
// non-tristate. If the RHS is equal to z, that assignment has to be skipped. Since the value may
|
2022-12-23 17:32:38 +01:00
|
|
|
// be not known at Verilation time, cases with tristates on RHS can't be handled statically.
|
2022-09-14 13:39:27 +02:00
|
|
|
//
|
2022-09-19 10:54:20 +02:00
|
|
|
// Static resolution is split into 2 parts.
|
|
|
|
|
// First part can be done before tristate propagation. It is about removing assignments that are
|
|
|
|
|
// weaker or equally strong as the strongest assignment with constant on RHS that has all bits
|
|
|
|
|
// the same (equal to 0 or 1). It is done in the following way:
|
2022-09-14 13:39:27 +02:00
|
|
|
// - The assignment of value 0 (size may be greater than 1), that has greatest strength (the
|
|
|
|
|
// one corresponding to 0) of all other assignments of 0, has to be found.
|
|
|
|
|
// - The same is done for value '1 and strength corresponding to value 1.
|
|
|
|
|
// - The greater of these two strengths is chosen. If they are equal the one that corresponds
|
|
|
|
|
// to 1 is taken as the greatest.
|
|
|
|
|
// - All assignments, that have strengths weaker or equal to the one that was found before, are
|
|
|
|
|
// removed. They are the assignments with constants on the RHS and also all assignments that have
|
|
|
|
|
// both strengths non-greater that the one was found, because they are weaker no matter what is on
|
|
|
|
|
// RHS.
|
|
|
|
|
//
|
2022-09-19 10:54:20 +02:00
|
|
|
// Second part of static resolution is done after tristate propagation.
|
|
|
|
|
// At that moment it is known that some expressions can't be equal to z. The exact value is
|
|
|
|
|
// unknown (except the ones with constants that were handled before), so weaker of both strengths
|
|
|
|
|
// has to be taken into account. All weaker assignments can be safely removed. It is done in
|
|
|
|
|
// similar way to the first part:
|
|
|
|
|
// - The assignment with non-tristate RHS with the greatest weaker strength has to be found.
|
|
|
|
|
// - Then all not stronger assignments can be removed.
|
|
|
|
|
//
|
2022-09-27 03:21:37 +02:00
|
|
|
// All assignments that are stronger than the strongest with non-tristate RHS are then tried to be
|
|
|
|
|
// handled dynamically. Currently it is supported only on assignments with symmetric strengths.
|
|
|
|
|
// In this case, the exact value of the RHS doesn't matter. It only matters if it equals z or not.
|
|
|
|
|
// Such assignments are handled by changing the values to z of these bits that are overwritten by
|
|
|
|
|
// stronger assignments. Then all assignments can be aggregated as they would have equal strengths
|
|
|
|
|
// (by | on them and their __en expressions). To change the value to z, the RHS should be & with
|
|
|
|
|
// negation of __en expression of stronger assignments. Changing RHS's __en expression is not
|
|
|
|
|
// needed, because it will be then aggregated with __en expression of stronger assignments using |,
|
|
|
|
|
// so & with the negation can be safely skipped.
|
|
|
|
|
// So the values of overwritten bits are actually changed to 0, which doesn't affect stronger
|
|
|
|
|
// assignments, because | operation was used.
|
|
|
|
|
//
|
|
|
|
|
// Dynamic handling is implemented in the following way:
|
|
|
|
|
// - group the assignments by their strengths,
|
|
|
|
|
// - handle assignments of the same strength by aggregating values with |
|
|
|
|
|
// - assign results to var__strength and var__strength__en variables
|
|
|
|
|
// - aggregate the results:
|
|
|
|
|
// orp = orp | (var__strength & ~enp)
|
|
|
|
|
// enp = enp | var__strength__en,
|
|
|
|
|
// where orp is aggregated value and enp is aggregated __en value.
|
2022-09-14 13:39:27 +02:00
|
|
|
//
|
|
|
|
|
// There is a possible problem with equally strong assignments, because multiple assignments with
|
|
|
|
|
// the same strength, but different values should result in x value, but these values are
|
|
|
|
|
// unsupported.
|
2009-01-06 17:03:57 +01:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2009-01-06 17:03:57 +01:00
|
|
|
#include "V3Tristate.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2023-10-29 02:12:27 +02:00
|
|
|
#include "V3AstUserAllocator.h"
|
2012-05-09 03:53:22 +02:00
|
|
|
#include "V3Graph.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Inst.h"
|
|
|
|
|
#include "V3Stats.h"
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2009-01-06 17:03:57 +01:00
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class TristateBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
2009-01-06 17:03:57 +01:00
|
|
|
public:
|
2012-04-22 03:45:28 +02:00
|
|
|
// METHODS
|
2009-01-06 17:03:57 +01:00
|
|
|
};
|
|
|
|
|
|
2012-05-09 03:53:22 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Graph support classes
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TristateVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TristateVertex, V3GraphVertex)
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const m_nodep;
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_isTristate = false; // Logic indicates a tristate
|
|
|
|
|
bool m_feedsTri = false; // Propagates to a tristate node (on RHS)
|
|
|
|
|
bool m_processed = false; // Tristating was cleaned up
|
2012-05-09 03:53:22 +02:00
|
|
|
public:
|
|
|
|
|
TristateVertex(V3Graph* graphp, AstNode* nodep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TristateVertex() override = default;
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2023-04-20 13:02:31 +02:00
|
|
|
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVar* varp() const { return VN_CAST(nodep(), Var); }
|
2023-08-03 08:52:52 +02:00
|
|
|
string name() const override {
|
2021-06-14 20:50:40 +02:00
|
|
|
return ((isTristate() ? "tri\\n"
|
|
|
|
|
: feedsTri() ? "feed\\n"
|
|
|
|
|
: "-\\n")
|
2020-04-15 13:58:34 +02:00
|
|
|
+ (nodep()->prettyTypeName() + " " + cvtToHex(nodep())));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override {
|
2021-06-14 20:50:40 +02:00
|
|
|
return (varp() ? (isTristate() ? "darkblue"
|
|
|
|
|
: feedsTri() ? "blue"
|
|
|
|
|
: "lightblue")
|
|
|
|
|
: (isTristate() ? "darkgreen"
|
|
|
|
|
: feedsTri() ? "green"
|
|
|
|
|
: "lightgreen"));
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
2023-04-20 13:02:31 +02:00
|
|
|
bool isTristate() const VL_MT_SAFE { return m_isTristate; }
|
2024-06-23 01:50:46 +02:00
|
|
|
void isTristate(bool flag) { m_isTristate = flag; }
|
2023-04-20 13:02:31 +02:00
|
|
|
bool feedsTri() const VL_MT_SAFE { return m_feedsTri; }
|
2024-06-23 01:50:46 +02:00
|
|
|
void feedsTri(bool flag) { m_feedsTri = flag; }
|
2012-05-09 03:53:22 +02:00
|
|
|
bool processed() const { return m_processed; }
|
2024-06-23 01:50:46 +02:00
|
|
|
void processed(bool flag) { m_processed = flag; }
|
2012-05-09 03:53:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TristateGraph final {
|
2012-05-09 03:53:22 +02:00
|
|
|
// NODE STATE
|
2023-10-29 02:12:27 +02:00
|
|
|
// AstVar::user4p -> TristateVertex* for variable being built
|
|
|
|
|
// VNUser4InUse m_inuser4; // In visitor below
|
2012-05-09 03:53:22 +02:00
|
|
|
|
|
|
|
|
// TYPES
|
|
|
|
|
public:
|
2021-03-13 00:10:45 +01:00
|
|
|
using VarVec = std::vector<AstVar*>;
|
2012-05-09 03:53:22 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
private:
|
2012-05-09 03:53:22 +02:00
|
|
|
// MEMBERS
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Graph m_graph; // Logic graph
|
2012-05-09 03:53:22 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2012-05-09 03:53:22 +02:00
|
|
|
TristateGraph() { clear(); }
|
|
|
|
|
virtual ~TristateGraph() { clear(); }
|
2020-01-25 16:19:59 +01:00
|
|
|
VL_UNCOPYABLE(TristateGraph);
|
2012-05-09 03:53:22 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// METHODS
|
|
|
|
|
|
|
|
|
|
TristateVertex* makeVertex(AstNode* nodep) {
|
2023-10-29 02:12:27 +02:00
|
|
|
TristateVertex* vertexp = reinterpret_cast<TristateVertex*>(nodep->user4p());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vertexp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(6, " New vertex " << nodep);
|
2022-11-20 23:40:38 +01:00
|
|
|
vertexp = new TristateVertex{&m_graph, nodep};
|
2023-10-29 02:12:27 +02:00
|
|
|
nodep->user4p(vertexp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return vertexp;
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// METHODS - Graph optimization
|
|
|
|
|
void graphWalkRecurseFwd(TristateVertex* vtxp, int level) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Propagate tristate forward to all sinks
|
|
|
|
|
// For example if on a CONST, propagate through CONCATS to ASSIGN
|
|
|
|
|
// to LHS VARREF of signal to tristate
|
|
|
|
|
if (!vtxp->isTristate()) return; // tristate involved
|
|
|
|
|
if (vtxp->user() == 1) return;
|
|
|
|
|
vtxp->user(1); // Recursed
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Mark tri " << level << " " << vtxp);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
|
|
|
|
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.top());
|
2019-05-19 22:13:13 +02:00
|
|
|
// Doesn't hurt to not check if already set, but by doing so when we
|
|
|
|
|
// print out the debug messages, we'll see this node at level 0 instead.
|
|
|
|
|
if (!vvertexp->isTristate()) {
|
|
|
|
|
vvertexp->isTristate(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
graphWalkRecurseFwd(vvertexp, level + 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// A variable is tristated. Find all of the LHS VARREFs that
|
|
|
|
|
// drive this signal now need tristate drivers
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
|
|
|
|
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.fromp());
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstVarRef* const refp = VN_CAST(vvertexp->nodep(), VarRef)) {
|
2020-11-07 16:37:55 +01:00
|
|
|
if (refp->access().isWriteOrRW()
|
2019-05-19 22:13:13 +02:00
|
|
|
// Doesn't hurt to not check if already set, but by doing so when we
|
|
|
|
|
// print out the debug messages, we'll see this node at level 0 instead.
|
|
|
|
|
&& !vvertexp->isTristate()) {
|
|
|
|
|
vvertexp->isTristate(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
graphWalkRecurseFwd(vvertexp, level + 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
void graphWalkRecurseBack(TristateVertex* vtxp, int level) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Called only on a tristate node; propagate a feedsTri attribute "backwards"
|
|
|
|
|
// towards any driving nodes, i.e. from a LHS VARREF back to a driving RHS VARREF
|
|
|
|
|
// This way if the RHS VARREF is also tristated we'll connect the
|
|
|
|
|
// enables up to the LHS VARREF. Otherwise if not marked
|
|
|
|
|
// feedsTri() we'll drop the LHS' enables, if any
|
|
|
|
|
if (!(vtxp->isTristate() || vtxp->feedsTri())) return; // tristate involved
|
|
|
|
|
if (vtxp->user() == 3) return;
|
|
|
|
|
vtxp->user(3); // Recursed
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Mark feedstri " << level << " " << vtxp);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
|
|
|
|
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.fromp());
|
2019-05-19 22:13:13 +02:00
|
|
|
// Doesn't hurt to not check if already set, but by doing so when we
|
|
|
|
|
// print out the debug messages, we'll see this node at level 0 instead.
|
|
|
|
|
if (!vvertexp->feedsTri()) {
|
|
|
|
|
vvertexp->feedsTri(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
graphWalkRecurseBack(vvertexp, level + 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
2020-01-25 16:19:59 +01:00
|
|
|
bool empty() const { return m_graph.empty(); }
|
2012-05-09 03:53:22 +02:00
|
|
|
void clear() {
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
const TristateVertex& vvertex = static_cast<TristateVertex&>(vtx);
|
|
|
|
|
if (vvertex.isTristate() && !vvertex.processed()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Not v3errorSrc as no reason to stop the world
|
2025-09-20 14:19:42 +02:00
|
|
|
vvertex.nodep()->v3warn(E_UNSUPPORTED, "Unsupported tristate construct"
|
|
|
|
|
" (in graph; not converted): "
|
|
|
|
|
<< vvertex.nodep()->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_graph.clear();
|
2023-10-29 02:12:27 +02:00
|
|
|
AstNode::user4ClearTree(); // Wipe all node user4p's that point to vertexes
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
void graphWalk(AstNodeModule* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Walking " << nodep);
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
graphWalkRecurseFwd(static_cast<TristateVertex*>(&vtx), 0);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
graphWalkRecurseBack(static_cast<TristateVertex*>(&vtx), 0);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name());
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
void associate(AstNode* fromp, AstNode* top) {
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, makeVertex(fromp), makeVertex(top), 1};
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
2022-09-19 10:54:20 +02:00
|
|
|
void deleteVerticesFromSubtreeRecurse(AstNode* nodep) {
|
|
|
|
|
if (!nodep) return;
|
|
|
|
|
// Skip vars, because they may be connected to more than one varref
|
|
|
|
|
if (!VN_IS(nodep, Var)) {
|
2023-10-29 02:12:27 +02:00
|
|
|
TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user4p());
|
2022-09-19 10:54:20 +02:00
|
|
|
if (vertexp) vertexp->unlinkDelete(&m_graph);
|
|
|
|
|
}
|
2025-03-16 19:34:19 +01:00
|
|
|
if (AstNode* const refp = nodep->op1p()) deleteVerticesFromSubtreeRecurse(refp);
|
|
|
|
|
if (AstNode* const refp = nodep->op2p()) deleteVerticesFromSubtreeRecurse(refp);
|
|
|
|
|
if (AstNode* const refp = nodep->op3p()) deleteVerticesFromSubtreeRecurse(refp);
|
|
|
|
|
if (AstNode* const refp = nodep->op4p()) deleteVerticesFromSubtreeRecurse(refp);
|
2022-09-19 10:54:20 +02:00
|
|
|
}
|
2021-12-19 22:06:45 +01:00
|
|
|
void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); }
|
2025-08-22 02:06:43 +02:00
|
|
|
bool isTristate(const AstNode* nodep) {
|
2023-10-29 02:12:27 +02:00
|
|
|
const TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user4p());
|
2019-05-19 22:13:13 +02:00
|
|
|
return vertexp && vertexp->isTristate();
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
2025-08-22 02:06:43 +02:00
|
|
|
bool feedsTri(const AstNode* nodep) {
|
2023-10-29 02:12:27 +02:00
|
|
|
const TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user4p());
|
2019-05-19 22:13:13 +02:00
|
|
|
return vertexp && vertexp->feedsTri();
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
void didProcess(AstNode* nodep) {
|
2023-10-29 02:12:27 +02:00
|
|
|
TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user4p());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vertexp) {
|
|
|
|
|
// Not v3errorSrc as no reason to stop the world
|
2025-09-20 14:19:42 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported tristate construct (not in propagation graph): "
|
|
|
|
|
<< nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// We don't warn if no vertexp->isTristate() as the creation
|
|
|
|
|
// process makes midling nodes that don't have it set
|
|
|
|
|
vertexp->processed(true);
|
|
|
|
|
}
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
// ITERATOR METHODS
|
|
|
|
|
VarVec tristateVars() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return all tristate variables
|
|
|
|
|
VarVec v;
|
2024-03-26 00:06:25 +01:00
|
|
|
for (const V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
const TristateVertex& vvertex = static_cast<const TristateVertex&>(vtx);
|
|
|
|
|
if (vvertex.isTristate()) {
|
|
|
|
|
if (AstVar* const nodep = VN_CAST(vvertex.nodep(), Var)) v.push_back(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return v;
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2009-01-06 17:03:57 +01:00
|
|
|
//######################################################################
|
2012-04-22 03:45:28 +02:00
|
|
|
// Given a node, flip any VarRef from LValue to RValue (i.e. make it an input)
|
2012-04-29 14:24:32 +02:00
|
|
|
// See also V3LinkLValue::linkLValueSet
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TristatePinVisitor final : public TristateBaseVisitor {
|
2012-05-09 03:53:22 +02:00
|
|
|
TristateGraph& m_tgraph;
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool m_lvalue; // Flip to be an LVALUE
|
2012-04-22 03:45:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2020-11-07 16:37:55 +01:00
|
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Tristate unexpected on R/W access flip");
|
|
|
|
|
if (m_lvalue && !nodep->access().isWriteOrRW()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Flip-to-LValue " << nodep);
|
2020-09-07 23:09:25 +02:00
|
|
|
nodep->access(VAccess::WRITE);
|
2020-11-07 16:37:55 +01:00
|
|
|
} else if (!m_lvalue && !nodep->access().isReadOnly()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Flip-to-RValue " << nodep);
|
2020-09-07 23:09:25 +02:00
|
|
|
nodep->access(VAccess::READ);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Mark the ex-output as tristated
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " setTristate-subpin " << nodep->varp());
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.setTristate(nodep->varp());
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Doesn't work because we'd set lvalue on the array index's var
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!m_lvalue, nodep, "ArraySel conversion to output, under tristate node");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2017-11-23 20:55:32 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSliceSel* nodep) override {
|
2017-11-23 20:55:32 +01:00
|
|
|
// Doesn't work because we'd set lvalue on the array index's var
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!m_lvalue, nodep, "SliceSel conversion to output, under tristate node");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2012-05-09 03:53:22 +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
|
|
|
|
2012-04-22 03:45:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2012-05-09 03:53:22 +02:00
|
|
|
TristatePinVisitor(AstNode* nodep, TristateGraph& tgraph, bool lvalue)
|
2020-08-22 13:43:56 +02:00
|
|
|
: m_tgraph(tgraph) // Need () or GCC 4.8 false warning
|
2020-08-16 15:55:36 +02:00
|
|
|
, m_lvalue{lvalue} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TristatePinVisitor() override = default;
|
2012-04-22 03:45:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TristateVisitor final : public TristateBaseVisitor {
|
2009-01-06 17:03:57 +01:00
|
|
|
// NODE STATE
|
2019-05-19 22:13:13 +02:00
|
|
|
// *::user1p -> pointer to output enable __en expressions
|
|
|
|
|
// *::user2 -> int - already visited, see U2_ enum
|
2023-10-29 02:12:27 +02:00
|
|
|
// AstVar::user3p -> See AuxAstVar via m_varAux
|
2012-05-09 03:53:22 +02:00
|
|
|
// See TristateGraph:
|
2023-10-29 02:12:27 +02:00
|
|
|
// AstVar::user4p -> TristateVertex* for variable being built
|
|
|
|
|
// AstStmt::user4p -> TristateVertex* for this statement
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
|
|
|
|
const VNUser3InUse m_inuser3;
|
|
|
|
|
const VNUser4InUse m_inuser4;
|
2023-10-29 02:12:27 +02:00
|
|
|
|
|
|
|
|
struct AuxAstVar final {
|
|
|
|
|
AstPull* pullp = nullptr; // pullup/pulldown direction
|
|
|
|
|
AstVar* outVarp = nullptr; // output __out var
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AstUser3Allocator<AstVar, AuxAstVar> m_varAux;
|
2012-04-22 03:45:28 +02:00
|
|
|
|
|
|
|
|
// TYPES
|
2024-01-20 21:06:46 +01:00
|
|
|
struct RefStrength final {
|
2022-09-27 03:21:37 +02:00
|
|
|
AstVarRef* m_varrefp;
|
|
|
|
|
VStrength m_strength;
|
|
|
|
|
RefStrength(AstVarRef* varrefp, VStrength strength)
|
|
|
|
|
: m_varrefp{varrefp}
|
|
|
|
|
, m_strength{strength} {}
|
|
|
|
|
};
|
|
|
|
|
using RefStrengthVec = std::vector<RefStrength>;
|
2024-01-26 02:33:43 +01:00
|
|
|
using VarMap = std::map<AstVar*, RefStrengthVec*>;
|
2022-09-14 13:39:27 +02:00
|
|
|
using Assigns = std::vector<AstAssignW*>;
|
|
|
|
|
using VarToAssignsMap = std::map<AstVar*, Assigns>;
|
2020-08-16 18:05:35 +02:00
|
|
|
enum : uint8_t {
|
2020-04-15 13:58:34 +02:00
|
|
|
U2_GRAPHING = 1, // bit[0] if did m_graphing visit
|
|
|
|
|
U2_NONGRAPH = 2, // bit[1] if did !m_graphing visit
|
|
|
|
|
U2_BOTH = 3
|
|
|
|
|
}; // Both bits set
|
2012-04-22 03:45:28 +02:00
|
|
|
|
|
|
|
|
// MEMBERS
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_graphing = false; // Major mode - creating graph
|
2012-05-09 03:53:22 +02:00
|
|
|
//
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstCell* m_cellp = nullptr; // current cell
|
2021-12-19 22:06:45 +01:00
|
|
|
VarMap m_lhsmap; // Tristate left-hand-side driver map
|
2022-09-14 13:39:27 +02:00
|
|
|
VarToAssignsMap m_assigns; // Assigns in current module
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_unique = 0;
|
|
|
|
|
bool m_alhs = false; // On LHS of assignment
|
2025-09-26 15:19:48 +02:00
|
|
|
bool m_inAlias = false; // Inside alias statement
|
2022-09-27 03:21:37 +02:00
|
|
|
VStrength m_currentStrength = VStrength::STRONG; // Current strength of assignment,
|
|
|
|
|
// Used only on LHS of assignment
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* m_logicp = nullptr; // Current logic being built
|
2020-04-15 13:58:34 +02:00
|
|
|
TristateGraph m_tgraph; // Logic graph
|
2012-04-22 03:45:28 +02:00
|
|
|
|
|
|
|
|
// STATS
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 m_statTriSigs; // stat tracking
|
2012-04-22 03:45:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2022-07-30 17:52:35 +02:00
|
|
|
string dbgState() const {
|
2020-04-15 13:58:34 +02:00
|
|
|
string o = (m_graphing ? " gr " : " ng ");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) o += "alhs ";
|
|
|
|
|
return o;
|
2012-05-06 22:52:08 +02:00
|
|
|
}
|
2021-12-19 22:06:45 +01:00
|
|
|
void modAddStmtp(AstNode* nodep, AstNode* newp) {
|
|
|
|
|
if (!m_modp) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Creating tristate signal not underneath a module: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
|
|
|
|
} else {
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newp);
|
2021-12-19 22:06:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
2012-05-09 03:53:22 +02:00
|
|
|
void associateLogic(AstNode* fromp, AstNode* top) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_logicp) m_tgraph.associate(fromp, top);
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
2021-12-31 20:46:16 +01:00
|
|
|
AstConst* newAllZerosOrOnes(AstNode* nodep, bool ones) {
|
|
|
|
|
V3Number num{nodep, nodep->width()};
|
|
|
|
|
if (ones) num.setAllBits1();
|
|
|
|
|
AstConst* const newp = new AstConst{nodep->fileline(), num};
|
|
|
|
|
return newp;
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* getEnp(AstNode* nodep) {
|
2022-08-07 14:12:57 +02:00
|
|
|
if (nodep->user1p()) {
|
|
|
|
|
if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
|
|
|
|
|
if (refp->varp()->isIO()) {
|
|
|
|
|
// When reading a tri-state port, we can always use the value
|
|
|
|
|
// because such port will have resolution logic in upper module.
|
|
|
|
|
return newAllZerosOrOnes(nodep, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-12-19 22:06:45 +01:00
|
|
|
// There's no select being built yet, so add what will become a
|
|
|
|
|
// constant output enable driver of all 1's
|
2021-12-31 20:46:16 +01:00
|
|
|
nodep->user1p(newAllZerosOrOnes(nodep, true));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-12-19 22:06:45 +01:00
|
|
|
// Otherwise return the previous output enable
|
2022-11-13 21:33:11 +01:00
|
|
|
return VN_AS(nodep->user1p(), NodeExpr);
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* getCreateEnVarp(AstVar* invarp, bool isTop) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return the master __en for the specified input variable
|
|
|
|
|
if (!invarp->user1p()) {
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* const newp
|
2024-12-02 05:03:19 +01:00
|
|
|
= new AstVar{invarp->fileline(), isTop ? VVarType::VAR : VVarType::MODULETEMP,
|
2024-04-11 15:02:58 +02:00
|
|
|
invarp->name() + "__en", invarp};
|
2024-12-02 05:03:19 +01:00
|
|
|
// Inherited VDirection::INPUT
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newenv " << newp);
|
2021-12-19 22:06:45 +01:00
|
|
|
modAddStmtp(invarp, newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
invarp->user1p(newp); // find envar given invarp
|
|
|
|
|
}
|
2021-10-22 14:56:48 +02:00
|
|
|
return VN_AS(invarp->user1p(), Var);
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
2022-10-01 16:34:30 +02:00
|
|
|
AstConst* getNonZConstp(AstConst* const constp) {
|
|
|
|
|
FileLine* const fl = constp->fileline();
|
|
|
|
|
V3Number numz{constp, constp->width()};
|
|
|
|
|
numz.opBitsZ(constp->num()); // Z->1, else 0
|
|
|
|
|
V3Number numz0{constp, constp->width()};
|
|
|
|
|
numz0.opNot(numz); // Z->0, else 1
|
|
|
|
|
return new AstConst{fl, numz0};
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* getEnExprBasedOnOriginalp(AstNodeExpr* const nodep) {
|
2022-10-01 16:34:30 +02:00
|
|
|
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
|
2024-04-11 15:02:58 +02:00
|
|
|
return new AstVarRef{varrefp->fileline(), getCreateEnVarp(varrefp->varp(), false),
|
2022-10-01 16:34:30 +02:00
|
|
|
VAccess::READ};
|
|
|
|
|
} else if (AstConst* const constp = VN_CAST(nodep, Const)) {
|
|
|
|
|
return getNonZConstp(constp);
|
|
|
|
|
} else if (AstExtend* const extendp = VN_CAST(nodep, Extend)) {
|
|
|
|
|
// Extend inserts 0 at the beginning. 0 in __en variable means that this bit equals z,
|
|
|
|
|
// so in order to preserve the value of the original AstExtend node we should insert 1
|
|
|
|
|
// instead of 0. To extend __en expression we have to negate its lhsp() and then negate
|
|
|
|
|
// whole extend.
|
|
|
|
|
|
|
|
|
|
// Unlink lhsp before copying to save unnecessary copy of lhsp
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = extendp->lhsp()->unlinkFrBack();
|
2023-10-15 04:20:42 +02:00
|
|
|
AstExtend* const enExtendp = extendp->cloneTree(false);
|
2022-10-01 16:34:30 +02:00
|
|
|
extendp->lhsp(lhsp);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const enLhsp = getEnExprBasedOnOriginalp(lhsp);
|
2022-10-01 16:34:30 +02:00
|
|
|
enExtendp->lhsp(new AstNot{enLhsp->fileline(), enLhsp});
|
|
|
|
|
return new AstNot{enExtendp->fileline(), enExtendp};
|
|
|
|
|
} else if (AstSel* const selp = VN_CAST(nodep, Sel)) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const fromp = selp->fromp()->unlinkFrBack();
|
2023-10-15 04:20:42 +02:00
|
|
|
AstSel* const enSelp = selp->cloneTree(false);
|
2022-10-01 16:34:30 +02:00
|
|
|
selp->fromp(fromp);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const enFromp = getEnExprBasedOnOriginalp(fromp);
|
2022-10-01 16:34:30 +02:00
|
|
|
enSelp->fromp(enFromp);
|
|
|
|
|
return enSelp;
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported tristate construct: " << nodep->prettyTypeName()
|
|
|
|
|
<< " in function " << __func__);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* getCreateOutVarp(AstVar* invarp, bool isTop) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return the master __out for the specified input variable
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!m_varAux(invarp).outVarp) {
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* const newp
|
2024-12-02 05:03:19 +01:00
|
|
|
= new AstVar{invarp->fileline(), isTop ? VVarType::VAR : VVarType::MODULETEMP,
|
2024-04-11 15:02:58 +02:00
|
|
|
invarp->name() + "__out", invarp};
|
2024-12-02 05:03:19 +01:00
|
|
|
// Inherited VDirection::OUTPUT
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newout " << newp);
|
2021-12-19 22:06:45 +01:00
|
|
|
modAddStmtp(invarp, newp);
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(invarp).outVarp = newp; // find outvar given invarp
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-10-29 02:12:27 +02:00
|
|
|
return m_varAux(invarp).outVarp;
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2012-04-29 16:14:13 +02:00
|
|
|
AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVar* const newp = new AstVar{fromp->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
"__Vtriunconn" + cvtToStr(m_unique++), dtypep};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newunc " << newp);
|
2021-12-19 22:06:45 +01:00
|
|
|
modAddStmtp(newp, newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
return newp;
|
2012-04-27 03:11:48 +02:00
|
|
|
}
|
|
|
|
|
|
2012-05-09 03:53:22 +02:00
|
|
|
void mapInsertLhsVarRef(AstVarRef* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " mapInsertLhsVarRef " << nodep);
|
2023-10-28 14:38:02 +02:00
|
|
|
AstVar* const key = nodep->varp();
|
|
|
|
|
const auto pair = m_lhsmap.emplace(key, nullptr);
|
|
|
|
|
if (pair.second) pair.first->second = new RefStrengthVec;
|
|
|
|
|
pair.first->second->push_back(RefStrength{nodep, m_currentStrength});
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newEnableDeposit(AstSel* selp, AstNodeExpr* enp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Form a "deposit" instruction for given enable, using existing select as a template.
|
|
|
|
|
// Would be nicer if we made this a new AST type
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const newp = new AstShiftL{
|
|
|
|
|
selp->fileline(), new AstExtend{selp->fileline(), enp, selp->fromp()->width()},
|
2023-09-17 04:50:54 +02:00
|
|
|
selp->lsbp()->cloneTreePure(false), selp->fromp()->width()};
|
2019-05-19 22:13:13 +02:00
|
|
|
return newp;
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2012-05-05 21:03:00 +02:00
|
|
|
void setPullDirection(AstVar* varp, AstPull* pullp) {
|
2023-10-29 02:12:27 +02:00
|
|
|
const AstPull* const oldpullp = m_varAux(varp).pullp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!oldpullp) {
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(varp).pullp = pullp; // save off to indicate the pull direction
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
if (oldpullp->direction() != pullp->direction()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
pullp->v3warn(E_UNSUPPORTED, "Unsupported: Conflicting pull directions.\n"
|
2020-11-19 03:03:23 +01:00
|
|
|
<< pullp->warnContextPrimary() << '\n'
|
2020-06-10 01:20:16 +02:00
|
|
|
<< oldpullp->warnOther()
|
|
|
|
|
<< "... Location of conflicting pull.\n"
|
|
|
|
|
<< oldpullp->warnContextSecondary());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2012-04-22 03:45:28 +02:00
|
|
|
void checkUnhandled(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check for unsupported tristate constructs. This is not a 100% check.
|
|
|
|
|
// The best way would be to visit the tree again and find any user1p()
|
|
|
|
|
// pointers that did not get picked up and expanded.
|
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Ignore Var's because they end up adjacent to statements
|
2018-02-02 03:32:58 +01:00
|
|
|
if ((nodep->op1p() && nodep->op1p()->user1p() && !VN_IS(nodep->op1p(), Var))
|
2022-11-22 23:46:58 +01:00
|
|
|
|| (nodep->op2p() && nodep->op2p()->user1p() && !VN_IS(nodep->op2p(), Var))
|
|
|
|
|
|| (nodep->op3p() && nodep->op3p()->user1p() && !VN_IS(nodep->op3p(), Var))
|
|
|
|
|
|| (nodep->op4p() && nodep->op4p()->user1p() && !VN_IS(nodep->op4p(), Var))) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2012-05-05 20:55:35 +02:00
|
|
|
void insertTristates(AstNodeModule* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Go through all the vars and find any that are outputs without drivers
|
|
|
|
|
// or inouts without high-Z logic and put a 1'bz driver on them and add
|
|
|
|
|
// them to the lhs map so they get expanded correctly.
|
2021-11-26 23:55:36 +01:00
|
|
|
const TristateGraph::VarVec vars = m_tgraph.tristateVars();
|
2020-11-11 04:10:38 +01:00
|
|
|
for (auto varp : vars) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_tgraph.isTristate(varp)) {
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_lhsmap.find(varp);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (it == m_lhsmap.end()) {
|
2021-12-19 22:06:45 +01:00
|
|
|
// This variable is floating, set output enable to
|
|
|
|
|
// always be off on this assign
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " Adding driver to var " << varp);
|
2021-12-31 20:46:16 +01:00
|
|
|
AstConst* const constp = newAllZerosOrOnes(varp, false);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarRef* const varrefp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const newp = new AstAssignW{varp->fileline(), varrefp, constp};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newoev " << newp);
|
2021-12-31 20:46:16 +01:00
|
|
|
varrefp->user1p(newAllZerosOrOnes(varp, false));
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{newp});
|
2019-05-19 22:13:13 +02:00
|
|
|
mapInsertLhsVarRef(varrefp); // insertTristates will convert
|
2021-12-19 22:06:45 +01:00
|
|
|
// // to a varref to the __out# variable
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now go through the lhs driver map and generate the output
|
|
|
|
|
// enable logic for any tristates.
|
|
|
|
|
// Note there might not be any drivers.
|
2024-01-26 02:33:43 +01:00
|
|
|
for (auto varp : vars) { // Use vector instead of m_lhsmap iteration for node stability
|
|
|
|
|
const auto it = m_lhsmap.find(varp);
|
|
|
|
|
if (it == m_lhsmap.end()) continue;
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const invarp = it->first;
|
2022-09-27 03:21:37 +02:00
|
|
|
RefStrengthVec* refsp = it->second;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Figure out if this var needs tristate expanded.
|
2021-12-19 22:06:45 +01:00
|
|
|
if (m_tgraph.isTristate(invarp)) {
|
|
|
|
|
insertTristatesSignal(nodep, invarp, refsp);
|
|
|
|
|
} else {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " NO TRISTATE ON:" << invarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-12-19 22:06:45 +01:00
|
|
|
// Delete the map and vector list now that we have expanded it.
|
|
|
|
|
m_lhsmap.erase(invarp);
|
|
|
|
|
VL_DO_DANGLING(delete refsp, refsp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
void aggregateTriSameStrength(AstNodeModule* nodep, AstVar* const varp, AstVar* const envarp,
|
|
|
|
|
RefStrengthVec::iterator beginStrength,
|
|
|
|
|
RefStrengthVec::iterator endStrength) {
|
2022-12-03 00:46:38 +01:00
|
|
|
// For each driver separate variables (normal and __en) are created and initialized with
|
2022-09-27 03:21:37 +02:00
|
|
|
// values. In case of normal variable, the original expression is reused. Their values are
|
|
|
|
|
// aggregated using | to form one expression, which are assigned to varp end envarp.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* orp = nullptr;
|
|
|
|
|
AstNodeExpr* enp = nullptr;
|
2022-09-27 03:21:37 +02:00
|
|
|
|
|
|
|
|
for (auto it = beginStrength; it != endStrength; it++) {
|
|
|
|
|
AstVarRef* refp = it->m_varrefp;
|
|
|
|
|
|
|
|
|
|
// create the new lhs driver for this var
|
|
|
|
|
AstVar* const newLhsp = new AstVar{varp->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
varp->name() + "__out" + cvtToStr(m_unique),
|
2024-03-19 11:43:06 +01:00
|
|
|
varp}; // 2-state ok; sep enable
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newout " << newLhsp);
|
2022-09-27 03:21:37 +02:00
|
|
|
nodep->addStmtsp(newLhsp);
|
2023-08-03 08:52:52 +02:00
|
|
|
refp->varp(newLhsp);
|
2022-09-27 03:21:37 +02:00
|
|
|
|
|
|
|
|
// create a new var for this drivers enable signal
|
2024-03-19 11:43:06 +01:00
|
|
|
AstVar* const newEnLhsp
|
|
|
|
|
= new AstVar{varp->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
varp->name() + "__en" + cvtToStr(m_unique++), envarp}; // 2-state ok
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newenlhsp " << newEnLhsp);
|
2022-09-27 03:21:37 +02:00
|
|
|
nodep->addStmtsp(newEnLhsp);
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const enLhspAssignp = new AstAssignW{
|
2022-09-27 03:21:37 +02:00
|
|
|
refp->fileline(), new AstVarRef{refp->fileline(), newEnLhsp, VAccess::WRITE},
|
|
|
|
|
getEnp(refp)};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newenlhspAssignp " << enLhspAssignp);
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{enLhspAssignp});
|
2022-09-27 03:21:37 +02:00
|
|
|
|
|
|
|
|
// now append this driver to the driver logic.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ref1p = new AstVarRef{refp->fileline(), newLhsp, VAccess::READ};
|
|
|
|
|
AstNodeExpr* const ref2p = new AstVarRef{refp->fileline(), newEnLhsp, VAccess::READ};
|
|
|
|
|
AstNodeExpr* const andp = new AstAnd{refp->fileline(), ref1p, ref2p};
|
2022-09-27 03:21:37 +02:00
|
|
|
|
|
|
|
|
// or this to the others
|
|
|
|
|
orp = (!orp) ? andp : new AstOr{refp->fileline(), orp, andp};
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ref3p = new AstVarRef{refp->fileline(), newEnLhsp, VAccess::READ};
|
2022-09-27 03:21:37 +02:00
|
|
|
enp = (!enp) ? ref3p : new AstOr{ref3p->fileline(), enp, ref3p};
|
|
|
|
|
}
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const assp = new AstAssignW{
|
2022-09-27 03:21:37 +02:00
|
|
|
varp->fileline(), new AstVarRef{varp->fileline(), varp, VAccess::WRITE}, orp};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newassp " << assp);
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{assp});
|
2022-09-27 03:21:37 +02:00
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const enAssp = new AstAssignW{
|
2022-09-27 03:21:37 +02:00
|
|
|
envarp->fileline(), new AstVarRef{envarp->fileline(), envarp, VAccess::WRITE}, enp};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newenassp " << enAssp);
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{enAssp});
|
2022-09-27 03:21:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insertTristatesSignal(AstNodeModule* nodep, AstVar* const invarp, RefStrengthVec* refsp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " TRISTATE EXPANDING:" << invarp);
|
2021-12-19 22:06:45 +01:00
|
|
|
++m_statTriSigs;
|
|
|
|
|
m_tgraph.didProcess(invarp);
|
|
|
|
|
|
|
|
|
|
// If the lhs var is a port, then we need to create ports for
|
|
|
|
|
// the output (__out) and output enable (__en) signals. The
|
|
|
|
|
// original port gets converted to an input. Don't tristate expand
|
|
|
|
|
// if this is the top level so that we can force the final
|
|
|
|
|
// tristate resolution at the top.
|
2024-04-11 15:02:58 +02:00
|
|
|
// Or if this is a top-level inout, do tristate expand if requested
|
|
|
|
|
// by pinsInoutEnables(). The resolution will be done outside of
|
|
|
|
|
// verilator.
|
2021-12-19 22:06:45 +01:00
|
|
|
AstVar* envarp = nullptr;
|
|
|
|
|
AstVar* outvarp = nullptr; // __out
|
|
|
|
|
AstVar* lhsp = invarp; // Variable to assign drive-value to (<in> or __out)
|
2024-04-11 15:02:58 +02:00
|
|
|
bool isTopInout
|
|
|
|
|
= (invarp->direction() == VDirection::INOUT) && invarp->isIO() && nodep->isTop();
|
|
|
|
|
if ((v3Global.opt.pinsInoutEnables() && isTopInout)
|
|
|
|
|
|| ((!nodep->isTop()) && invarp->isIO())) {
|
2021-12-19 22:06:45 +01:00
|
|
|
// This var becomes an input
|
|
|
|
|
invarp->varType2In(); // convert existing port to type input
|
|
|
|
|
// Create an output port (__out)
|
2024-04-11 15:02:58 +02:00
|
|
|
outvarp = getCreateOutVarp(invarp, isTopInout);
|
2021-12-19 22:06:45 +01:00
|
|
|
outvarp->varType2Out();
|
|
|
|
|
lhsp = outvarp; // Must assign to __out, not to normal input signal
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " TRISTATE propagates up with " << lhsp);
|
2021-12-19 22:06:45 +01:00
|
|
|
// Create an output enable port (__en)
|
|
|
|
|
// May already be created if have foo === 1'bz somewhere
|
2024-04-11 15:02:58 +02:00
|
|
|
envarp
|
|
|
|
|
= getCreateEnVarp(invarp, isTopInout); // direction will be sen in visit(AstPin*)
|
2021-12-19 22:06:45 +01:00
|
|
|
//
|
|
|
|
|
outvarp->user1p(envarp);
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(outvarp).pullp = m_varAux(invarp).pullp; // AstPull* propagation
|
2025-05-23 02:29:32 +02:00
|
|
|
if (m_varAux(invarp).pullp) UINFO(9, "propagate pull to " << outvarp);
|
2021-12-19 22:06:45 +01:00
|
|
|
} else if (invarp->user1p()) {
|
|
|
|
|
envarp = VN_AS(invarp->user1p(), Var); // From CASEEQ, foo === 1'bz
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* orp = nullptr;
|
|
|
|
|
AstNodeExpr* enp = nullptr;
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
std::sort(refsp->begin(), refsp->end(),
|
|
|
|
|
[](RefStrength a, RefStrength b) { return a.m_strength > b.m_strength; });
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
auto beginStrength = refsp->begin();
|
|
|
|
|
while (beginStrength != refsp->end()) {
|
|
|
|
|
auto endStrength = beginStrength + 1;
|
|
|
|
|
while (endStrength != refsp->end()
|
|
|
|
|
&& endStrength->m_strength == beginStrength->m_strength)
|
|
|
|
|
endStrength++;
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
FileLine* const fl = beginStrength->m_varrefp->fileline();
|
|
|
|
|
const string strengthVarName = lhsp->name() + "__" + beginStrength->m_strength.ascii();
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
// var__strength variable
|
|
|
|
|
AstVar* varStrengthp = new AstVar{fl, VVarType::MODULETEMP, strengthVarName,
|
2024-03-19 11:43:06 +01:00
|
|
|
invarp}; // 2-state ok; sep enable;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newstrength " << varStrengthp);
|
2022-09-27 03:21:37 +02:00
|
|
|
nodep->addStmtsp(varStrengthp);
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
// var__strength__en variable
|
|
|
|
|
AstVar* enVarStrengthp = new AstVar{fl, VVarType::MODULETEMP, strengthVarName + "__en",
|
2024-03-19 11:43:06 +01:00
|
|
|
invarp}; // 2-state ok;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newenstrength " << enVarStrengthp);
|
2022-09-27 03:21:37 +02:00
|
|
|
nodep->addStmtsp(enVarStrengthp);
|
2021-12-19 22:06:45 +01:00
|
|
|
|
2022-09-27 03:21:37 +02:00
|
|
|
aggregateTriSameStrength(nodep, varStrengthp, enVarStrengthp, beginStrength,
|
|
|
|
|
endStrength);
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprCurrentStrengthp;
|
2022-09-27 03:21:37 +02:00
|
|
|
if (enp) {
|
|
|
|
|
// If weaker driver should be overwritten by a stronger, replace its value with z
|
|
|
|
|
exprCurrentStrengthp
|
|
|
|
|
= new AstAnd{fl, new AstVarRef{fl, varStrengthp, VAccess::READ},
|
2023-09-17 04:50:54 +02:00
|
|
|
new AstNot{fl, enp->cloneTreePure(false)}};
|
2022-09-27 03:21:37 +02:00
|
|
|
} else {
|
|
|
|
|
exprCurrentStrengthp = new AstVarRef{fl, varStrengthp, VAccess::READ};
|
2019-05-10 02:03:19 +02:00
|
|
|
}
|
2022-09-27 03:21:37 +02:00
|
|
|
orp = (!orp) ? exprCurrentStrengthp : new AstOr{fl, orp, exprCurrentStrengthp};
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* enVarStrengthRefp = new AstVarRef{fl, enVarStrengthp, VAccess::READ};
|
2022-09-27 03:21:37 +02:00
|
|
|
|
|
|
|
|
enp = (!enp) ? enVarStrengthRefp : new AstOr{fl, enp, enVarStrengthRefp};
|
|
|
|
|
|
|
|
|
|
beginStrength = endStrength;
|
2021-12-19 22:06:45 +01:00
|
|
|
}
|
2022-09-27 03:21:37 +02:00
|
|
|
|
2021-12-19 22:06:45 +01:00
|
|
|
if (!outvarp) {
|
|
|
|
|
// This is the final pre-forced resolution of the tristate, so we apply
|
|
|
|
|
// the pull direction to any undriven pins.
|
2023-10-29 02:12:27 +02:00
|
|
|
const AstPull* const pullp = m_varAux(lhsp).pullp;
|
2021-12-31 20:46:16 +01:00
|
|
|
bool pull1 = pullp && pullp->direction() == 1; // Else default is down
|
2022-09-27 03:21:37 +02:00
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* undrivenp;
|
2022-09-27 03:21:37 +02:00
|
|
|
if (envarp) {
|
|
|
|
|
undrivenp = new AstNot{envarp->fileline(),
|
|
|
|
|
new AstVarRef{envarp->fileline(), envarp, VAccess::READ}};
|
|
|
|
|
} else {
|
|
|
|
|
if (enp) {
|
|
|
|
|
undrivenp = new AstNot{enp->fileline(), enp};
|
|
|
|
|
} else {
|
|
|
|
|
undrivenp = newAllZerosOrOnes(invarp, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-31 20:46:16 +01:00
|
|
|
undrivenp
|
|
|
|
|
= new AstAnd{invarp->fileline(), undrivenp, newAllZerosOrOnes(invarp, pull1)};
|
2022-09-27 03:21:37 +02:00
|
|
|
orp = new AstOr{invarp->fileline(), orp, undrivenp};
|
2021-12-19 22:06:45 +01:00
|
|
|
}
|
2022-09-27 03:21:37 +02:00
|
|
|
|
2021-12-19 22:06:45 +01:00
|
|
|
if (envarp) {
|
2022-09-27 03:21:37 +02:00
|
|
|
AstAssignW* const enAssp = new AstAssignW{
|
|
|
|
|
enp->fileline(), new AstVarRef{envarp->fileline(), envarp, VAccess::WRITE}, enp};
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, enAssp, "", "enAssp");
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{enAssp});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-09-27 03:21:37 +02:00
|
|
|
|
2021-12-19 22:06:45 +01:00
|
|
|
// __out (child) or <in> (parent) = drive-value expression
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const assp = new AstAssignW{
|
2022-09-27 03:21:37 +02:00
|
|
|
lhsp->fileline(), new AstVarRef{lhsp->fileline(), lhsp, VAccess::WRITE}, orp};
|
2021-12-19 22:06:45 +01:00
|
|
|
assp->user2(U2_BOTH); // Don't process further; already resolved
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, assp, "", "lhsp-eqn");
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
nodep->addStmtsp(new AstAlways{assp});
|
2024-04-11 15:02:58 +02:00
|
|
|
|
|
|
|
|
// If this is a top-level inout, make sure that the INOUT pins get __en and __out
|
|
|
|
|
if (v3Global.opt.pinsInoutEnables() && isTopInout) {
|
|
|
|
|
if (envarp) {
|
|
|
|
|
envarp->primaryIO(true);
|
|
|
|
|
envarp->direction(VDirection::OUTPUT);
|
|
|
|
|
}
|
|
|
|
|
if (outvarp) {
|
|
|
|
|
outvarp->primaryIO(true);
|
|
|
|
|
outvarp->direction(VDirection::OUTPUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-05 20:55:35 +02:00
|
|
|
}
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
bool isOnlyAssignmentIsToLhsVar(const AstAssignW* const nodep) {
|
|
|
|
|
if (const AstVarRef* const varRefp = VN_CAST(nodep->lhsp(), VarRef)) {
|
2022-09-27 03:21:37 +02:00
|
|
|
auto foundIt = m_assigns.find(varRefp->varp());
|
|
|
|
|
if (foundIt != m_assigns.end()) {
|
|
|
|
|
auto const& assignsToVar = foundIt->second;
|
|
|
|
|
if (assignsToVar.size() == 1 && assignsToVar[0] == nodep) return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 13:39:27 +02:00
|
|
|
void addToAssignmentList(AstAssignW* nodep) {
|
|
|
|
|
if (AstVarRef* const varRefp = VN_CAST(nodep->lhsp(), VarRef)) {
|
|
|
|
|
if (varRefp->varp()->isNet()) {
|
|
|
|
|
m_assigns[varRefp->varp()].push_back(nodep);
|
|
|
|
|
} else if (nodep->strengthSpecp()) {
|
|
|
|
|
nodep->strengthSpecp()->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
} else if (nodep->strengthSpecp()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Assignments with signal strength with LHS of type: "
|
|
|
|
|
<< nodep->lhsp()->prettyTypeName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
uint8_t getStrength(const AstAssignW* const nodep, bool value) {
|
2022-09-14 13:39:27 +02:00
|
|
|
if (AstStrengthSpec* const strengthSpec = nodep->strengthSpecp()) {
|
|
|
|
|
return value ? strengthSpec->strength1() : strengthSpec->strength0();
|
|
|
|
|
}
|
|
|
|
|
return VStrength::STRONG; // default strength is strong
|
|
|
|
|
}
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
bool assignmentOfValueOnAllBits(const AstAssignW* const nodep, bool value) {
|
|
|
|
|
if (const AstConst* const constp = VN_CAST(nodep->rhsp(), Const)) {
|
2022-09-14 13:39:27 +02:00
|
|
|
const V3Number num = constp->num();
|
|
|
|
|
return value ? num.isEqAllOnes() : num.isEqZero();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstAssignW* getStrongestAssignmentOfValue(const Assigns& assigns, bool value) {
|
|
|
|
|
auto maxIt = std::max_element(
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
assigns.begin(), assigns.end(), [&](const AstAssignW* ap, const AstAssignW* bp) {
|
2022-09-14 13:39:27 +02:00
|
|
|
bool valuesOnRhsA = assignmentOfValueOnAllBits(ap, value);
|
|
|
|
|
bool valuesOnRhsB = assignmentOfValueOnAllBits(bp, value);
|
|
|
|
|
if (!valuesOnRhsA) return valuesOnRhsB;
|
|
|
|
|
if (!valuesOnRhsB) return false;
|
|
|
|
|
return getStrength(ap, value) < getStrength(bp, value);
|
|
|
|
|
});
|
|
|
|
|
// If not all assignments have const with all bits equal to value on the RHS,
|
|
|
|
|
// std::max_element will return one of them anyway, so it has to be checked before
|
|
|
|
|
// returning
|
|
|
|
|
return assignmentOfValueOnAllBits(*maxIt, value) ? *maxIt : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
bool isAssignmentNotStrongerThanStrength(const AstAssignW* assignp, uint8_t strength) {
|
2022-09-19 10:54:20 +02:00
|
|
|
// If the value of the RHS is known and has all bits equal, only strength corresponding to
|
|
|
|
|
// its value is taken into account. In opposite case, both strengths are compared.
|
|
|
|
|
const uint8_t strength0 = getStrength(assignp, 0);
|
|
|
|
|
const uint8_t strength1 = getStrength(assignp, 1);
|
|
|
|
|
return (strength0 <= strength && strength1 <= strength)
|
|
|
|
|
|| (strength0 <= strength && assignmentOfValueOnAllBits(assignp, 0))
|
|
|
|
|
|| (strength1 <= strength && assignmentOfValueOnAllBits(assignp, 1));
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 23:53:46 +02:00
|
|
|
AstNodeExpr* newMergeExpr(AstNodeExpr* const lhsp, AstNodeExpr* const rhsp, FileLine* const fl,
|
|
|
|
|
bool isWor) {
|
|
|
|
|
AstNodeExpr* expr = nullptr;
|
|
|
|
|
if (isWor)
|
|
|
|
|
expr = new AstOr{fl, lhsp, rhsp};
|
|
|
|
|
else
|
|
|
|
|
expr = new AstAnd{fl, lhsp, rhsp};
|
|
|
|
|
return expr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mergeWiredNetsAssignments() {
|
|
|
|
|
// Support for WOR/TRIOR/WAND/TRIAND, by merging the Assignments for the
|
|
|
|
|
// same Net (merge by or for WOR/TIOR and merge by and for WAND/TRIAND).
|
|
|
|
|
for (auto& varpAssigns : m_assigns) {
|
|
|
|
|
Assigns& assigns = varpAssigns.second;
|
|
|
|
|
if (assigns.size() > 1) {
|
|
|
|
|
AstVar* varp = varpAssigns.first;
|
|
|
|
|
if (varp->isWiredNet()) {
|
|
|
|
|
auto it = assigns.begin();
|
|
|
|
|
AstAssignW* const assignWp0 = *it;
|
|
|
|
|
FileLine* const fl = assignWp0->fileline();
|
|
|
|
|
AstNodeExpr* wExp = nullptr;
|
|
|
|
|
while (++it != assigns.end()) {
|
|
|
|
|
AstAssignW* assignWpi = *it;
|
|
|
|
|
if (!wExp) {
|
|
|
|
|
wExp = newMergeExpr(assignWp0->rhsp()->cloneTreePure(false),
|
|
|
|
|
assignWpi->rhsp()->cloneTreePure(false), fl,
|
|
|
|
|
varp->isWor());
|
|
|
|
|
} else {
|
|
|
|
|
wExp = newMergeExpr(wExp, assignWpi->rhsp()->cloneTreePure(false), fl,
|
|
|
|
|
varp->isWor());
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING((assignWpi->unlinkFrBack()->deleteTree()), assignWpi);
|
|
|
|
|
}
|
|
|
|
|
AstVarRef* const wVarRef = new AstVarRef{fl, varp, VAccess::WRITE};
|
|
|
|
|
AstAssignW* const wAssignp = new AstAssignW{fl, wVarRef, wExp};
|
|
|
|
|
assignWp0->replaceWith(wAssignp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(assignWp0), assignWp0);
|
|
|
|
|
assigns.clear();
|
|
|
|
|
assigns.push_back(wAssignp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
void removeNotStrongerAssignments(Assigns& assigns, const AstAssignW* strongestp,
|
2022-09-19 10:54:20 +02:00
|
|
|
uint8_t greatestKnownStrength) {
|
2022-09-14 13:39:27 +02:00
|
|
|
// Weaker assignments are these assignments that can't change the final value of the net.
|
2022-09-19 10:54:20 +02:00
|
|
|
// They can be safely removed. Assignments of the same strength are also removed, because
|
|
|
|
|
// duplicates aren't needed. One problem is with 2 assignments of different values and
|
|
|
|
|
// equal strengths. It should result in assignment of x value, but these values aren't
|
|
|
|
|
// supported now.
|
|
|
|
|
auto removedIt = std::remove_if(assigns.begin(), assigns.end(), [&](AstAssignW* assignp) {
|
|
|
|
|
if (assignp == strongestp) return false;
|
|
|
|
|
if (isAssignmentNotStrongerThanStrength(assignp, greatestKnownStrength)) {
|
|
|
|
|
// Vertices corresponding to nodes from removed assignment's subtree have to be
|
|
|
|
|
// removed.
|
|
|
|
|
m_tgraph.deleteVerticesFromSubtreeRecurse(assignp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(assignp->unlinkFrBack()), assignp);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
assigns.erase(removedIt, assigns.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void removeAssignmentsNotStrongerThanUniformConstant() {
|
|
|
|
|
// If a stronger assignment of a constant with all bits equal to the same
|
|
|
|
|
// value (0 or 1), is found, all weaker assignments can be safely removed.
|
|
|
|
|
for (auto& varpAssigns : m_assigns) {
|
|
|
|
|
Assigns& assigns = varpAssigns.second;
|
|
|
|
|
if (assigns.size() > 1) {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
const AstAssignW* const strongest0p = getStrongestAssignmentOfValue(assigns, 0);
|
|
|
|
|
const AstAssignW* const strongest1p = getStrongestAssignmentOfValue(assigns, 1);
|
|
|
|
|
const AstAssignW* strongestp = nullptr;
|
2022-09-19 10:54:20 +02:00
|
|
|
uint8_t greatestKnownStrength = 0;
|
|
|
|
|
const auto getIfStrongest
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
= [&](const AstAssignW* const strongestCandidatep, bool value) {
|
2022-09-19 10:54:20 +02:00
|
|
|
if (!strongestCandidatep) return;
|
|
|
|
|
uint8_t strength = getStrength(strongestCandidatep, value);
|
|
|
|
|
if (strength >= greatestKnownStrength) {
|
|
|
|
|
greatestKnownStrength = strength;
|
|
|
|
|
strongestp = strongestCandidatep;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
getIfStrongest(strongest0p, 0);
|
|
|
|
|
getIfStrongest(strongest1p, 1);
|
|
|
|
|
|
|
|
|
|
if (strongestp) {
|
|
|
|
|
removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength);
|
|
|
|
|
}
|
2022-09-14 13:39:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-19 10:54:20 +02:00
|
|
|
void removeAssignmentsNotStrongerThanNonTristate() {
|
|
|
|
|
// Similar function as removeAssignmentsNotStrongerThanUniformConstant, but here the
|
|
|
|
|
// assignments that have strength not stronger than the strongest assignment with
|
|
|
|
|
// non-tristate RHS are removed. Strengths are compared according to their smaller values,
|
|
|
|
|
// because the values of RHSs are unknown. (Assignments not stronger than strongest
|
|
|
|
|
// constant are already removed.)
|
2022-09-14 13:39:27 +02:00
|
|
|
for (auto& varpAssigns : m_assigns) {
|
2022-09-19 10:54:20 +02:00
|
|
|
Assigns& assigns = varpAssigns.second;
|
|
|
|
|
if (assigns.size() > 1) {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
auto maxIt
|
|
|
|
|
= std::max_element(assigns.begin(), assigns.end(),
|
|
|
|
|
[&](const AstAssignW* ap, const AstAssignW* bp) {
|
|
|
|
|
if (m_tgraph.isTristate(ap))
|
|
|
|
|
return !m_tgraph.isTristate(bp);
|
|
|
|
|
if (m_tgraph.isTristate(bp)) return false;
|
|
|
|
|
const uint8_t minStrengthA
|
|
|
|
|
= std::min(getStrength(ap, 0), getStrength(ap, 1));
|
|
|
|
|
const uint8_t minStrengthB
|
|
|
|
|
= std::min(getStrength(bp, 0), getStrength(bp, 1));
|
|
|
|
|
return minStrengthA < minStrengthB;
|
|
|
|
|
});
|
2022-09-19 10:54:20 +02:00
|
|
|
// If RHSs of all assignments are tristate, 1st element is returned, so it is
|
|
|
|
|
// needed to check if it is non-tristate.
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
const AstAssignW* const strongestp
|
|
|
|
|
= m_tgraph.isTristate(*maxIt) ? nullptr : *maxIt;
|
2022-09-19 10:54:20 +02:00
|
|
|
if (strongestp) {
|
|
|
|
|
uint8_t greatestKnownStrength
|
|
|
|
|
= std::min(getStrength(strongestp, 0), getStrength(strongestp, 1));
|
|
|
|
|
removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength);
|
|
|
|
|
}
|
2022-09-14 13:39:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-22 03:45:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_alhs && nodep->num().hasZ()) m_tgraph.setTristate(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// Detect any Z consts and convert them to 0's with an enable that is also 0.
|
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
|
|
|
|
// A pin with 1'b0 or similar connection results in an assign with constant on LHS
|
|
|
|
|
// due to the pinReconnectSimple call in visit AstPin.
|
|
|
|
|
// We can ignore the output override by making a temporary
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const varp = getCreateUnconnVarp(nodep, nodep->dtypep());
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::WRITE};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " const->" << newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_tgraph.isTristate(nodep)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2022-10-01 16:34:30 +02:00
|
|
|
AstConst* const enp = getNonZConstp(nodep);
|
|
|
|
|
V3Number num1{nodep, nodep->width()};
|
|
|
|
|
num1.opAnd(nodep->num(), enp->num()); // 01X->01X, Z->0
|
|
|
|
|
AstConst* const newconstp = new AstConst{fl, num1};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newconstp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
newconstp->user1p(enp); // Propagate up constant with non-Z bits as 1
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCond* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) {
|
2022-09-15 20:43:56 +02:00
|
|
|
associateLogic(nodep, nodep->thenp());
|
|
|
|
|
associateLogic(nodep, nodep->elsep());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-09-15 20:43:56 +02:00
|
|
|
associateLogic(nodep->thenp(), nodep);
|
|
|
|
|
associateLogic(nodep->elsep(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Generate the new output enable signal for this cond if either
|
|
|
|
|
// expression 1 or 2 have an output enable '__en' signal. If the
|
|
|
|
|
// condition has an enable, not sure what to do, so generate an
|
|
|
|
|
// error.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp = nodep->condp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (condp->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
condp->v3warn(E_UNSUPPORTED, "Unsupported: don't know how to deal with "
|
|
|
|
|
"tristate logic in the conditional expression");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const thenp = nodep->thenp();
|
|
|
|
|
AstNodeExpr* const elsep = nodep->elsep();
|
2022-09-15 20:43:56 +02:00
|
|
|
if (thenp->user1p() || elsep->user1p()) { // else no tristates
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const en1p = getEnp(thenp);
|
|
|
|
|
AstNodeExpr* const en2p = getEnp(elsep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// The output enable of a cond is a cond of the output enable of the
|
|
|
|
|
// two expressions with the same conditional.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const enp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstCond{nodep->fileline(), condp->cloneTree(false), en1p, en2p};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newcond " << enp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1p(enp); // propagate up COND(lhsp->enable, rhsp->enable)
|
2025-09-16 20:22:36 +02:00
|
|
|
if (thenp->user1p() && !thenp->user1p()->backp()) pushDeletep(thenp->user1p());
|
|
|
|
|
if (elsep->user1p() && !elsep->user1p()->backp()) pushDeletep(elsep->user1p());
|
2022-09-15 20:43:56 +02:00
|
|
|
thenp->user1p(nullptr);
|
|
|
|
|
elsep->user1p(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-19 15:35:35 +02:00
|
|
|
void visit(AstExprStmt* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (m_graphing) {
|
|
|
|
|
UASSERT_OBJ(!m_alhs, nodep, "AstExprStmt node on the LHS of assignment");
|
|
|
|
|
associateLogic(nodep->resultp(), nodep);
|
|
|
|
|
} else if (nodep->resultp()->user1p()) {
|
|
|
|
|
nodep->user1p(getEnp(nodep->resultp()));
|
|
|
|
|
nodep->resultp()->user1p(nullptr);
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSel* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) {
|
|
|
|
|
associateLogic(nodep, nodep->fromp());
|
|
|
|
|
} else {
|
|
|
|
|
associateLogic(nodep->fromp(), nodep);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (m_alhs) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->user1p()) {
|
|
|
|
|
// Form a "deposit" instruction. Would be nicer if we made this a new AST type
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const newp
|
|
|
|
|
= newEnableDeposit(nodep, VN_AS(nodep->user1p(), NodeExpr));
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->fromp()->user1p(newp); // Push to varref (etc)
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "assign-sel");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->lsbp()->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported RHS tristate construct: "
|
|
|
|
|
<< nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (nodep->fromp()->user1p()) { // SEL(VARREF, lsb)
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const en1p = getEnp(nodep->fromp());
|
|
|
|
|
AstNodeExpr* const enp
|
2023-09-17 04:50:54 +02:00
|
|
|
= new AstSel{nodep->fileline(), en1p, nodep->lsbp()->cloneTreePure(true),
|
2025-06-24 17:59:09 +02:00
|
|
|
nodep->widthConst()};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newsel " << enp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1p(enp); // propagate up SEL(fromp->enable, value)
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConcat* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) {
|
|
|
|
|
associateLogic(nodep, nodep->lhsp());
|
|
|
|
|
associateLogic(nodep, nodep->rhsp());
|
|
|
|
|
} else {
|
|
|
|
|
associateLogic(nodep->lhsp(), nodep);
|
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->user1p()) {
|
|
|
|
|
// Each half of the concat gets a select of the enable expression
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const enp = VN_AS(nodep->user1p(), NodeExpr);
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep->user1p(nullptr);
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->lhsp()->user1p(new AstSel{nodep->fileline(), enp->cloneTreePure(true),
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->rhsp()->width(),
|
2022-11-20 23:40:38 +01:00
|
|
|
nodep->lhsp()->width()});
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->rhsp()->user1p(
|
2022-11-20 23:40:38 +01:00
|
|
|
new AstSel{nodep->fileline(), enp, 0, nodep->rhsp()->width()});
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Generate the new output enable signal, just as a concat
|
|
|
|
|
// identical to the data concat
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const expr1p = nodep->lhsp();
|
|
|
|
|
AstNodeExpr* const expr2p = nodep->rhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const en1p = getEnp(expr1p);
|
|
|
|
|
AstNodeExpr* const en2p = getEnp(expr2p);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const enp = new AstConcat{nodep->fileline(), en1p, en2p};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newconc " << enp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1p(enp); // propagate up CONCAT(lhsp->enable, rhsp->enable)
|
2020-08-15 16:12:55 +02:00
|
|
|
expr1p->user1p(nullptr);
|
|
|
|
|
expr2p->user1p(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBufIf1* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// For BufIf1, the enable is the LHS expression
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
|
m_tgraph.setTristate(nodep);
|
|
|
|
|
} else {
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep->backp(), "", "bufif");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_alhs) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const expr1p = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const expr2p = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* enp;
|
|
|
|
|
if (AstNodeExpr* const en2p = VN_AS(expr2p->user1p(), NodeExpr)) {
|
2022-11-20 23:40:38 +01:00
|
|
|
enp = new AstAnd{nodep->fileline(), expr1p, en2p};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
enp = expr1p;
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
expr1p->user1p(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
expr2p->user1p(enp); // Becomes new node
|
|
|
|
|
// Don't need the BufIf any more, can just have the data direct
|
|
|
|
|
nodep->replaceWith(expr2p);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " bufif datap=" << expr2p);
|
|
|
|
|
UINFO(9, " bufif enp=" << enp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2012-05-05 21:04:33 +02:00
|
|
|
void visitAndOr(AstNodeBiop* nodep, bool isAnd) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
|
|
|
|
associateLogic(nodep->lhsp(), nodep);
|
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
|
} else {
|
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// ANDs and Z's have issues. Earlier optimizations convert
|
|
|
|
|
// expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we
|
|
|
|
|
// have to define what is means to AND 1'bz with other
|
|
|
|
|
// expressions. I don't think this is spec, but here I take the
|
|
|
|
|
// approach that when one expression is 1, that the Z passes. This
|
|
|
|
|
// makes the COND's work. It is probably better to not perform the
|
|
|
|
|
// conditional optimization if the bits are Z.
|
|
|
|
|
//
|
|
|
|
|
// ORs have the same issues as ANDs. Earlier optimizations convert
|
|
|
|
|
// expressions like "(COND) ? 1'bz : 1'b1" to "COND | 1'bz". So we
|
|
|
|
|
// have to define what is means to OR 1'bz with other
|
|
|
|
|
// expressions. Here I take the approach that when one expression
|
|
|
|
|
// is 0, that is passes the other.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const expr1p = nodep->lhsp();
|
|
|
|
|
AstNodeExpr* const expr2p = nodep->rhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!expr1p->user1p() && !expr2p->user1p()) {
|
|
|
|
|
return; // no tristates in either expression, so nothing to do
|
|
|
|
|
}
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const en1p = getEnp(expr1p);
|
|
|
|
|
AstNodeExpr* const en2p = getEnp(expr2p);
|
2023-09-17 04:50:54 +02:00
|
|
|
AstNodeExpr* subexpr1p = expr1p->cloneTreePure(false);
|
|
|
|
|
AstNodeExpr* subexpr2p = expr2p->cloneTreePure(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (isAnd) {
|
2022-11-20 23:40:38 +01:00
|
|
|
subexpr1p = new AstNot{nodep->fileline(), subexpr1p};
|
|
|
|
|
subexpr2p = new AstNot{nodep->fileline(), subexpr2p};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// calc new output enable
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const enp = new AstOr{
|
|
|
|
|
nodep->fileline(), new AstAnd{nodep->fileline(), en1p, en2p},
|
|
|
|
|
new AstOr{nodep->fileline(),
|
|
|
|
|
new AstAnd{nodep->fileline(), en1p->cloneTree(false), subexpr1p},
|
|
|
|
|
new AstAnd{nodep->fileline(), en2p->cloneTree(false), subexpr2p}}};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " neweqn " << enp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1p(enp);
|
2020-08-15 16:12:55 +02:00
|
|
|
expr1p->user1p(nullptr);
|
|
|
|
|
expr2p->user1p(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAnd* nodep) override { visitAndOr(nodep, true); }
|
|
|
|
|
void visit(AstOr* nodep) override { visitAndOr(nodep, false); }
|
2011-02-24 03:21:59 +01:00
|
|
|
|
2012-04-22 03:45:28 +02:00
|
|
|
void visitAssign(AstNodeAssign* nodep) {
|
2022-11-19 21:23:37 +01:00
|
|
|
VL_RESTORER(m_alhs);
|
|
|
|
|
VL_RESTORER(m_currentStrength);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2024-09-07 00:13:52 +02:00
|
|
|
if (AstAssignW* assignWp = VN_CAST(nodep, AssignW)) {
|
|
|
|
|
if (assignWp->timingControlp() || assignWp->getLhsNetDelay()) return;
|
|
|
|
|
addToAssignmentList(assignWp);
|
|
|
|
|
}
|
2022-09-14 13:39:27 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->user2() & U2_GRAPHING) return;
|
2021-12-19 22:06:45 +01:00
|
|
|
VL_RESTORER(m_logicp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_logicp = nodep;
|
2021-12-19 22:06:45 +01:00
|
|
|
nodep->user2(U2_GRAPHING);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
m_alhs = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
m_alhs = false;
|
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
|
associateLogic(nodep, nodep->lhsp());
|
|
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->user2() & U2_NONGRAPH) {
|
|
|
|
|
return; // Iterated here, or created assignment to ignore
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user2(U2_NONGRAPH);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "assign");
|
2019-05-19 22:13:13 +02:00
|
|
|
// if the rhsp of this assign statement has an output enable driver,
|
2019-09-09 13:50:21 +02:00
|
|
|
// then propagate the corresponding output enable assign statement.
|
2019-05-19 22:13:13 +02:00
|
|
|
// down the lvalue tree by recursion for eventual attachment to
|
|
|
|
|
// the appropriate output signal's VarRef.
|
|
|
|
|
if (nodep->rhsp()->user1p()) {
|
|
|
|
|
nodep->lhsp()->user1p(nodep->rhsp()->user1p());
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep->rhsp()->user1p(nullptr);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " enp<-rhs " << nodep->lhsp()->user1p());
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
|
|
|
|
m_alhs = true; // And user1p() will indicate tristate equation, if any
|
2022-09-27 03:21:37 +02:00
|
|
|
if (AstAssignW* const assignWp = VN_CAST(nodep, AssignW)) {
|
|
|
|
|
if (AstStrengthSpec* const specp = assignWp->strengthSpecp()) {
|
|
|
|
|
if (specp->strength0() != specp->strength1()) {
|
|
|
|
|
// Unequal strengths are not a problem if the assignment is the only
|
|
|
|
|
// assignment to its variable. Unfortunately, m_assigns map stores only
|
|
|
|
|
// assignments to var. Selects are not inserted, so they may be handled
|
|
|
|
|
// improperly
|
|
|
|
|
if (!isOnlyAssignmentIsToLhsVar(assignWp)) {
|
|
|
|
|
assignWp->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Unable to resolve unequal strength specifier");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
m_currentStrength = specp->strength0();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignW* nodep) override { visitAssign(nodep); }
|
|
|
|
|
void visit(AstAssign* nodep) override { visitAssign(nodep); }
|
2025-09-26 15:19:48 +02:00
|
|
|
void visit(AstAlias* nodep) override {
|
|
|
|
|
VL_RESTORER(m_alhs);
|
|
|
|
|
VL_RESTORER(m_inAlias);
|
|
|
|
|
m_inAlias = true;
|
|
|
|
|
if (m_graphing) {
|
|
|
|
|
if (nodep->user2() & U2_GRAPHING) return;
|
2025-09-29 19:23:51 +02:00
|
|
|
m_alhs = true; // In AstAlias all operands should be considered as lhs
|
2025-09-26 15:19:48 +02:00
|
|
|
iterateChildren(nodep);
|
2025-09-29 19:23:51 +02:00
|
|
|
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
|
|
|
|
associateLogic(itemp, nodep);
|
|
|
|
|
associateLogic(nodep, itemp);
|
|
|
|
|
}
|
2025-09-26 15:19:48 +02:00
|
|
|
} else {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
|
|
|
|
|
void visitCaseEq(AstNodeBiop* nodep, bool neq) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
checkUnhandled(nodep);
|
|
|
|
|
// Unsupported: A === 3'b000 should compare with the enables, but we don't do
|
|
|
|
|
// so at present, we only compare if there is a z in the equation.
|
|
|
|
|
// Otherwise we'd need to attach an enable to every signal, then optimize them
|
|
|
|
|
// away later when we determine the signal has no tristate
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
// Constification always moves const to LHS
|
2022-08-18 13:03:05 +02:00
|
|
|
AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
|
2022-10-01 16:34:30 +02:00
|
|
|
if (constp && constp->user1p()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in))
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp();
|
2022-10-01 16:34:30 +02:00
|
|
|
rhsp->unlinkFrBack();
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* enRhsp;
|
2022-10-01 16:34:30 +02:00
|
|
|
if (rhsp->user1p()) {
|
2022-11-13 21:33:11 +01:00
|
|
|
enRhsp = VN_AS(rhsp->user1p(), NodeExpr);
|
2022-10-01 16:34:30 +02:00
|
|
|
rhsp->user1p(nullptr);
|
|
|
|
|
} else {
|
|
|
|
|
enRhsp = getEnExprBasedOnOriginalp(rhsp);
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newp
|
2025-09-09 23:39:44 +02:00
|
|
|
= new AstLogAnd{fl, new AstEq{fl, VN_AS(constp->user1p(), Const), enRhsp},
|
2020-09-07 23:09:25 +02:00
|
|
|
// Keep the caseeq if there are X's present
|
2025-09-09 23:39:44 +02:00
|
|
|
new AstEqCase{fl, new AstConst{fl, constp->num()}, rhsp}};
|
|
|
|
|
constp->user1p(nullptr);
|
2022-10-01 16:34:30 +02:00
|
|
|
if (neq) newp = new AstLogNot{fl, newp};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newceq " << newp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "caseeq-old");
|
|
|
|
|
UINFOTREE(9, newp, "", "caseeq-new");
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-08-18 13:03:05 +02:00
|
|
|
} else if (constp && nodep->rhsp()->user1p()) {
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
constp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* newp = new AstLogAnd{fl,
|
|
|
|
|
new AstEq{fl, newAllZerosOrOnes(constp, false),
|
|
|
|
|
VN_AS(rhsp->user1p(), NodeExpr)},
|
|
|
|
|
// Keep the caseeq if there are X's present
|
|
|
|
|
new AstEqCase{fl, constp, rhsp}};
|
2022-08-18 13:03:05 +02:00
|
|
|
if (neq) newp = new AstLogNot{fl, newp};
|
|
|
|
|
rhsp->user1p(nullptr);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newceq " << newp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "caseeq-old");
|
|
|
|
|
UINFOTREE(9, newp, "", "caseeq-new");
|
2022-08-18 13:03:05 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
checkUnhandled(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
2014-03-08 19:33:44 +01:00
|
|
|
void visitEqNeqWild(AstNodeBiop* nodep) {
|
2024-12-12 14:51:48 +01:00
|
|
|
if (!VN_IS(nodep->rhsp(), Const) && nodep->rhsp()->dtypep()->isFourstate()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: RHS of ==? or !=? is fourstate but not a constant");
|
2019-05-19 22:13:13 +02:00
|
|
|
// rhs we want to keep X/Z intact, so otherwise ignore
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->lhsp()->user1p()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2014-03-08 19:33:44 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEqCase* nodep) override { visitCaseEq(nodep, false); }
|
|
|
|
|
void visit(AstNeqCase* nodep) override { visitCaseEq(nodep, true); }
|
|
|
|
|
void visit(AstEqWild* nodep) override { visitEqNeqWild(nodep); }
|
|
|
|
|
void visit(AstNeqWild* nodep) override { visitEqNeqWild(nodep); }
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCountBits* nodep) override {
|
2020-11-28 03:34:40 +01:00
|
|
|
std::array<bool, 3> dropop;
|
2021-10-22 14:56:48 +02:00
|
|
|
dropop[0] = VN_IS(nodep->rhsp(), Const) && VN_AS(nodep->rhsp(), Const)->num().isAnyZ();
|
|
|
|
|
dropop[1] = VN_IS(nodep->thsp(), Const) && VN_AS(nodep->thsp(), Const)->num().isAnyZ();
|
|
|
|
|
dropop[2] = VN_IS(nodep->fhsp(), Const) && VN_AS(nodep->fhsp(), Const)->num().isAnyZ();
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " COUNTBITS(" << dropop[0] << dropop[1] << dropop[2] << " " << nodep);
|
2020-11-28 03:34:40 +01:00
|
|
|
if (m_graphing) {
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
if (!dropop[0]) iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
if (!dropop[1]) iterateAndNextNull(nodep->thsp());
|
|
|
|
|
if (!dropop[2]) iterateAndNextNull(nodep->fhsp());
|
|
|
|
|
} else {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* nonXp = nullptr;
|
2022-07-30 16:01:25 +02:00
|
|
|
if (!dropop[0]) {
|
2020-11-28 03:34:40 +01:00
|
|
|
nonXp = nodep->rhsp();
|
2022-07-30 16:01:25 +02:00
|
|
|
} else if (!dropop[1]) {
|
2020-11-28 03:34:40 +01:00
|
|
|
nonXp = nodep->thsp();
|
2022-07-30 16:01:25 +02:00
|
|
|
} else if (!dropop[2]) {
|
2020-11-28 03:34:40 +01:00
|
|
|
nonXp = nodep->fhsp();
|
2022-07-30 16:01:25 +02:00
|
|
|
}
|
2020-11-28 03:34:40 +01:00
|
|
|
// Replace 'z with non-Z
|
|
|
|
|
if (dropop[0] || dropop[1] || dropop[2]) {
|
|
|
|
|
// Unsupported: A $countones('0) should compare with the enables, but we don't
|
|
|
|
|
// do so at present, we only compare if there is a z in the equation. Otherwise
|
|
|
|
|
// we'd need to attach an enable to every signal, then optimize them away later
|
|
|
|
|
// when we determine the signal has no tristate
|
2024-10-27 14:30:54 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef); // Input variable
|
|
|
|
|
if (!varrefp) {
|
2020-11-28 03:34:40 +01:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported LHS tristate construct: "
|
|
|
|
|
<< nodep->prettyTypeName());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* const envarp = getCreateEnVarp(varrefp->varp(), false);
|
2020-11-28 03:34:40 +01:00
|
|
|
// If any drops, we need to add in the count of Zs (from __en)
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " COUNTBITS('z)-> " << nodep);
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker relinkHandle;
|
2020-11-28 03:34:40 +01:00
|
|
|
nodep->unlinkFrBack(&relinkHandle);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* newp = new AstCountOnes{
|
|
|
|
|
nodep->fileline(), new AstVarRef{nodep->fileline(), envarp, VAccess::READ}};
|
2020-11-28 03:34:40 +01:00
|
|
|
if (nonXp) { // Need to still count '0 or '1 or 'x's
|
|
|
|
|
if (dropop[0]) {
|
|
|
|
|
nodep->rhsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->rhsp(nonXp->cloneTreePure(true));
|
2020-11-28 03:34:40 +01:00
|
|
|
}
|
|
|
|
|
if (dropop[1]) {
|
|
|
|
|
nodep->thsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->thsp(nonXp->cloneTreePure(true));
|
2020-11-28 03:34:40 +01:00
|
|
|
}
|
|
|
|
|
if (dropop[2]) {
|
|
|
|
|
nodep->fhsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->fhsp(nonXp->cloneTreePure(true));
|
2020-11-28 03:34:40 +01:00
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstAdd{nodep->fileline(), nodep, newp};
|
2025-09-11 13:01:36 +02:00
|
|
|
} else {
|
|
|
|
|
// TODO: looks dubious that we still iterate this below...
|
|
|
|
|
pushDeletep(nodep);
|
2020-11-28 03:34:40 +01:00
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "countout");
|
2020-11-28 03:34:40 +01:00
|
|
|
relinkHandle.relink(newp);
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPull* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVarRef* varrefp = nullptr;
|
2018-02-27 13:24:31 +01:00
|
|
|
if (VN_IS(nodep->lhsp(), VarRef)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
varrefp = VN_AS(nodep->lhsp(), VarRef);
|
2018-02-27 13:24:31 +01:00
|
|
|
} else if (VN_IS(nodep->lhsp(), Sel)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_IS(VN_AS(nodep->lhsp(), Sel)->fromp(), VarRef)) {
|
|
|
|
|
varrefp = VN_AS(VN_AS(nodep->lhsp(), Sel)->fromp(), VarRef);
|
2018-02-11 01:08:07 +01:00
|
|
|
}
|
|
|
|
|
if (!varrefp) {
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(4, nodep, "", "");
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported pullup/down (weak driver) construct.");
|
2018-02-11 01:08:07 +01:00
|
|
|
} else {
|
|
|
|
|
if (m_graphing) {
|
2021-12-19 22:06:45 +01:00
|
|
|
VL_RESTORER(m_logicp);
|
2018-02-11 01:08:07 +01:00
|
|
|
m_logicp = nodep;
|
2021-12-19 22:06:45 +01:00
|
|
|
varrefp->access(VAccess::WRITE);
|
2018-02-11 01:08:07 +01:00
|
|
|
m_tgraph.setTristate(nodep);
|
|
|
|
|
associateLogic(nodep, varrefp->varp());
|
|
|
|
|
} else {
|
|
|
|
|
// Replace any pullup/pulldowns with assignw logic and set the
|
2023-10-29 02:12:27 +02:00
|
|
|
// direction of the pull in the var aux data. Given
|
2018-02-11 01:08:07 +01:00
|
|
|
// the complexity of merging tristate drivers at any level, the
|
|
|
|
|
// current limitation of this implementation is that a pullup/down
|
|
|
|
|
// gets applied to all bits of a bus and a bus cannot have drivers
|
2019-09-09 13:50:21 +02:00
|
|
|
// in opposite directions on individual pins.
|
2020-09-07 23:09:25 +02:00
|
|
|
varrefp->access(VAccess::WRITE);
|
2018-02-11 01:08:07 +01:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
m_tgraph.didProcess(varrefp->varp());
|
|
|
|
|
setPullDirection(varrefp->varp(), nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!m_graphing) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
2023-10-29 02:12:27 +02:00
|
|
|
// Node must persist as var aux data points to it
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2012-05-09 03:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void iteratePinGuts(AstPin* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2021-12-19 22:06:45 +01:00
|
|
|
VL_RESTORER(m_logicp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_logicp = nodep;
|
|
|
|
|
if (nodep->exprp()) {
|
|
|
|
|
associateLogic(nodep->exprp(), nodep);
|
|
|
|
|
associateLogic(nodep, nodep->exprp());
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// All heavy lifting completed in graph visitor.
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->exprp()) m_tgraph.didProcess(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2011-02-24 03:21:59 +01:00
|
|
|
|
2012-04-27 03:11:48 +02:00
|
|
|
// .tri(SEL(trisig,x)) becomes
|
|
|
|
|
// INPUT: -> (VARREF(trisig__pinin)),
|
|
|
|
|
// trisig__pinin = SEL(trisig,x) // via pinReconnectSimple
|
|
|
|
|
// OUTPUT: -> (VARREF(trisig__pinout))
|
2012-05-08 05:42:58 +02:00
|
|
|
// SEL(trisig,x) = trisig__pinout
|
2019-05-19 22:13:13 +02:00
|
|
|
// ^-- ->user1p() == trisig__pinen
|
2012-04-27 03:11:48 +02:00
|
|
|
// ENABLE: -> (VARREF(trisig__pinen)
|
2019-05-19 22:13:13 +02:00
|
|
|
// Added complication is the signal may be an output/inout or just
|
|
|
|
|
// input with tie off (or not) up top
|
|
|
|
|
// PIN PORT NEW PORTS AND CONNECTIONS
|
|
|
|
|
// N/C input in(from-resolver), __en(to-resolver-only), __out(to-resolver-only)
|
|
|
|
|
// N/C inout Spec says illegal
|
|
|
|
|
// N/C output Unsupported; Illegal?
|
|
|
|
|
// wire input in(from-resolver-with-wire-value), __en(from-resolver-wire),
|
|
|
|
|
// __out(to-resolver-only)
|
|
|
|
|
// wire inout in, __en, __out
|
|
|
|
|
// wire output in, __en, __out
|
|
|
|
|
// const input in(from-resolver-with-const-value), __en(from-resolver-const),
|
|
|
|
|
// __out(to-resolver-only)
|
|
|
|
|
// const inout Spec says illegal
|
|
|
|
|
// const output Unsupported; Illegal?
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPin* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
|
|
|
|
if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded
|
|
|
|
|
nodep->user2(U2_GRAPHING);
|
|
|
|
|
// Find child module's new variables.
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const enModVarp = static_cast<AstVar*>(nodep->modVarp()->user1p());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!enModVarp) {
|
2020-04-10 05:26:03 +02:00
|
|
|
// May have an output only that later connects to a tristate, so simplify now.
|
|
|
|
|
V3Inst::pinReconnectSimple(nodep, m_cellp, false);
|
2019-05-19 22:13:13 +02:00
|
|
|
iteratePinGuts(nodep);
|
|
|
|
|
return; // No __en signals on this pin
|
|
|
|
|
}
|
|
|
|
|
// Tristate exists:
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "pin-pre");
|
2012-04-22 03:45:28 +02:00
|
|
|
|
2018-10-27 23:29:00 +02:00
|
|
|
// Empty/in-only; need Z to propagate
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool inDeclProcessing = (nodep->exprp()
|
|
|
|
|
&& nodep->modVarp()->direction() == VDirection::INPUT
|
|
|
|
|
// Need to consider the original state
|
|
|
|
|
// instead of current state as we converted
|
|
|
|
|
// tristates to inputs, which do not want
|
|
|
|
|
// to have this.
|
|
|
|
|
&& !nodep->modVarp()->declDirection().isWritable());
|
2018-10-27 23:29:00 +02:00
|
|
|
if (!nodep->exprp()) { // No-connect; covert to empty connection
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "Unconnected pin terminate " << nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const ucVarp = getCreateUnconnVarp(nodep, nodep->modVarp()->dtypep());
|
2022-11-20 23:40:38 +01:00
|
|
|
nodep->exprp(new AstVarRef{nodep->fileline(), ucVarp,
|
2018-10-27 23:29:00 +02:00
|
|
|
// We converted, so use declaration output state
|
2020-09-07 23:09:25 +02:00
|
|
|
nodep->modVarp()->declDirection().isWritable()
|
|
|
|
|
? VAccess::WRITE
|
2022-11-20 23:40:38 +01:00
|
|
|
: VAccess::READ});
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.setTristate(ucVarp);
|
|
|
|
|
// We don't need a driver on the wire; the lack of one will default to tristate
|
|
|
|
|
} else if (inDeclProcessing) { // Not an input that was a converted tristate
|
|
|
|
|
// Input only may have driver in underneath module which would stomp
|
|
|
|
|
// the input value. So make a temporary connection.
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstAssignW* const reAssignp
|
2021-11-13 19:50:44 +01:00
|
|
|
= V3Inst::pinReconnectSimple(nodep, m_cellp, true, true);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "Input pin buffering: " << reAssignp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.setTristate(reAssignp->lhsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pinReconnectSimple needs to presume input or output behavior; we need both
|
|
|
|
|
// Therefore, create the enable, output and separate input pin,
|
|
|
|
|
// then pinReconnectSimple all
|
|
|
|
|
// Create the output enable pin, connect to new signal
|
2025-09-09 23:39:44 +02:00
|
|
|
AstVar* const enVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
nodep->name() + "__en" + cvtToStr(m_unique++),
|
|
|
|
|
VFlagBitPacked{}, enModVarp->width()};
|
|
|
|
|
if (inDeclProcessing) { // __en(from-resolver-const) or __en(from-resolver-wire)
|
|
|
|
|
enModVarp->varType2In();
|
|
|
|
|
} else {
|
|
|
|
|
enModVarp->varType2Out();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-09-09 23:39:44 +02:00
|
|
|
UINFO(9, " newenv " << enVarp);
|
|
|
|
|
AstPin* const enpinp
|
|
|
|
|
= new AstPin{nodep->fileline(), nodep->pinNum(),
|
|
|
|
|
enModVarp->name(), // should be {var}"__en"
|
|
|
|
|
new AstVarRef{nodep->fileline(), enVarp, VAccess::WRITE}};
|
|
|
|
|
enpinp->modVarp(enModVarp);
|
|
|
|
|
UINFO(9, " newpin " << enpinp);
|
|
|
|
|
enpinp->user2(U2_BOTH); // don't iterate the pin later
|
|
|
|
|
nodep->addNextHere(enpinp);
|
|
|
|
|
m_modp->addStmtsp(enVarp);
|
|
|
|
|
UINFOTREE(9, enpinp, "", "pin-ena");
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Create new output pin
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstAssignW* outAssignp = nullptr; // If reconnected, the related assignment
|
2020-08-15 16:12:55 +02:00
|
|
|
AstPin* outpinp = nullptr;
|
2023-10-29 02:12:27 +02:00
|
|
|
AstVar* const outModVarp = m_varAux(nodep->modVarp()).outVarp;
|
2018-01-25 04:30:30 +01:00
|
|
|
if (!outModVarp) {
|
|
|
|
|
// At top, no need for __out as might be input only. Otherwise resolvable.
|
2020-01-18 13:56:50 +01:00
|
|
|
if (!m_modp->isTop()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: tristate in top-level IO: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
2020-01-18 13:56:50 +01:00
|
|
|
}
|
2018-01-25 04:30:30 +01:00
|
|
|
} else {
|
2023-09-17 04:50:54 +02:00
|
|
|
AstNodeExpr* const outexprp = VN_AS(nodep->exprp(), NodeExpr)
|
|
|
|
|
->cloneTreePure(false); // Note has lvalue() set
|
2022-11-20 23:40:38 +01:00
|
|
|
outpinp = new AstPin{nodep->fileline(), nodep->pinNum(),
|
2019-05-19 22:13:13 +02:00
|
|
|
outModVarp->name(), // should be {var}"__out"
|
2022-11-20 23:40:38 +01:00
|
|
|
outexprp};
|
2019-05-19 22:13:13 +02:00
|
|
|
outpinp->modVarp(outModVarp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newpin " << outpinp);
|
2019-05-19 22:13:13 +02:00
|
|
|
outpinp->user2(U2_BOTH); // don't iterate the pin later
|
|
|
|
|
nodep->addNextHere(outpinp);
|
|
|
|
|
// Simplify
|
|
|
|
|
if (inDeclProcessing) { // Not an input that was a converted tristate
|
|
|
|
|
// The pin is an input, but we need an output
|
|
|
|
|
// The if() above is needed because the Visitor is
|
|
|
|
|
// simple, it will flip ArraySel's and such, but if the
|
|
|
|
|
// pin is an input the earlier reconnectSimple made it
|
|
|
|
|
// a VarRef without any ArraySel, etc
|
2021-11-26 16:52:36 +01:00
|
|
|
TristatePinVisitor{outexprp, m_tgraph, true};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, outpinp, "", "pin-opr");
|
2020-04-15 13:58:34 +02:00
|
|
|
outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp,
|
|
|
|
|
true); // Note may change outpinp->exprp()
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, outpinp, "", "pin-out");
|
|
|
|
|
UINFOTREE(9, outAssignp, "", "pin-oas");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Must still iterate the outAssignp, as need to build output equation
|
|
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Existing pin becomes an input, and we mark each resulting signal as tristate
|
2021-11-26 23:55:36 +01:00
|
|
|
const TristatePinVisitor visitor{nodep->exprp(), m_tgraph, false};
|
|
|
|
|
const AstNode* const inAssignp = V3Inst::pinReconnectSimple(
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep, m_cellp, true); // Note may change nodep->exprp()
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "pin-in:");
|
|
|
|
|
UINFOTREE(9, inAssignp, "", "pin-as:");
|
2012-05-09 03:53:22 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Connect enable to output signal
|
|
|
|
|
AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to
|
|
|
|
|
if (!outAssignp) {
|
2018-01-25 04:30:30 +01:00
|
|
|
if (!outpinp) {
|
2020-08-15 16:12:55 +02:00
|
|
|
exprrefp = nullptr; // Primary input only
|
2018-01-25 04:30:30 +01:00
|
|
|
} else {
|
|
|
|
|
// pinReconnect should have converted this
|
2018-02-02 03:32:58 +01:00
|
|
|
exprrefp = VN_CAST(outpinp->exprp(), VarRef);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!exprrefp) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported tristate port expression: "
|
|
|
|
|
<< nodep->exprp()->prettyTypeName());
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2018-01-25 04:30:30 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-01-25 04:30:30 +01:00
|
|
|
// pinReconnect should have converted this
|
2020-04-15 13:58:34 +02:00
|
|
|
exprrefp = VN_CAST(outAssignp->rhsp(),
|
|
|
|
|
VarRef); // This should be the same var as the output pin
|
|
|
|
|
if (!exprrefp) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported tristate port expression: "
|
|
|
|
|
<< nodep->exprp()->prettyTypeName());
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (exprrefp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "outref " << exprrefp);
|
2020-04-15 13:58:34 +02:00
|
|
|
// Mark as now tristated; iteration will pick it up from there
|
2025-09-09 23:39:44 +02:00
|
|
|
exprrefp->user1p(new AstVarRef{nodep->fileline(), enVarp, VAccess::READ});
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!outAssignp) {
|
|
|
|
|
mapInsertLhsVarRef(exprrefp); // insertTristates will convert
|
|
|
|
|
// // to a varref to the __out# variable
|
|
|
|
|
} // else the assignment deals with the connection
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Propagate any pullups/pulldowns upwards if necessary
|
2018-01-25 04:30:30 +01:00
|
|
|
if (exprrefp) {
|
2023-10-29 02:12:27 +02:00
|
|
|
if (AstPull* const pullp = m_varAux(nodep->modVarp()).pullp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "propagate pull on " << exprrefp);
|
2019-05-19 22:13:13 +02:00
|
|
|
setPullDirection(exprrefp->varp(), pullp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Don't need to visit the created assigns, as it was added at
|
|
|
|
|
// the end of the next links and normal iterateChild recursion
|
|
|
|
|
// will come back to them eventually.
|
|
|
|
|
// Mark the original signal as tristated
|
|
|
|
|
iteratePinGuts(nodep);
|
|
|
|
|
}
|
|
|
|
|
// Not graph building
|
|
|
|
|
else {
|
|
|
|
|
if (nodep->user2() & U2_NONGRAPH) return; // This pin is already expanded
|
|
|
|
|
nodep->user2(U2_NONGRAPH);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
iteratePinGuts(nodep);
|
|
|
|
|
}
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW()) associateLogic(nodep, nodep->varp());
|
|
|
|
|
if (nodep->access().isReadOrRW()) associateLogic(nodep->varp(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
if (nodep->user2() & U2_NONGRAPH) return; // Processed
|
|
|
|
|
nodep->user2(U2_NONGRAPH);
|
|
|
|
|
// Detect all var lhs drivers and adds them to the
|
|
|
|
|
// VarMap so that after the walk through the module we can expand
|
|
|
|
|
// any tristate logic on the driver.
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW() && m_tgraph.isTristate(nodep->varp())) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Ref-to-lvalue " << nodep);
|
2025-09-26 15:19:48 +02:00
|
|
|
if (m_inAlias) {
|
|
|
|
|
if (nodep->varp()->direction().isAny()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Port as alias argument: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Tristate variable referenced in alias: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-11-07 16:37:55 +01:00
|
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Tristate unexpected on R/W access");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
mapInsertLhsVarRef(nodep);
|
2020-11-01 22:59:23 +01:00
|
|
|
} else if (nodep->access().isReadOnly()
|
2020-04-15 13:58:34 +02:00
|
|
|
// Not already processed, nor varref from visit(AstPin) creation
|
|
|
|
|
&& !nodep->user1p()
|
|
|
|
|
// Reference to another tristate variable
|
|
|
|
|
&& m_tgraph.isTristate(nodep->varp())
|
|
|
|
|
// and in a position where it feeds upstream to another tristate
|
|
|
|
|
&& m_tgraph.feedsTri(nodep)) {
|
2019-09-09 13:50:21 +02:00
|
|
|
// Then propagate the enable from the original variable
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Ref-to-tri " << nodep);
|
2024-04-11 15:02:58 +02:00
|
|
|
AstVar* const enVarp = getCreateEnVarp(nodep->varp(), false);
|
2022-11-20 23:40:38 +01:00
|
|
|
nodep->user1p(new AstVarRef{nodep->fileline(), enVarp, VAccess::READ});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2024-03-27 23:07:14 +01:00
|
|
|
(void)m_alhs; // NOP; user1() already passed down from assignment
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, dbgState() << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_graphing) {
|
|
|
|
|
// If tri0/1 force a pullup
|
|
|
|
|
if (nodep->user2() & U2_GRAPHING) return; // Already processed
|
|
|
|
|
nodep->user2(U2_GRAPHING);
|
|
|
|
|
if (nodep->isPulldown() || nodep->isPullup()) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newp = new AstPull{
|
|
|
|
|
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
|
|
|
|
|
nodep->isPullup()};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " newpul " << newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->addNextHere(newp);
|
|
|
|
|
// We'll iterate on the new AstPull later
|
|
|
|
|
}
|
2024-11-26 00:25:36 +01:00
|
|
|
if (nodep->isInout()
|
2018-10-27 23:29:00 +02:00
|
|
|
//|| varp->isOutput()
|
|
|
|
|
// Note unconnected output only changes behavior vs. previous
|
|
|
|
|
// versions and causes outputs that don't come from anywhere to
|
|
|
|
|
// possibly create connection errors.
|
|
|
|
|
// One example of problems is this: "output z; task t; z <= {something}; endtask"
|
2020-04-15 13:58:34 +02:00
|
|
|
) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " setTristate-inout " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.setTristate(nodep);
|
|
|
|
|
}
|
|
|
|
|
} else { // !graphing
|
|
|
|
|
if (m_tgraph.isTristate(nodep)) {
|
|
|
|
|
// nodep->isPulldown() || nodep->isPullup() handled in TristateGraphVisitor
|
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, nodep);
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_graphing);
|
|
|
|
|
VL_RESTORER(m_unique);
|
|
|
|
|
VL_RESTORER(m_lhsmap);
|
2022-09-14 13:39:27 +02:00
|
|
|
VL_RESTORER(m_assigns);
|
2020-01-25 16:19:59 +01:00
|
|
|
// Not preserved, needs pointer instead: TristateGraph origTgraph = m_tgraph;
|
|
|
|
|
UASSERT_OBJ(m_tgraph.empty(), nodep, "Unsupported: NodeModule under NodeModule");
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2020-01-20 19:27:27 +01:00
|
|
|
// Clear state
|
2019-05-19 22:13:13 +02:00
|
|
|
m_graphing = false;
|
2020-01-20 19:27:27 +01:00
|
|
|
m_tgraph.clear();
|
|
|
|
|
m_unique = 0;
|
2020-08-15 16:12:55 +02:00
|
|
|
m_logicp = nullptr;
|
2020-01-20 19:27:27 +01:00
|
|
|
m_lhsmap.clear();
|
2022-09-14 13:39:27 +02:00
|
|
|
m_assigns.clear();
|
2020-01-20 19:27:27 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
// Walk the graph, finding all variables and tristate constructs
|
|
|
|
|
{
|
|
|
|
|
m_graphing = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_graphing = false;
|
|
|
|
|
}
|
2024-10-09 23:53:46 +02:00
|
|
|
// Merge the assignments for very Wired net LHS : wor, trior, wand and triand
|
|
|
|
|
mergeWiredNetsAssignments();
|
2022-09-19 10:54:20 +02:00
|
|
|
// Remove all assignments not stronger than the strongest uniform constant
|
|
|
|
|
removeAssignmentsNotStrongerThanUniformConstant();
|
2020-01-20 19:27:27 +01:00
|
|
|
// Use graph to find tristate signals
|
|
|
|
|
m_tgraph.graphWalk(nodep);
|
2022-09-19 10:54:20 +02:00
|
|
|
|
|
|
|
|
// Remove all assignments not stronger than the strongest non-tristate RHS
|
|
|
|
|
removeAssignmentsNotStrongerThanNonTristate();
|
|
|
|
|
|
2020-01-20 19:27:27 +01:00
|
|
|
// Build the LHS drivers map for this module
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Insert new logic for all tristates
|
|
|
|
|
insertTristates(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-01-25 16:19:59 +01:00
|
|
|
m_tgraph.clear(); // Recursion not supported
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2020-04-05 15:30:23 +02:00
|
|
|
// don't deal with classes
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// don't deal with functions
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCaseItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// don't deal with casez compare '???? values
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2021-12-19 22:06:45 +01:00
|
|
|
VL_RESTORER(m_cellp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_cellp = nodep;
|
|
|
|
|
m_alhs = false;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-18 00:58:53 +01:00
|
|
|
void visit(AstNetlist* nodep) override { iterateChildrenBackwardsConst(nodep); }
|
2009-01-06 17:03:57 +01:00
|
|
|
|
|
|
|
|
// Default: Just iterate
|
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
|
|
|
checkUnhandled(nodep);
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2025-09-09 23:39:44 +02:00
|
|
|
explicit TristateVisitor(AstNetlist* netlistp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tgraph.clear();
|
2025-09-09 23:39:44 +02:00
|
|
|
iterate(netlistp);
|
|
|
|
|
#ifdef VL_LEAK_CHECKS
|
|
|
|
|
// It's a bit chaotic up there
|
|
|
|
|
std::vector<AstNode*> unusedRootps;
|
|
|
|
|
netlistp->foreach([&](AstNode* nodep) {
|
|
|
|
|
AstNode* const enp = nodep->user1p();
|
|
|
|
|
if (!enp) return;
|
|
|
|
|
if (enp->backp()) return;
|
|
|
|
|
unusedRootps.emplace_back(enp);
|
|
|
|
|
});
|
|
|
|
|
for (AstNode* const nodep : unusedRootps) VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
#endif
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TristateVisitor() override {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs);
|
2012-04-22 03:45:28 +02:00
|
|
|
}
|
2009-01-06 17:03:57 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Tristate class functions
|
|
|
|
|
|
|
|
|
|
void V3Tristate::tristateAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ TristateVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("tristate", 0, dumpTreeEitherLevel() >= 3);
|
2009-01-06 17:03:57 +01:00
|
|
|
}
|