2012-04-13 01:08:20 +00:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2009-01-06 16:03:57 +00:00
|
|
|
//*************************************************************************
|
|
|
|
// DESCRIPTION: Verilator: Deals with tristate logic
|
|
|
|
//
|
2019-11-08 03:33:59 +00:00
|
|
|
// Code available from: https://verilator.org
|
2009-01-06 16:03:57 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2020-03-21 15:24:24 +00:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
// Version 2.0.
|
2020-03-21 15:24:24 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2009-01-06 16:03:57 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
// V3Tristate's Transformations:
|
|
|
|
//
|
2012-05-05 18:49:43 +00:00
|
|
|
// Modify the design to expand tristate logic into its
|
2013-02-21 02:51:39 +00:00
|
|
|
// corresponding two state representation. At the lowest levels,
|
2012-05-09 01:53:22 +00: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
|
2018-10-27 21:29:00 +00:00
|
|
|
// VAR->isInoutish
|
|
|
|
// VAR->isPullup/isPulldown (converted to AstPullup/AstPulldown
|
|
|
|
// BufIf0/1
|
2012-05-09 01:53:22 +00:00
|
|
|
// All variables on the LHS need to become tristate when there is:
|
2019-05-19 20:13:13 +00: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 01:53:22 +00: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 01:45:28 +00: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 02:51:39 +00:00
|
|
|
// driver and output enable. If the signal propagates up into higher
|
2012-04-22 01:45:28 +00: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 16:03:57 +00:00
|
|
|
//
|
2013-02-28 03:59:17 +00: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
|
|
|
|
// rather than hierarchically.
|
2009-01-06 16:03:57 +00:00
|
|
|
//*************************************************************************
|
2019-10-05 00:17:11 +00:00
|
|
|
|
2009-01-06 16:03:57 +00:00
|
|
|
#include "config_build.h"
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
#include "V3Tristate.h"
|
|
|
|
#include "V3Ast.h"
|
|
|
|
#include "V3Stats.h"
|
2009-10-07 11:54:30 +00:00
|
|
|
#include "V3Inst.h"
|
2012-05-09 01:53:22 +00:00
|
|
|
#include "V3Graph.h"
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2018-10-14 17:43:24 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
|
2009-01-06 16:03:57 +00:00
|
|
|
//######################################################################
|
|
|
|
|
2020-11-19 02:32:16 +00:00
|
|
|
class TristateBaseVisitor VL_NOT_FINAL : public AstNVisitor {
|
2009-01-06 16:03:57 +00:00
|
|
|
public:
|
2012-04-22 01:45:28 +00:00
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-06 16:03:57 +00:00
|
|
|
};
|
|
|
|
|
2012-05-09 01:53:22 +00:00
|
|
|
//######################################################################
|
|
|
|
// Graph support classes
|
|
|
|
|
2020-11-19 02:32:16 +00:00
|
|
|
class TristateVertex final : public V3GraphVertex {
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* m_nodep;
|
2020-08-15 17:11:27 +00: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 01:53:22 +00:00
|
|
|
public:
|
|
|
|
TristateVertex(V3Graph* graphp, AstNode* nodep)
|
2020-08-16 13:55:36 +00:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
, m_nodep{nodep} {}
|
2020-11-17 00:56:16 +00:00
|
|
|
virtual ~TristateVertex() override = default;
|
2017-11-28 01:11:34 +00:00
|
|
|
// ACCESSORS
|
2012-05-09 01:53:22 +00:00
|
|
|
AstNode* nodep() const { return m_nodep; }
|
2018-02-02 02:32:58 +00:00
|
|
|
AstVar* varp() const { return VN_CAST(nodep(), Var); }
|
2020-08-15 15:44:10 +00:00
|
|
|
virtual string name() const override {
|
2020-04-15 11:58:34 +00:00
|
|
|
return ((isTristate() ? "tri\\n" : feedsTri() ? "feed\\n" : "-\\n")
|
|
|
|
+ (nodep()->prettyTypeName() + " " + cvtToHex(nodep())));
|
|
|
|
}
|
2020-08-15 15:44:10 +00:00
|
|
|
virtual string dotColor() const override {
|
2020-04-15 11:58:34 +00:00
|
|
|
return (varp() ? (isTristate() ? "darkblue" : feedsTri() ? "blue" : "lightblue")
|
|
|
|
: (isTristate() ? "darkgreen" : feedsTri() ? "green" : "lightgreen"));
|
|
|
|
}
|
2020-08-15 15:44:10 +00:00
|
|
|
virtual FileLine* fileline() const override { return nodep()->fileline(); }
|
2012-05-09 01:53:22 +00:00
|
|
|
void isTristate(bool flag) { m_isTristate = flag; }
|
|
|
|
bool isTristate() const { return m_isTristate; }
|
|
|
|
void feedsTri(bool flag) { m_feedsTri = flag; }
|
|
|
|
bool feedsTri() const { return m_feedsTri; }
|
|
|
|
void processed(bool flag) { m_processed = flag; }
|
|
|
|
bool processed() const { return m_processed; }
|
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
2020-11-19 02:32:16 +00:00
|
|
|
class TristateGraph final {
|
2012-05-09 01:53:22 +00:00
|
|
|
// NODE STATE
|
2019-05-19 20:13:13 +00:00
|
|
|
// AstVar::user5p -> TristateVertex* for variable being built
|
2020-04-15 11:58:34 +00:00
|
|
|
// AstUser5InUse m_inuser5; // In visitor below
|
2012-05-09 01:53:22 +00:00
|
|
|
|
|
|
|
// TYPES
|
|
|
|
public:
|
|
|
|
typedef std::vector<AstVar*> VarVec;
|
|
|
|
|
2020-04-15 11:58:34 +00:00
|
|
|
private:
|
2012-05-09 01:53:22 +00:00
|
|
|
// MEMBERS
|
2019-05-19 20:13:13 +00:00
|
|
|
V3Graph m_graph; // Logic graph
|
2012-05-09 01:53:22 +00:00
|
|
|
|
|
|
|
public:
|
2019-09-12 11:22:22 +00:00
|
|
|
// CONSTRUCTORS
|
2012-05-09 01:53:22 +00:00
|
|
|
TristateGraph() { clear(); }
|
|
|
|
virtual ~TristateGraph() { clear(); }
|
2020-01-25 15:19:59 +00:00
|
|
|
VL_UNCOPYABLE(TristateGraph);
|
2012-05-09 01:53:22 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2012-05-09 01:53:22 +00:00
|
|
|
|
|
|
|
TristateVertex* makeVertex(AstNode* nodep) {
|
2018-10-14 22:39:33 +00:00
|
|
|
TristateVertex* vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!vertexp) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(6, " New vertex " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
vertexp = new TristateVertex(&m_graph, nodep);
|
|
|
|
nodep->user5p(vertexp);
|
|
|
|
}
|
|
|
|
return vertexp;
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// METHODS - Graph optimization
|
|
|
|
void graphWalkRecurseFwd(TristateVertex* vtxp, int level) {
|
2019-05-19 20:13:13 +00: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
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Mark tri " << level << " " << vtxp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
TristateVertex* vvertexp = dynamic_cast<TristateVertex*>(edgep->top());
|
|
|
|
// 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 11:58:34 +00:00
|
|
|
graphWalkRecurseFwd(vvertexp, level + 1);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// A variable is tristated. Find all of the LHS VARREFs that
|
|
|
|
// drive this signal now need tristate drivers
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
TristateVertex* vvertexp = dynamic_cast<TristateVertex*>(edgep->fromp());
|
2018-02-02 02:32:58 +00:00
|
|
|
if (const AstVarRef* refp = VN_CAST(vvertexp->nodep(), VarRef)) {
|
2020-11-07 15:37:55 +00:00
|
|
|
if (refp->access().isWriteOrRW()
|
2019-05-19 20:13:13 +00: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 11:58:34 +00:00
|
|
|
graphWalkRecurseFwd(vvertexp, level + 1);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
void graphWalkRecurseBack(TristateVertex* vtxp, int level) {
|
2019-05-19 20:13:13 +00: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
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Mark feedstri " << level << " " << vtxp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
TristateVertex* vvertexp = dynamic_cast<TristateVertex*>(edgep->fromp());
|
|
|
|
// 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 11:58:34 +00:00
|
|
|
graphWalkRecurseBack(vvertexp, level + 1);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
// METHODS
|
2020-01-25 15:19:59 +00:00
|
|
|
bool empty() const { return m_graph.empty(); }
|
2012-05-09 01:53:22 +00:00
|
|
|
void clear() {
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
TristateVertex* vvertexp = static_cast<TristateVertex*>(itp);
|
|
|
|
if (vvertexp->isTristate() && !vvertexp->processed()) {
|
|
|
|
// Not v3errorSrc as no reason to stop the world
|
2018-03-10 21:32:04 +00:00
|
|
|
vvertexp->nodep()->v3error("Unsupported tristate construct"
|
2019-05-19 20:13:13 +00:00
|
|
|
" (in graph; not converted): "
|
2020-04-15 11:58:34 +00:00
|
|
|
<< vvertexp->nodep()->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
m_graph.clear();
|
|
|
|
AstNode::user5ClearTree(); // Wipe all node user5p's that point to vertexes
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
void graphWalk(AstNodeModule* nodep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
// if (debug() >= 9) m_graph.dumpDotFilePrefixed("tri_pre__" + nodep->name());
|
|
|
|
UINFO(9, " Walking " << nodep << endl);
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2018-10-14 22:39:33 +00:00
|
|
|
graphWalkRecurseFwd(static_cast<TristateVertex*>(itp), 0);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2018-10-14 22:39:33 +00:00
|
|
|
graphWalkRecurseBack(static_cast<TristateVertex*>(itp), 0);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name());
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); }
|
2012-05-09 01:53:22 +00:00
|
|
|
void associate(AstNode* fromp, AstNode* top) {
|
2019-05-19 20:13:13 +00:00
|
|
|
new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1);
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
bool isTristate(AstNode* nodep) {
|
2018-10-14 22:39:33 +00:00
|
|
|
TristateVertex* vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
|
2019-05-19 20:13:13 +00:00
|
|
|
return vertexp && vertexp->isTristate();
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
bool feedsTri(AstNode* nodep) {
|
2018-10-14 22:39:33 +00:00
|
|
|
TristateVertex* vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
|
2019-05-19 20:13:13 +00:00
|
|
|
return vertexp && vertexp->feedsTri();
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
void didProcess(AstNode* nodep) {
|
2018-10-14 22:39:33 +00:00
|
|
|
TristateVertex* vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!vertexp) {
|
|
|
|
// Not v3errorSrc as no reason to stop the world
|
|
|
|
nodep->v3error("Unsupported tristate construct (not in propagation graph): "
|
2020-04-15 11:58:34 +00:00
|
|
|
<< nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00: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 01:53:22 +00:00
|
|
|
}
|
|
|
|
// ITERATOR METHODS
|
|
|
|
VarVec tristateVars() {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Return all tristate variables
|
|
|
|
VarVec v;
|
2020-04-15 11:58:34 +00:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
TristateVertex* vvertexp = static_cast<TristateVertex*>(itp);
|
|
|
|
if (vvertexp->isTristate()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (AstVar* nodep = VN_CAST(vvertexp->nodep(), Var)) v.push_back(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return v;
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-01-06 16:03:57 +00:00
|
|
|
//######################################################################
|
2012-04-22 01:45:28 +00:00
|
|
|
// Given a node, flip any VarRef from LValue to RValue (i.e. make it an input)
|
2012-04-29 12:24:32 +00:00
|
|
|
// See also V3LinkLValue::linkLValueSet
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-11-19 02:32:16 +00:00
|
|
|
class TristatePinVisitor final : public TristateBaseVisitor {
|
2012-05-09 01:53:22 +00:00
|
|
|
TristateGraph& m_tgraph;
|
2019-05-19 20:13:13 +00:00
|
|
|
bool m_lvalue; // Flip to be an LVALUE
|
2012-04-22 01:45:28 +00:00
|
|
|
// VISITORS
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstVarRef* nodep) override {
|
2020-11-07 15:37:55 +00:00
|
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Tristate unexpected on R/W access flip");
|
|
|
|
if (m_lvalue && !nodep->access().isWriteOrRW()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Flip-to-LValue " << nodep << endl);
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->access(VAccess::WRITE);
|
2020-11-07 15:37:55 +00:00
|
|
|
} else if (!m_lvalue && !nodep->access().isReadOnly()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Flip-to-RValue " << nodep << endl);
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->access(VAccess::READ);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Mark the ex-output as tristated
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " setTristate-subpin " << nodep->varp() << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.setTristate(nodep->varp());
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstArraySel* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Doesn't work because we'd set lvalue on the array index's var
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(!m_lvalue, nodep, "ArraySel conversion to output, under tristate node");
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2017-11-23 19:55:32 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstSliceSel* nodep) override {
|
2017-11-23 19:55:32 +00:00
|
|
|
// Doesn't work because we'd set lvalue on the array index's var
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(!m_lvalue, nodep, "SliceSel conversion to output, under tristate node");
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 12:31:14 +00:00
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
public:
|
2019-09-12 11:22:22 +00:00
|
|
|
// CONSTRUCTORS
|
2012-05-09 01:53:22 +00:00
|
|
|
TristatePinVisitor(AstNode* nodep, TristateGraph& tgraph, bool lvalue)
|
2020-08-22 11:43:56 +00:00
|
|
|
: m_tgraph(tgraph) // Need () or GCC 4.8 false warning
|
2020-08-16 13:55:36 +00:00
|
|
|
, m_lvalue{lvalue} {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(nodep);
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2020-11-17 00:56:16 +00:00
|
|
|
virtual ~TristatePinVisitor() override = default;
|
2012-04-22 01:45:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-11-19 02:32:16 +00:00
|
|
|
class TristateVisitor final : public TristateBaseVisitor {
|
2009-01-06 16:03:57 +00:00
|
|
|
// NODE STATE
|
2019-05-19 20:13:13 +00:00
|
|
|
// *::user1p -> pointer to output enable __en expressions
|
|
|
|
// *::user2 -> int - already visited, see U2_ enum
|
|
|
|
// AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p)
|
|
|
|
// AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p)
|
2012-05-09 01:53:22 +00:00
|
|
|
// See TristateGraph:
|
2019-05-19 20:13:13 +00:00
|
|
|
// AstVar::user5p -> TristateVertex* for variable being built
|
|
|
|
// AstStmt*::user5p -> TristateVertex* for this statement
|
2020-04-15 11:58:34 +00:00
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
AstUser2InUse m_inuser2;
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
AstUser4InUse m_inuser4;
|
|
|
|
AstUser5InUse m_inuser5;
|
2012-04-22 01:45:28 +00:00
|
|
|
|
|
|
|
// TYPES
|
|
|
|
typedef std::vector<AstVarRef*> RefVec;
|
2020-11-26 01:57:30 +00:00
|
|
|
typedef std::unordered_map<AstVar*, RefVec*> VarMap;
|
2020-08-16 16:05:35 +00:00
|
|
|
enum : uint8_t {
|
2020-04-15 11:58:34 +00: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 01:45:28 +00:00
|
|
|
|
|
|
|
// MEMBERS
|
2020-08-16 13:55:36 +00:00
|
|
|
bool m_graphing = false; // Major mode - creating graph
|
2012-05-09 01:53:22 +00:00
|
|
|
//
|
2020-08-16 13:55:36 +00:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
AstCell* m_cellp = nullptr; // current cell
|
2020-04-15 11:58:34 +00:00
|
|
|
VarMap m_lhsmap; // LHS driver map
|
2020-08-16 13:55:36 +00:00
|
|
|
int m_unique = 0;
|
|
|
|
bool m_alhs = false; // On LHS of assignment
|
|
|
|
AstNode* m_logicp = nullptr; // Current logic being built
|
2020-04-15 11:58:34 +00:00
|
|
|
TristateGraph m_tgraph; // Logic graph
|
2012-04-22 01:45:28 +00:00
|
|
|
|
|
|
|
// STATS
|
2019-10-05 11:54:14 +00:00
|
|
|
VDouble0 m_statTriSigs; // stat tracking
|
2012-04-22 01:45:28 +00:00
|
|
|
|
|
|
|
// METHODS
|
2012-05-06 20:52:08 +00:00
|
|
|
string dbgState() {
|
2020-04-15 11:58:34 +00:00
|
|
|
string o = (m_graphing ? " gr " : " ng ");
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) o += "alhs ";
|
|
|
|
return o;
|
2012-05-06 20:52:08 +00:00
|
|
|
}
|
2012-05-09 01:53:22 +00:00
|
|
|
void associateLogic(AstNode* fromp, AstNode* top) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (m_logicp) m_tgraph.associate(fromp, top);
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
AstNode* getEnp(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// checks if user1p() is null, and if so, adds a constant output
|
|
|
|
// enable driver of all 1's. Otherwise returns the user1p() data.
|
2019-05-10 00:03:19 +00:00
|
|
|
if (!nodep->user1p()) {
|
|
|
|
V3Number num(nodep, nodep->width());
|
|
|
|
num.setAllBits1();
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* enp = new AstConst(nodep->fileline(), num);
|
|
|
|
nodep->user1p(enp);
|
|
|
|
}
|
|
|
|
return nodep->user1p();
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
AstVar* getCreateEnVarp(AstVar* invarp) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Return the master __en for the specified input variable
|
|
|
|
if (!invarp->user1p()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP,
|
|
|
|
invarp->name() + "__en", invarp);
|
|
|
|
UINFO(9, " newenv " << newp << endl);
|
|
|
|
if (!m_modp) {
|
2020-06-09 23:20:16 +00:00
|
|
|
invarp->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported: Creating tristate signal not underneath a module: "
|
|
|
|
<< invarp->prettyNameQ());
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
|
|
|
m_modp->addStmtp(newp);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
invarp->user1p(newp); // find envar given invarp
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
return VN_CAST(invarp->user1p(), Var);
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
AstVar* getCreateOutVarp(AstVar* invarp) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Return the master __out for the specified input variable
|
|
|
|
if (!invarp->user4p()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP,
|
|
|
|
invarp->name() + "__out", invarp);
|
|
|
|
UINFO(9, " newout " << newp << endl);
|
|
|
|
if (!m_modp) {
|
2020-06-09 23:20:16 +00:00
|
|
|
invarp->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported: Creating tristate signal not underneath a module: "
|
|
|
|
<< invarp->prettyNameQ());
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
|
|
|
m_modp->addStmtp(newp);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
invarp->user4p(newp); // find outvar given invarp
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
return VN_CAST(invarp->user4p(), Var);
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2012-04-29 14:14:13 +00:00
|
|
|
AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* newp = new AstVar(fromp->fileline(), AstVarType::MODULETEMP,
|
|
|
|
"__Vtriunconn" + cvtToStr(m_unique++), dtypep);
|
|
|
|
UINFO(9, " newunc " << newp << endl);
|
|
|
|
if (!m_modp) {
|
2020-06-09 23:20:16 +00:00
|
|
|
newp->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported: Creating tristate signal not underneath a module");
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
|
|
|
m_modp->addStmtp(newp);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
return newp;
|
2012-04-27 01:11:48 +00:00
|
|
|
}
|
|
|
|
|
2012-05-09 01:53:22 +00:00
|
|
|
void mapInsertLhsVarRef(AstVarRef* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
AstVar* key = nodep->varp();
|
2020-08-16 15:43:49 +00:00
|
|
|
const auto it = m_lhsmap.find(key);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " mapInsertLhsVarRef " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (it == m_lhsmap.end()) { // Not found
|
2020-01-17 01:17:11 +00:00
|
|
|
RefVec* refsp = new RefVec();
|
|
|
|
refsp->push_back(nodep);
|
|
|
|
m_lhsmap.insert(make_pair(key, refsp));
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2018-12-06 11:56:39 +00:00
|
|
|
it->second->push_back(nodep);
|
|
|
|
}
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) {
|
2019-05-19 20:13:13 +00: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
|
|
|
|
AstNode* newp = new AstShiftL(selp->fileline(),
|
2020-04-15 11:58:34 +00:00
|
|
|
new AstExtend(selp->fileline(), enp, selp->fromp()->width()),
|
|
|
|
selp->lsbp()->cloneTree(false), selp->fromp()->width());
|
2019-05-19 20:13:13 +00:00
|
|
|
return newp;
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2012-05-05 19:03:00 +00:00
|
|
|
void setPullDirection(AstVar* varp, AstPull* pullp) {
|
2018-10-14 22:39:33 +00:00
|
|
|
AstPull* oldpullp = static_cast<AstPull*>(varp->user3p());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!oldpullp) {
|
|
|
|
varp->user3p(pullp); // save off to indicate the pull direction
|
|
|
|
} else {
|
|
|
|
if (oldpullp->direction() != pullp->direction()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
pullp->v3warn(E_UNSUPPORTED, "Unsupported: Conflicting pull directions.\n"
|
2020-11-19 02:03:23 +00:00
|
|
|
<< pullp->warnContextPrimary() << '\n'
|
2020-06-09 23:20:16 +00:00
|
|
|
<< oldpullp->warnOther()
|
|
|
|
<< "... Location of conflicting pull.\n"
|
|
|
|
<< oldpullp->warnContextSecondary());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
void checkUnhandled(AstNode* nodep) {
|
2019-05-19 20:13:13 +00: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-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
// Ignore Var's because they end up adjacent to statements
|
2018-02-02 02:32:58 +00:00
|
|
|
if ((nodep->op1p() && nodep->op1p()->user1p() && !VN_IS(nodep->op1p(), Var))
|
|
|
|
|| (nodep->op2p() && nodep->op2p()->user1p() && !VN_IS(nodep->op1p(), Var))
|
|
|
|
|| (nodep->op3p() && nodep->op3p()->user1p() && !VN_IS(nodep->op1p(), Var))
|
|
|
|
|| (nodep->op4p() && nodep->op4p()->user1p() && !VN_IS(nodep->op1p(), Var))) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2012-05-05 18:55:35 +00:00
|
|
|
void insertTristates(AstNodeModule* nodep) {
|
2019-05-19 20:13:13 +00: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.
|
|
|
|
TristateGraph::VarVec vars = m_tgraph.tristateVars();
|
2020-11-11 03:10:38 +00:00
|
|
|
for (auto varp : vars) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_tgraph.isTristate(varp)) {
|
2020-08-16 15:43:49 +00:00
|
|
|
const auto it = m_lhsmap.find(varp);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (it == m_lhsmap.end()) {
|
|
|
|
// set output enable to always be off on this assign
|
|
|
|
// statement so that this var is floating
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(8, " Adding driver to var " << varp << endl);
|
|
|
|
AstConst* constp = new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
|
|
|
varp->width(), 0);
|
2020-09-07 21:09:25 +00:00
|
|
|
AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newoev " << newp << endl);
|
|
|
|
varrefp->user1p(new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
|
|
|
varp->width(), 0));
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addStmtp(newp);
|
|
|
|
mapInsertLhsVarRef(varrefp); // insertTristates will convert
|
|
|
|
// // to a varref to the __out# variable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now go through the lhs driver map and generate the output
|
|
|
|
// enable logic for any tristates.
|
|
|
|
// Note there might not be any drivers.
|
|
|
|
for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) {
|
2020-04-15 11:58:34 +00:00
|
|
|
nextit = it;
|
|
|
|
++nextit;
|
2018-12-06 11:56:39 +00:00
|
|
|
AstVar* invarp = it->first;
|
2020-01-17 01:17:11 +00:00
|
|
|
RefVec* refsp = it->second;
|
2012-05-05 18:55:35 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// Figure out if this var needs tristate expanded.
|
|
|
|
if (!m_tgraph.isTristate(invarp)) {
|
|
|
|
// This var has no tristate logic, so we leave it alone.
|
|
|
|
UINFO(8, " NO TRISTATE ON:" << invarp << endl);
|
|
|
|
m_lhsmap.erase(invarp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(delete refsp, refsp);
|
2019-05-19 20:13:13 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++m_statTriSigs;
|
|
|
|
m_tgraph.didProcess(invarp);
|
|
|
|
UINFO(8, " TRISTATE EXPANDING:" << invarp << endl);
|
|
|
|
|
|
|
|
// 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.
|
2020-08-15 14:12:55 +00:00
|
|
|
AstVar* envarp = nullptr;
|
|
|
|
AstVar* outvarp = nullptr; // __out
|
2019-05-19 20:13:13 +00:00
|
|
|
AstVar* lhsp = invarp; // Variable to assign drive-value to (<in> or __out)
|
|
|
|
if (!nodep->isTop() && invarp->isIO()) {
|
|
|
|
// This var becomes an input
|
|
|
|
invarp->varType2In(); // convert existing port to type input
|
|
|
|
// Create an output port (__out)
|
|
|
|
outvarp = getCreateOutVarp(invarp);
|
|
|
|
outvarp->varType2Out();
|
|
|
|
lhsp = outvarp; // Must assign to __out, not to normal input signal
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " TRISTATE propagates up with " << lhsp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Create an output enable port (__en)
|
2020-04-15 11:58:34 +00:00
|
|
|
// May already be created if have foo === 1'bz somewhere
|
|
|
|
envarp = getCreateEnVarp(invarp);
|
2019-05-19 20:13:13 +00:00
|
|
|
envarp->varType2Out();
|
|
|
|
//
|
|
|
|
outvarp->user1p(envarp);
|
|
|
|
outvarp->user3p(invarp->user3p()); // AstPull* propagation
|
2020-04-15 11:58:34 +00:00
|
|
|
if (invarp->user3p()) UINFO(9, "propagate pull to " << outvarp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else if (invarp->user1p()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
envarp = VN_CAST(invarp->user1p(), Var); // From CASEEQ, foo === 1'bz
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:12:55 +00:00
|
|
|
AstNode* orp = nullptr;
|
|
|
|
AstNode* enp = nullptr;
|
|
|
|
AstNode* undrivenp = nullptr;
|
2019-05-19 20:13:13 +00:00
|
|
|
|
|
|
|
// loop through the lhs drivers to build the driver resolution logic
|
2020-11-11 03:10:38 +00:00
|
|
|
for (auto refp : *refsp) {
|
2019-05-19 20:13:13 +00:00
|
|
|
int w = lhsp->width();
|
|
|
|
|
|
|
|
// create the new lhs driver for this var
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
|
|
|
|
lhsp->name() + "__out" + cvtToStr(m_unique),
|
2019-05-19 20:13:13 +00:00
|
|
|
VFlagBitPacked(), w); // 2-state ok; sep enable
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newout " << newlhsp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addStmtp(newlhsp);
|
|
|
|
refp->varp(newlhsp); // assign the new var to the varref
|
|
|
|
refp->name(newlhsp->name());
|
|
|
|
|
|
|
|
// create a new var for this drivers enable signal
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* newenp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
|
|
|
|
lhsp->name() + "__en" + cvtToStr(m_unique++),
|
|
|
|
VFlagBitPacked(), w); // 2-state ok
|
|
|
|
UINFO(9, " newenp " << newenp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addStmtp(newenp);
|
|
|
|
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* enassp = new AstAssignW(
|
2020-09-07 21:09:25 +00:00
|
|
|
refp->fileline(), new AstVarRef(refp->fileline(), newenp, VAccess::WRITE),
|
|
|
|
getEnp(refp));
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newass " << enassp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addStmtp(enassp);
|
|
|
|
|
|
|
|
// now append this driver to the driver logic.
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* ref1p = new AstVarRef(refp->fileline(), newlhsp, VAccess::READ);
|
|
|
|
AstNode* ref2p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* andp = new AstAnd(refp->fileline(), ref1p, ref2p);
|
|
|
|
|
|
|
|
// or this to the others
|
|
|
|
orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp);
|
|
|
|
|
|
|
|
if (envarp) {
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* ref3p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
|
2019-05-19 20:13:13 +00:00
|
|
|
enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p);
|
|
|
|
}
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* tmp = new AstNot(
|
|
|
|
newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, VAccess::READ));
|
2020-04-15 11:58:34 +00:00
|
|
|
undrivenp = ((!undrivenp) ? tmp : new AstAnd(refp->fileline(), tmp, undrivenp));
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
if (!undrivenp) { // No drivers on the bus
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number ones(invarp, lhsp->width());
|
|
|
|
ones.setAllBits1();
|
2019-05-10 00:03:19 +00:00
|
|
|
undrivenp = new AstConst(invarp->fileline(), ones);
|
|
|
|
}
|
|
|
|
if (!outvarp) {
|
|
|
|
// This is the final resolution of the tristate, so we apply
|
|
|
|
// the pull direction to any undriven pins.
|
|
|
|
V3Number pull(invarp, lhsp->width());
|
2018-10-14 22:39:33 +00:00
|
|
|
AstPull* pullp = static_cast<AstPull*>(lhsp->user3p());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (pullp && pullp->direction() == 1) {
|
|
|
|
pull.setAllBits1();
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, "Has pullup " << pullp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
pull.setAllBits0(); // Default pull direction is down.
|
|
|
|
}
|
|
|
|
undrivenp = new AstAnd(invarp->fileline(), undrivenp,
|
|
|
|
new AstConst(invarp->fileline(), pull));
|
|
|
|
orp = new AstOr(invarp->fileline(), orp, undrivenp);
|
|
|
|
} else {
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(undrivenp->deleteTree(), undrivenp);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
if (envarp) {
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->addStmtp(new AstAssignW(
|
2020-09-07 21:09:25 +00:00
|
|
|
enp->fileline(), new AstVarRef(envarp->fileline(), envarp, VAccess::WRITE),
|
|
|
|
enp));
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
// __out (child) or <in> (parent) = drive-value expression
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* assp = new AstAssignW(
|
|
|
|
lhsp->fileline(), new AstVarRef(lhsp->fileline(), lhsp, VAccess::WRITE), orp);
|
2019-05-19 20:13:13 +00:00
|
|
|
assp->user2(U2_BOTH); // Don't process further; already resolved
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) assp->dumpTree(cout, "-lhsp-eqn: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addStmtp(assp);
|
|
|
|
// Delete the map and vector list now that we have expanded it.
|
|
|
|
m_lhsmap.erase(invarp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(delete refsp, refsp);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2012-05-05 18:55:35 +00:00
|
|
|
}
|
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
// VISITORS
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstConst* nodep) override {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (!m_alhs && nodep->num().hasZ()) m_tgraph.setTristate(nodep);
|
2019-05-19 20:13:13 +00: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
|
|
|
|
AstVar* varp = getCreateUnconnVarp(nodep, nodep->dtypep());
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* newp = new AstVarRef(nodep->fileline(), varp, VAccess::WRITE);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " const->" << newp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (m_tgraph.isTristate(nodep)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
FileLine* fl = nodep->fileline();
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number numz(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
numz.opBitsZ(nodep->num()); // Z->1, else 0
|
|
|
|
V3Number numz0(nodep, nodep->width());
|
|
|
|
numz0.opNot(numz); // Z->0, else 1
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number num1(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
num1.opAnd(nodep->num(), numz0); // 01X->01X, Z->0
|
2019-05-19 20:13:13 +00:00
|
|
|
AstConst* newconstp = new AstConst(fl, num1);
|
2020-04-15 11:58:34 +00:00
|
|
|
AstConst* enp = new AstConst(fl, numz0);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->replaceWith(newconstp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
newconstp->user1p(enp); // Propagate up constant with non-Z bits as 1
|
|
|
|
}
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstCond* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) {
|
|
|
|
associateLogic(nodep, nodep->expr1p());
|
|
|
|
associateLogic(nodep, nodep->expr2p());
|
|
|
|
} else {
|
|
|
|
associateLogic(nodep->expr1p(), nodep);
|
|
|
|
associateLogic(nodep->expr2p(), nodep);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00: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.
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* condp = nodep->condp();
|
2019-05-19 20:13:13 +00:00
|
|
|
if (condp->user1p()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
condp->v3warn(E_UNSUPPORTED, "Unsupported: don't know how to deal with "
|
|
|
|
"tristate logic in the conditional expression");
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
AstNode* expr1p = nodep->expr1p();
|
|
|
|
AstNode* expr2p = nodep->expr2p();
|
|
|
|
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
AstNode* en1p = getEnp(expr1p);
|
|
|
|
AstNode* en2p = getEnp(expr2p);
|
|
|
|
// The output enable of a cond is a cond of the output enable of the
|
|
|
|
// two expressions with the same conditional.
|
|
|
|
AstNode* enp = new AstCond(nodep->fileline(), condp->cloneTree(false), en1p, en2p);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newcond " << enp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user1p(enp); // propagate up COND(lhsp->enable, rhsp->enable)
|
2020-08-15 14:12:55 +00:00
|
|
|
expr1p->user1p(nullptr);
|
|
|
|
expr2p->user1p(nullptr);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstSel* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) {
|
|
|
|
associateLogic(nodep, nodep->fromp());
|
|
|
|
} else {
|
|
|
|
associateLogic(nodep->fromp(), nodep);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (m_alhs) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->user1p()) {
|
|
|
|
// Form a "deposit" instruction. Would be nicer if we made this a new AST type
|
|
|
|
AstNode* newp = newEnableDeposit(nodep, nodep->user1p());
|
|
|
|
nodep->fromp()->user1p(newp); // Push to varref (etc)
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newp->dumpTree(cout, "-assign-sel; ");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->lsbp()->user1p()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported RHS tristate construct: "
|
|
|
|
<< nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
if (nodep->fromp()->user1p()) { // SEL(VARREF, lsb)
|
|
|
|
AstNode* en1p = getEnp(nodep->fromp());
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* enp
|
|
|
|
= new AstSel(nodep->fileline(), en1p, nodep->lsbp()->cloneTree(true),
|
|
|
|
nodep->widthp()->cloneTree(true));
|
|
|
|
UINFO(9, " newsel " << enp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user1p(enp); // propagate up SEL(fromp->enable, value)
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstConcat* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) {
|
|
|
|
associateLogic(nodep, nodep->lhsp());
|
|
|
|
associateLogic(nodep, nodep->rhsp());
|
|
|
|
} else {
|
|
|
|
associateLogic(nodep->lhsp(), nodep);
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->user1p()) {
|
|
|
|
// Each half of the concat gets a select of the enable expression
|
|
|
|
AstNode* enp = nodep->user1p();
|
2020-08-15 14:12:55 +00:00
|
|
|
nodep->user1p(nullptr);
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->lhsp()->user1p(new AstSel(nodep->fileline(), enp->cloneTree(true),
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->rhsp()->width(),
|
|
|
|
nodep->lhsp()->width()));
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->rhsp()->user1p(
|
|
|
|
new AstSel(nodep->fileline(), enp, 0, nodep->rhsp()->width()));
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Generate the new output enable signal, just as a concat
|
|
|
|
// identical to the data concat
|
|
|
|
AstNode* expr1p = nodep->lhsp();
|
|
|
|
AstNode* expr2p = nodep->rhsp();
|
|
|
|
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
AstNode* en1p = getEnp(expr1p);
|
|
|
|
AstNode* en2p = getEnp(expr2p);
|
|
|
|
AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newconc " << enp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user1p(enp); // propagate up CONCAT(lhsp->enable, rhsp->enable)
|
2020-08-15 14:12:55 +00:00
|
|
|
expr1p->user1p(nullptr);
|
|
|
|
expr2p->user1p(nullptr);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstBufIf1* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// For BufIf1, the enable is the LHS expression
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
m_tgraph.setTristate(nodep);
|
|
|
|
} else {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) nodep->backp()->dumpTree(cout, "-bufif: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_alhs) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
AstNode* expr1p = nodep->lhsp()->unlinkFrBack();
|
|
|
|
AstNode* expr2p = nodep->rhsp()->unlinkFrBack();
|
|
|
|
AstNode* enp;
|
|
|
|
if (AstNode* en2p = expr2p->user1p()) {
|
|
|
|
enp = new AstAnd(nodep->fileline(), expr1p, en2p);
|
|
|
|
} else {
|
|
|
|
enp = expr1p;
|
|
|
|
}
|
2020-08-15 14:12:55 +00:00
|
|
|
expr1p->user1p(nullptr);
|
2019-05-19 20:13:13 +00:00
|
|
|
expr2p->user1p(enp); // Becomes new node
|
|
|
|
// Don't need the BufIf any more, can just have the data direct
|
|
|
|
nodep->replaceWith(expr2p);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " bufif datap=" << expr2p << endl);
|
|
|
|
UINFO(9, " bufif enp=" << enp << endl);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2012-05-05 19:04:33 +00:00
|
|
|
void visitAndOr(AstNodeBiop* nodep, bool isAnd) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
|
|
|
associateLogic(nodep->lhsp(), nodep);
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
} else {
|
|
|
|
if (m_alhs && nodep->user1p()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00: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.
|
|
|
|
AstNode* expr1p = nodep->lhsp();
|
|
|
|
AstNode* expr2p = nodep->rhsp();
|
|
|
|
if (!expr1p->user1p() && !expr2p->user1p()) {
|
|
|
|
return; // no tristates in either expression, so nothing to do
|
|
|
|
}
|
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
AstNode* en1p = getEnp(expr1p);
|
|
|
|
AstNode* en2p = getEnp(expr2p);
|
|
|
|
AstNode* subexpr1p = expr1p->cloneTree(false);
|
|
|
|
AstNode* subexpr2p = expr2p->cloneTree(false);
|
|
|
|
if (isAnd) {
|
|
|
|
subexpr1p = new AstNot(nodep->fileline(), subexpr1p);
|
|
|
|
subexpr2p = new AstNot(nodep->fileline(), subexpr2p);
|
|
|
|
}
|
|
|
|
// calc new output enable
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* 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)));
|
|
|
|
UINFO(9, " neweqn " << enp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user1p(enp);
|
2020-08-15 14:12:55 +00:00
|
|
|
expr1p->user1p(nullptr);
|
|
|
|
expr2p->user1p(nullptr);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstAnd* nodep) override { visitAndOr(nodep, true); }
|
|
|
|
virtual void visit(AstOr* nodep) override { visitAndOr(nodep, false); }
|
2011-02-24 02:21:59 +00:00
|
|
|
|
2012-04-22 01:45:28 +00:00
|
|
|
void visitAssign(AstNodeAssign* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
|
|
|
if (nodep->user2() & U2_GRAPHING) return;
|
|
|
|
nodep->user2(U2_GRAPHING);
|
|
|
|
m_logicp = nodep;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
m_alhs = true;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
m_alhs = false;
|
|
|
|
associateLogic(nodep->rhsp(), nodep);
|
|
|
|
associateLogic(nodep, nodep->lhsp());
|
2020-08-15 14:12:55 +00:00
|
|
|
m_logicp = nullptr;
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (nodep->user2() & U2_NONGRAPH) {
|
|
|
|
return; // Iterated here, or created assignment to ignore
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user2(U2_NONGRAPH);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "-assign: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// if the rhsp of this assign statement has an output enable driver,
|
2019-09-09 11:50:21 +00:00
|
|
|
// then propagate the corresponding output enable assign statement.
|
2019-05-19 20:13:13 +00: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 14:12:55 +00:00
|
|
|
nodep->rhsp()->user1p(nullptr);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " enp<-rhs " << nodep->lhsp()->user1p() << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
}
|
|
|
|
m_alhs = true; // And user1p() will indicate tristate equation, if any
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
m_alhs = false;
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstAssignW* nodep) override { visitAssign(nodep); }
|
|
|
|
virtual void visit(AstAssign* nodep) override { visitAssign(nodep); }
|
2012-04-22 01:45:28 +00:00
|
|
|
|
|
|
|
void visitCaseEq(AstNodeBiop* nodep, bool neq) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00: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 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
|
|
|
// Constification always moves const to LHS
|
|
|
|
const AstConst* constp = VN_CAST(nodep->lhsp(), Const);
|
2018-02-02 02:32:58 +00:00
|
|
|
AstVarRef* varrefp = VN_CAST(nodep->rhsp(), VarRef); // Input variable
|
2019-05-19 20:13:13 +00:00
|
|
|
if (constp && constp->user1p() && varrefp) {
|
|
|
|
// 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in))
|
|
|
|
varrefp->unlinkFrBack();
|
|
|
|
FileLine* fl = nodep->fileline();
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number oneIfEn = VN_CAST(constp->user1p(), Const)
|
|
|
|
->num(); // visit(AstConst) already split into en/ones
|
2020-08-16 18:55:46 +00:00
|
|
|
const V3Number& oneIfEnOne = constp->num();
|
2019-05-19 20:13:13 +00:00
|
|
|
AstVar* envarp = getCreateEnVarp(varrefp->varp());
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* newp
|
|
|
|
= new AstLogAnd(fl,
|
|
|
|
new AstEq(fl, new AstConst(fl, oneIfEn),
|
|
|
|
new AstVarRef(fl, envarp, VAccess::READ)),
|
|
|
|
// Keep the caseeq if there are X's present
|
|
|
|
new AstEqCase(fl, new AstConst(fl, oneIfEnOne), varrefp));
|
2019-05-19 20:13:13 +00:00
|
|
|
if (neq) newp = new AstLogNot(fl, newp);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newceq " << newp << endl);
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "-caseeq-old: ");
|
|
|
|
if (debug() >= 9) newp->dumpTree(cout, "-caseeq-new: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
checkUnhandled(nodep);
|
|
|
|
}
|
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
2014-03-08 18:33:44 +00:00
|
|
|
void visitEqNeqWild(AstNodeBiop* nodep) {
|
2018-02-02 02:32:58 +00:00
|
|
|
if (!VN_IS(nodep->rhsp(), Const)) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, // Says spac.
|
|
|
|
"Unsupported: RHS of ==? or !=? must be constant to be synthesizable");
|
2019-05-19 20:13:13 +00:00
|
|
|
// rhs we want to keep X/Z intact, so otherwise ignore
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->lhsp()->user1p()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
"Unsupported LHS tristate construct: " << nodep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-03-08 18:33:44 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstEqCase* nodep) override { visitCaseEq(nodep, false); }
|
|
|
|
virtual void visit(AstNeqCase* nodep) override { visitCaseEq(nodep, true); }
|
|
|
|
virtual void visit(AstEqWild* nodep) override { visitEqNeqWild(nodep); }
|
|
|
|
virtual void visit(AstNeqWild* nodep) override { visitEqNeqWild(nodep); }
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstPull* nodep) override {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2020-08-15 14:12:55 +00:00
|
|
|
AstVarRef* varrefp = nullptr;
|
2018-02-27 12:24:31 +00:00
|
|
|
if (VN_IS(nodep->lhsp(), VarRef)) {
|
|
|
|
varrefp = VN_CAST(nodep->lhsp(), VarRef);
|
|
|
|
} else if (VN_IS(nodep->lhsp(), Sel)
|
|
|
|
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), VarRef)) {
|
|
|
|
varrefp = VN_CAST(VN_CAST(nodep->lhsp(), Sel)->fromp(), VarRef);
|
2018-02-11 00:08:07 +00:00
|
|
|
}
|
|
|
|
if (!varrefp) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 4) nodep->dumpTree(cout, "- ");
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported pullup/down (weak driver) construct.");
|
2018-02-11 00:08:07 +00:00
|
|
|
} else {
|
|
|
|
if (m_graphing) {
|
2020-09-07 21:09:25 +00:00
|
|
|
varrefp->access(VAccess::WRITE);
|
2018-02-11 00:08:07 +00:00
|
|
|
m_logicp = nodep;
|
|
|
|
m_tgraph.setTristate(nodep);
|
|
|
|
associateLogic(nodep, varrefp->varp());
|
2020-08-15 14:12:55 +00:00
|
|
|
m_logicp = nullptr;
|
2018-02-11 00:08:07 +00:00
|
|
|
} else {
|
|
|
|
// Replace any pullup/pulldowns with assignw logic and set the
|
|
|
|
// direction of the pull in the user3() data on the var. Given
|
|
|
|
// 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 11:50:21 +00:00
|
|
|
// in opposite directions on individual pins.
|
2020-09-07 21:09:25 +00:00
|
|
|
varrefp->access(VAccess::WRITE);
|
2018-02-11 00:08:07 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
m_tgraph.didProcess(varrefp->varp());
|
|
|
|
setPullDirection(varrefp->varp(), nodep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_graphing) {
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->unlinkFrBack();
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Node must persist as user3p points to it
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2012-05-09 01:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void iteratePinGuts(AstPin* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
|
|
|
m_logicp = nodep;
|
|
|
|
if (nodep->exprp()) {
|
|
|
|
associateLogic(nodep->exprp(), nodep);
|
|
|
|
associateLogic(nodep, nodep->exprp());
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 14:12:55 +00:00
|
|
|
m_logicp = nullptr;
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
// All heavy lifting completed in graph visitor.
|
2020-04-15 11:58:34 +00:00
|
|
|
if (nodep->exprp()) m_tgraph.didProcess(nodep);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2011-02-24 02:21:59 +00:00
|
|
|
|
2012-04-27 01:11:48 +00:00
|
|
|
// .tri(SEL(trisig,x)) becomes
|
|
|
|
// INPUT: -> (VARREF(trisig__pinin)),
|
|
|
|
// trisig__pinin = SEL(trisig,x) // via pinReconnectSimple
|
|
|
|
// OUTPUT: -> (VARREF(trisig__pinout))
|
2012-05-08 03:42:58 +00:00
|
|
|
// SEL(trisig,x) = trisig__pinout
|
2019-05-19 20:13:13 +00:00
|
|
|
// ^-- ->user1p() == trisig__pinen
|
2012-04-27 01:11:48 +00:00
|
|
|
// ENABLE: -> (VARREF(trisig__pinen)
|
2019-05-19 20:13:13 +00: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?
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstPin* nodep) override {
|
2019-05-19 20:13:13 +00: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.
|
2018-10-14 22:39:33 +00:00
|
|
|
AstVar* enModVarp = static_cast<AstVar*>(nodep->modVarp()->user1p());
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!enModVarp) {
|
2020-04-10 03:26:03 +00:00
|
|
|
// May have an output only that later connects to a tristate, so simplify now.
|
|
|
|
V3Inst::pinReconnectSimple(nodep, m_cellp, false);
|
2019-05-19 20:13:13 +00:00
|
|
|
iteratePinGuts(nodep);
|
|
|
|
return; // No __en signals on this pin
|
|
|
|
}
|
|
|
|
// Tristate exists:
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "-pin-pre: ");
|
2012-04-22 01:45:28 +00:00
|
|
|
|
2018-10-27 21:29:00 +00:00
|
|
|
// Empty/in-only; need Z to propagate
|
|
|
|
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());
|
|
|
|
if (!nodep->exprp()) { // No-connect; covert to empty connection
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(5, "Unconnected pin terminate " << nodep << endl);
|
2018-10-27 21:29:00 +00:00
|
|
|
AstVar* ucVarp = getCreateUnconnVarp(nodep, nodep->modVarp()->dtypep());
|
|
|
|
nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp,
|
|
|
|
// We converted, so use declaration output state
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->modVarp()->declDirection().isWritable()
|
|
|
|
? VAccess::WRITE
|
|
|
|
: VAccess::READ));
|
2019-05-19 20:13:13 +00: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.
|
2018-03-16 03:19:43 +00:00
|
|
|
AstAssignW* reAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, true, true);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(5, "Input pin buffering: " << reAssignp << endl);
|
2019-05-19 20:13:13 +00: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
|
|
|
|
AstNode* enrefp;
|
|
|
|
{
|
2020-04-15 11:58:34 +00:00
|
|
|
AstVar* enVarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->name() + "__en" + cvtToStr(m_unique++),
|
|
|
|
VFlagBitPacked(), enModVarp->width());
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newenv " << enVarp << endl);
|
2020-09-07 21:09:25 +00:00
|
|
|
AstPin* enpinp
|
|
|
|
= new AstPin(nodep->fileline(), nodep->pinNum(),
|
|
|
|
enModVarp->name(), // should be {var}"__en"
|
|
|
|
new AstVarRef(nodep->fileline(), enVarp, VAccess::WRITE));
|
2019-05-19 20:13:13 +00:00
|
|
|
enpinp->modVarp(enModVarp);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newpin " << enpinp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
enpinp->user2(U2_BOTH); // don't iterate the pin later
|
|
|
|
nodep->addNextHere(enpinp);
|
|
|
|
m_modp->addStmtp(enVarp);
|
2020-09-07 21:09:25 +00:00
|
|
|
enrefp = new AstVarRef(nodep->fileline(), enVarp, VAccess::READ);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newvrf " << enrefp << endl);
|
|
|
|
if (debug() >= 9) enpinp->dumpTree(cout, "-pin-ena: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
// Create new output pin
|
2020-08-15 14:12:55 +00:00
|
|
|
AstAssignW* outAssignp = nullptr; // If reconnected, the related assignment
|
|
|
|
AstPin* outpinp = nullptr;
|
2018-10-14 22:39:33 +00:00
|
|
|
AstVar* outModVarp = static_cast<AstVar*>(nodep->modVarp()->user4p());
|
2018-01-25 03:30:30 +00:00
|
|
|
if (!outModVarp) {
|
|
|
|
// At top, no need for __out as might be input only. Otherwise resolvable.
|
2020-01-18 12:56:50 +00:00
|
|
|
if (!m_modp->isTop()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: tristate in top-level IO: "
|
|
|
|
<< nodep->prettyNameQ());
|
2020-01-18 12:56:50 +00:00
|
|
|
}
|
2018-01-25 03:30:30 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set
|
2020-04-15 11:58:34 +00:00
|
|
|
outpinp = new AstPin(nodep->fileline(), nodep->pinNum(),
|
2019-05-19 20:13:13 +00:00
|
|
|
outModVarp->name(), // should be {var}"__out"
|
|
|
|
outexprp);
|
|
|
|
outpinp->modVarp(outModVarp);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newpin " << outpinp << endl);
|
2019-05-19 20:13:13 +00: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
|
2020-04-15 11:58:34 +00:00
|
|
|
TristatePinVisitor visitor(outexprp, m_tgraph, true);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) outpinp->dumpTree(cout, "-pin-opr: ");
|
|
|
|
outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp,
|
|
|
|
true); // Note may change outpinp->exprp()
|
|
|
|
if (debug() >= 9) outpinp->dumpTree(cout, "-pin-out: ");
|
|
|
|
if (debug() >= 9 && outAssignp) outAssignp->dumpTree(cout, "-pin-out: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// Must still iterate the outAssignp, as need to build output equation
|
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// Existing pin becomes an input, and we mark each resulting signal as tristate
|
2020-04-15 11:58:34 +00:00
|
|
|
TristatePinVisitor visitor(nodep->exprp(), m_tgraph, false);
|
|
|
|
AstNode* inAssignp = V3Inst::pinReconnectSimple(
|
|
|
|
nodep, m_cellp, true); // Note may change nodep->exprp()
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "-pin-in: ");
|
|
|
|
if (debug() >= 9 && inAssignp) inAssignp->dumpTree(cout, "-pin-as: ");
|
2012-05-09 01:53:22 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// Connect enable to output signal
|
|
|
|
AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to
|
|
|
|
if (!outAssignp) {
|
2018-01-25 03:30:30 +00:00
|
|
|
if (!outpinp) {
|
2020-08-15 14:12:55 +00:00
|
|
|
exprrefp = nullptr; // Primary input only
|
2018-01-25 03:30:30 +00:00
|
|
|
} else {
|
|
|
|
// pinReconnect should have converted this
|
2018-02-02 02:32:58 +00:00
|
|
|
exprrefp = VN_CAST(outpinp->exprp(), VarRef);
|
2020-04-15 11:58:34 +00:00
|
|
|
if (!exprrefp) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported tristate port expression: "
|
|
|
|
<< nodep->exprp()->prettyTypeName());
|
2020-04-15 11:58:34 +00:00
|
|
|
}
|
2018-01-25 03:30:30 +00:00
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2018-01-25 03:30:30 +00:00
|
|
|
// pinReconnect should have converted this
|
2020-04-15 11:58:34 +00:00
|
|
|
exprrefp = VN_CAST(outAssignp->rhsp(),
|
|
|
|
VarRef); // This should be the same var as the output pin
|
|
|
|
if (!exprrefp) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported tristate port expression: "
|
|
|
|
<< nodep->exprp()->prettyTypeName());
|
2020-04-15 11:58:34 +00:00
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
if (exprrefp) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, "outref " << exprrefp << endl);
|
|
|
|
// Mark as now tristated; iteration will pick it up from there
|
|
|
|
exprrefp->user1p(enrefp);
|
2019-05-19 20:13:13 +00: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 03:30:30 +00:00
|
|
|
if (exprrefp) {
|
2018-10-14 22:39:33 +00:00
|
|
|
if (AstPull* pullp = static_cast<AstPull*>(nodep->modVarp()->user3p())) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, "propagate pull on " << exprrefp << endl);
|
2019-05-19 20:13:13 +00: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);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
iteratePinGuts(nodep);
|
|
|
|
}
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstVarRef* nodep) override {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_graphing) {
|
2020-11-07 15:37:55 +00:00
|
|
|
if (nodep->access().isWriteOrRW()) associateLogic(nodep, nodep->varp());
|
|
|
|
if (nodep->access().isReadOrRW()) associateLogic(nodep->varp(), nodep);
|
2019-05-19 20:13:13 +00: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 15:37:55 +00:00
|
|
|
if (nodep->access().isWriteOrRW() && m_tgraph.isTristate(nodep->varp())) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Ref-to-lvalue " << nodep << endl);
|
2020-11-07 15:37:55 +00:00
|
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Tristate unexpected on R/W access");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.didProcess(nodep);
|
|
|
|
mapInsertLhsVarRef(nodep);
|
2020-11-01 21:59:23 +00:00
|
|
|
} else if (nodep->access().isReadOnly()
|
2020-04-15 11:58:34 +00: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 11:50:21 +00:00
|
|
|
// Then propagate the enable from the original variable
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " Ref-to-tri " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
AstVar* enVarp = getCreateEnVarp(nodep->varp());
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->user1p(new AstVarRef(nodep->fileline(), enVarp, VAccess::READ));
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
if (m_alhs) {} // NOP; user1() already passed down from assignment
|
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstVar* nodep) override {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, dbgState() << nodep << endl);
|
2019-05-19 20:13:13 +00: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()) {
|
2020-09-07 21:09:25 +00:00
|
|
|
AstNode* newp = new AstPull(
|
|
|
|
nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, VAccess::WRITE),
|
|
|
|
nodep->isPullup());
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(9, " newpul " << newp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->addNextHere(newp);
|
|
|
|
// We'll iterate on the new AstPull later
|
|
|
|
}
|
2018-10-27 21:29:00 +00:00
|
|
|
if (nodep->isInoutish()
|
|
|
|
//|| 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 11:58:34 +00:00
|
|
|
) {
|
|
|
|
UINFO(9, " setTristate-inout " << nodep << endl);
|
2019-05-19 20:13:13 +00: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 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
2020-01-20 18:27:27 +00:00
|
|
|
UINFO(8, nodep << endl);
|
2020-08-25 01:10:43 +00:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
VL_RESTORER(m_graphing);
|
|
|
|
VL_RESTORER(m_unique);
|
|
|
|
VL_RESTORER(m_lhsmap);
|
2020-01-25 15:19:59 +00:00
|
|
|
// Not preserved, needs pointer instead: TristateGraph origTgraph = m_tgraph;
|
|
|
|
UASSERT_OBJ(m_tgraph.empty(), nodep, "Unsupported: NodeModule under NodeModule");
|
2019-05-19 20:13:13 +00:00
|
|
|
{
|
2020-01-20 18:27:27 +00:00
|
|
|
// Clear state
|
2019-05-19 20:13:13 +00:00
|
|
|
m_graphing = false;
|
2020-01-20 18:27:27 +00:00
|
|
|
m_tgraph.clear();
|
|
|
|
m_unique = 0;
|
2020-08-15 14:12:55 +00:00
|
|
|
m_logicp = nullptr;
|
2020-01-20 18:27:27 +00:00
|
|
|
m_lhsmap.clear();
|
|
|
|
m_modp = nodep;
|
|
|
|
// Walk the graph, finding all variables and tristate constructs
|
|
|
|
{
|
|
|
|
m_graphing = true;
|
|
|
|
iterateChildren(nodep);
|
|
|
|
m_graphing = false;
|
|
|
|
}
|
|
|
|
// Use graph to find tristate signals
|
|
|
|
m_tgraph.graphWalk(nodep);
|
|
|
|
// Build the LHS drivers map for this module
|
|
|
|
iterateChildren(nodep);
|
|
|
|
// Insert new logic for all tristates
|
|
|
|
insertTristates(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2020-01-25 15:19:59 +00:00
|
|
|
m_tgraph.clear(); // Recursion not supported
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstClass* nodep) override {
|
2020-04-05 13:30:23 +00:00
|
|
|
// don't deal with classes
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeFTask* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// don't deal with functions
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstCaseItem* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// don't deal with casez compare '???? values
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->bodysp());
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstCell* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_cellp = nodep;
|
|
|
|
m_alhs = false;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 14:12:55 +00:00
|
|
|
m_cellp = nullptr;
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNetlist* nodep) override { iterateChildrenBackwards(nodep); }
|
2009-01-06 16:03:57 +00:00
|
|
|
|
|
|
|
// Default: Just iterate
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNode* nodep) override {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
checkUnhandled(nodep);
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2019-09-12 11:22:22 +00:00
|
|
|
// CONSTRUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
explicit TristateVisitor(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_tgraph.clear();
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(nodep);
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|
2020-08-15 15:44:10 +00:00
|
|
|
virtual ~TristateVisitor() override {
|
2019-05-19 20:13:13 +00:00
|
|
|
V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs);
|
2012-04-22 01:45:28 +00:00
|
|
|
}
|
2009-01-06 16:03:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Tristate class functions
|
|
|
|
|
|
|
|
void V3Tristate::tristateAll(AstNetlist* nodep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
{ TristateVisitor visitor(nodep); } // Destruct before checking
|
2017-09-18 02:52:57 +00:00
|
|
|
V3Global::dumpCheckGlobalTree("tristate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2009-01-06 16:03:57 +00:00
|
|
|
}
|