Tristate: Major rework to support Z tieoffs, bug499, bug510.
This commit is contained in:
parent
b31a7cdcbf
commit
435a27b66a
|
|
@ -23,7 +23,26 @@
|
||||||
//
|
//
|
||||||
// Modify the design to expand tristate logic into its
|
// Modify the design to expand tristate logic into its
|
||||||
// corresponding two state reprasentation. At the lowest levels,
|
// corresponding two state reprasentation. At the lowest levels,
|
||||||
// expressions that have Z in them are converted into two state
|
//
|
||||||
|
// In detail:
|
||||||
|
//
|
||||||
|
// Over each module, from child to parent:
|
||||||
|
// Build a graph, connecting signals together so we can propagate tristates
|
||||||
|
// Variable becomes tristate with
|
||||||
|
// VAR->isInout
|
||||||
|
// VAR->isPullup/isPulldown (converted to AstPullup/AstPulldown
|
||||||
|
// BufIf0/1
|
||||||
|
// All variables on the LHS need to become tristate when there is:
|
||||||
|
// 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)
|
||||||
|
// 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
|
||||||
// drivers and corresponding output enable signals are generated.
|
// drivers and corresponding output enable signals are generated.
|
||||||
// These enable signals get transformed and regenerated through any
|
// These enable signals get transformed and regenerated through any
|
||||||
// logic that they may go through until they hit the module level. At
|
// logic that they may go through until they hit the module level. At
|
||||||
|
|
@ -52,6 +71,7 @@
|
||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
#include "V3Inst.h"
|
#include "V3Inst.h"
|
||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
|
#include "V3Graph.h"
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
|
|
@ -65,24 +85,229 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Graph support classes
|
||||||
|
|
||||||
|
class TristateVertex : public V3GraphVertex {
|
||||||
|
AstNode* m_nodep;
|
||||||
|
bool m_isTristate; // Logic indicates a tristate
|
||||||
|
bool m_feedsTri; // Propagates to a tristate node (on RHS)
|
||||||
|
bool m_processed; // Tristating was cleaned up
|
||||||
|
public:
|
||||||
|
TristateVertex(V3Graph* graphp, AstNode* nodep)
|
||||||
|
: V3GraphVertex(graphp)
|
||||||
|
, m_nodep(nodep), m_isTristate(false), m_feedsTri(false), m_processed(false) {}
|
||||||
|
virtual ~TristateVertex() {}
|
||||||
|
// Accessors
|
||||||
|
AstNode* nodep() const { return m_nodep; }
|
||||||
|
AstVar* varp() const { return nodep()->castVar(); }
|
||||||
|
virtual string name() const {
|
||||||
|
return ((isTristate() ? "tri\\n"
|
||||||
|
:feedsTri() ? "feed\\n" : "-\\n")
|
||||||
|
+(nodep()->prettyTypeName()+" "+cvtToStr((void*)nodep()))); }
|
||||||
|
virtual string dotColor() const {
|
||||||
|
return (varp()
|
||||||
|
? (isTristate() ? "darkblue"
|
||||||
|
:feedsTri() ? "blue" : "lightblue")
|
||||||
|
: (isTristate() ? "darkgreen"
|
||||||
|
:feedsTri() ? "green" : "lightgreen")); }
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
|
||||||
|
class TristateGraph {
|
||||||
|
// NODE STATE
|
||||||
|
// AstVar::user5p -> TristateVertex* for variable being built
|
||||||
|
//AstUser5InUse m_inuser5; // In visitor below
|
||||||
|
|
||||||
|
// TYPES
|
||||||
|
public:
|
||||||
|
typedef std::vector<AstVar*> VarVec;
|
||||||
|
private:
|
||||||
|
|
||||||
|
// MEMBERS
|
||||||
|
V3Graph m_graph; // Logic graph
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CONSTUCTORS
|
||||||
|
TristateGraph() { clear(); }
|
||||||
|
virtual ~TristateGraph() { clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// METHODS
|
||||||
|
static int debug() {
|
||||||
|
static int level = -1;
|
||||||
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
TristateVertex* makeVertex(AstNode* nodep) {
|
||||||
|
TristateVertex* vertexp = (TristateVertex*)(nodep->user5p());
|
||||||
|
if (!vertexp) {
|
||||||
|
UINFO(6," New vertex "<<nodep<<endl);
|
||||||
|
vertexp = new TristateVertex(&m_graph, nodep);
|
||||||
|
nodep->user5p(vertexp);
|
||||||
|
}
|
||||||
|
return vertexp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS - Graph optimization
|
||||||
|
void graphWalkRecurseFwd(TristateVertex* vtxp, int level) {
|
||||||
|
// 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
|
||||||
|
UINFO(9," Mark tri "<<level<<" "<<vtxp<<endl);
|
||||||
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
||||||
|
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||||
|
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);
|
||||||
|
graphWalkRecurseFwd(vvertexp, level+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A variable is tristated. Find all of the LHS VARREFs that drive this signal now need tristate drivers
|
||||||
|
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) {
|
||||||
|
TristateVertex* vvertexp = dynamic_cast<TristateVertex*>(edgep->fromp());
|
||||||
|
if (AstVarRef* refp = vvertexp->nodep()->castVarRef()) {
|
||||||
|
if (refp->lvalue()
|
||||||
|
// 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);
|
||||||
|
graphWalkRecurseFwd(vvertexp, level+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void graphWalkRecurseBack(TristateVertex* vtxp, int level) {
|
||||||
|
// 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
|
||||||
|
UINFO(9," Mark feedstri "<<level<<" "<<vtxp<<endl);
|
||||||
|
if (!vtxp->varp()) { // not a var where we stop the recursion
|
||||||
|
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) {
|
||||||
|
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);
|
||||||
|
graphWalkRecurseBack(vvertexp, level+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// METHODS
|
||||||
|
void clear() {
|
||||||
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||||
|
TristateVertex* vvertexp = static_cast<TristateVertex*>(itp);
|
||||||
|
if (vvertexp->isTristate() && !vvertexp->processed()) {
|
||||||
|
// Not v3errorSrc as no reason to stop the world
|
||||||
|
vvertexp->nodep()->v3error("Unsupported tristate construct (in graph; not converted): "<<vvertexp->nodep()->prettyTypeName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_graph.clear();
|
||||||
|
}
|
||||||
|
void graphWalk(AstNodeModule* nodep) {
|
||||||
|
//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()) {
|
||||||
|
graphWalkRecurseFwd((TristateVertex*)itp, 0);
|
||||||
|
}
|
||||||
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||||
|
graphWalkRecurseBack((TristateVertex*)itp, 0);
|
||||||
|
}
|
||||||
|
if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pos__"+nodep->name());
|
||||||
|
}
|
||||||
|
void setTristate(AstNode* nodep) {
|
||||||
|
makeVertex(nodep)->isTristate(true);
|
||||||
|
}
|
||||||
|
void associate(AstNode* fromp, AstNode* top) {
|
||||||
|
new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1);
|
||||||
|
}
|
||||||
|
bool isTristate(AstNode* nodep) {
|
||||||
|
TristateVertex* vertexp = (TristateVertex*)(nodep->user5p());
|
||||||
|
return vertexp && vertexp->isTristate();
|
||||||
|
}
|
||||||
|
bool feedsTri(AstNode* nodep) {
|
||||||
|
TristateVertex* vertexp = (TristateVertex*)(nodep->user5p());
|
||||||
|
return vertexp && vertexp->feedsTri();
|
||||||
|
}
|
||||||
|
void didProcess(AstNode* nodep) {
|
||||||
|
TristateVertex* vertexp = (TristateVertex*)(nodep->user5p());
|
||||||
|
if (!vertexp) {
|
||||||
|
// Not v3errorSrc as no reason to stop the world
|
||||||
|
nodep->v3error("Unsupported tristate constuct (not in propagation graph): "<<nodep->prettyTypeName());
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ITERATOR METHODS
|
||||||
|
VarVec tristateVars() {
|
||||||
|
// Return all tristate variables
|
||||||
|
VarVec v;
|
||||||
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||||
|
TristateVertex* vvertexp = static_cast<TristateVertex*>(itp);
|
||||||
|
if (vvertexp->isTristate()) {
|
||||||
|
if (AstVar* nodep = vvertexp->nodep()->castVar()) {
|
||||||
|
v.push_back(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Given a node, flip any VarRef from LValue to RValue (i.e. make it an input)
|
// Given a node, flip any VarRef from LValue to RValue (i.e. make it an input)
|
||||||
// See also V3LinkLValue::linkLValueSet
|
// See also V3LinkLValue::linkLValueSet
|
||||||
|
|
||||||
class TristatePinVisitor : public TristateBaseVisitor {
|
class TristatePinVisitor : public TristateBaseVisitor {
|
||||||
|
TristateGraph& m_tgraph;
|
||||||
|
bool m_lvalue; // Flip to be an LVALUE
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||||
if (nodep->lvalue()) {
|
if (m_lvalue && !nodep->lvalue()) {
|
||||||
|
UINFO(9," Flip-to-LValue "<<nodep<<endl);
|
||||||
|
nodep->lvalue(true);
|
||||||
|
} else if (!m_lvalue && nodep->lvalue()) {
|
||||||
UINFO(9," Flip-to-RValue "<<nodep<<endl);
|
UINFO(9," Flip-to-RValue "<<nodep<<endl);
|
||||||
nodep->lvalue(false);
|
nodep->lvalue(false);
|
||||||
|
// Mark the ex-output as tristated
|
||||||
|
UINFO(9," setTristate-subpin "<<nodep->varp()<<endl);
|
||||||
|
m_tgraph.setTristate(nodep->varp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstArraySel* nodep, AstNUser*) {
|
||||||
|
// Doesn't work because we'd set lvalue on the array index's var
|
||||||
|
if (m_lvalue) nodep->v3fatalSrc("ArraySel conversion to output, under tristate node");
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTUCTORS
|
||||||
TristatePinVisitor(AstNode* nodep) {
|
TristatePinVisitor(AstNode* nodep, TristateGraph& tgraph, bool lvalue)
|
||||||
|
: m_tgraph(tgraph), m_lvalue(lvalue) {
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
}
|
}
|
||||||
virtual ~TristatePinVisitor() {}
|
virtual ~TristatePinVisitor() {}
|
||||||
|
|
@ -93,38 +318,50 @@ public:
|
||||||
class TristateVisitor : public TristateBaseVisitor {
|
class TristateVisitor : public TristateBaseVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// *::user1p -> pointer to output enable __en expressions
|
// *::user1p -> pointer to output enable __en expressions
|
||||||
// AstVarRef::user2 -> bool - already visited
|
// *::user2 -> int - already visited, see U2_ enum
|
||||||
// AstVar::user2 -> bool - already visited
|
|
||||||
// AstPin::user2 -> bool - already visited
|
|
||||||
// AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p)
|
// AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p)
|
||||||
// AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p)
|
// AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p)
|
||||||
|
// See TristateGraph:
|
||||||
|
// AstVar::user5p -> TristateVertex* for variable being built
|
||||||
|
// AstStmt*::user5p -> TristateVertex* for this statement
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
AstUser2InUse m_inuser2;
|
AstUser2InUse m_inuser2;
|
||||||
AstUser3InUse m_inuser3;
|
AstUser3InUse m_inuser3;
|
||||||
AstUser4InUse m_inuser4;
|
AstUser4InUse m_inuser4;
|
||||||
|
AstUser5InUse m_inuser5;
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
typedef std::vector<AstVar*> VarVec;
|
|
||||||
typedef std::vector<AstVarRef*> RefVec;
|
typedef std::vector<AstVarRef*> RefVec;
|
||||||
typedef std::map<AstVar*, RefVec*> VarMap;
|
typedef std::map<AstVar*, RefVec*> VarMap;
|
||||||
|
enum { 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
|
||||||
|
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
|
bool m_graphing; // Major mode - creating graph
|
||||||
|
//
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Current module
|
||||||
AstCell* m_cellp; // current cell
|
AstCell* m_cellp; // current cell
|
||||||
VarMap m_lhsmap; // LHS driver map
|
VarMap m_lhsmap; // LHS driver map
|
||||||
VarVec m_varvec; // list of all vars for doing a final cleanup of inouts and undriven outputs that were not detected through finding Z logic in the module itself
|
|
||||||
int m_unique;
|
int m_unique;
|
||||||
bool m_alhs; // On LHS of assignment
|
bool m_alhs; // On LHS of assignment
|
||||||
|
AstNode* m_logicp; // Current logic being built
|
||||||
|
TristateGraph m_tgraph; // Logic graph
|
||||||
|
|
||||||
// STATS
|
// STATS
|
||||||
V3Double0 m_statTriSigs; // stat tracking
|
V3Double0 m_statTriSigs; // stat tracking
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
string dbgState() {
|
string dbgState() {
|
||||||
string o;
|
string o = (m_graphing?" gr ":" ng ");
|
||||||
if (m_alhs) o += "alhs ";
|
if (m_alhs) o += "alhs ";
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
void associateLogic(AstNode* fromp, AstNode* top) {
|
||||||
|
if (m_logicp) {
|
||||||
|
m_tgraph.associate(fromp, top);
|
||||||
|
}
|
||||||
|
}
|
||||||
AstNode* getEnp(AstNode* nodep) {
|
AstNode* getEnp(AstNode* nodep) {
|
||||||
// checks if user1p() is null, and if so, adds a constant output
|
// checks if user1p() is null, and if so, adds a constant output
|
||||||
// enable driver of all 1's. Otherwise returns the user1p() data.
|
// enable driver of all 1's. Otherwise returns the user1p() data.
|
||||||
|
|
@ -178,6 +415,19 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mapInsertLhsVarRef(AstVarRef* nodep) {
|
||||||
|
AstVar* key = nodep->varp();
|
||||||
|
VarMap::iterator it = m_lhsmap.find(key);
|
||||||
|
UINFO(9," mapInsertLhsVarRef "<<nodep<<endl);
|
||||||
|
if (it == m_lhsmap.end()) { // Not found
|
||||||
|
RefVec* refs = new RefVec();
|
||||||
|
refs->push_back(nodep);
|
||||||
|
m_lhsmap.insert(make_pair(key, refs));
|
||||||
|
} else {
|
||||||
|
(*it).second->push_back(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) {
|
AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) {
|
||||||
// Form a "deposit" instruction for given enable, using existing select as a template.
|
// Form a "deposit" instruction for given enable, using existing select as a template.
|
||||||
// Would be nicer if we made this a new AST type
|
// Would be nicer if we made this a new AST type
|
||||||
|
|
@ -219,14 +469,10 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
// Go through all the vars and find any that are outputs without drivers
|
// 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
|
// 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.
|
// them to the lhs map so they get expanded correctly.
|
||||||
for (VarVec::iterator ii = m_varvec.begin(); ii != m_varvec.end(); ++ii) {
|
TristateGraph::VarVec vars = m_tgraph.tristateVars();
|
||||||
|
for (TristateGraph::VarVec::iterator ii = vars.begin(); ii != vars.end(); ++ii) {
|
||||||
AstVar* varp = (*ii);
|
AstVar* varp = (*ii);
|
||||||
if (varp->isInout()
|
if (m_tgraph.isTristate(varp)) {
|
||||||
//|| 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"
|
|
||||||
) {
|
|
||||||
VarMap::iterator it = m_lhsmap.find(varp);
|
VarMap::iterator it = m_lhsmap.find(varp);
|
||||||
if (it == m_lhsmap.end()) {
|
if (it == m_lhsmap.end()) {
|
||||||
// set output enable to always be off on this assign statement so that this var is floating
|
// set output enable to always be off on this assign statement so that this var is floating
|
||||||
|
|
@ -236,9 +482,11 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstConst* constp = new AstConst(varp->fileline(), zeros);
|
AstConst* constp = new AstConst(varp->fileline(), zeros);
|
||||||
AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true);
|
AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true);
|
||||||
AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp);
|
AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp);
|
||||||
|
UINFO(9," newoev "<<newp<<endl);
|
||||||
|
varrefp->user1p(new AstConst(varp->fileline(),zeros));
|
||||||
nodep->addStmtp(newp);
|
nodep->addStmtp(newp);
|
||||||
visit(varrefp, NULL);
|
mapInsertLhsVarRef(varrefp); // insertTristates will convert
|
||||||
varrefp->user1p(new AstConst(varp->fileline(),zeros));//set output enable to always be off on this assign statement so that this var is floating
|
// // to a varref to the __out# variable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,18 +499,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
RefVec* refs = (*it).second;
|
RefVec* refs = (*it).second;
|
||||||
|
|
||||||
// Figure out if this var needs tristate expanded.
|
// Figure out if this var needs tristate expanded.
|
||||||
int needs_expanded = 0;
|
if (!m_tgraph.isTristate(invarp)) {
|
||||||
// If need enable signal gets expanded
|
|
||||||
if (invarp->user1p()) { needs_expanded++; }
|
|
||||||
// all inouts get expanded
|
|
||||||
if (invarp->isInout()) { needs_expanded++; }
|
|
||||||
// loop through to find all vars that have __en logic. They get expanded.
|
|
||||||
for (RefVec::iterator ii = refs->begin(); ii != refs->end(); ++ii) {
|
|
||||||
AstVarRef* refp = (*ii);
|
|
||||||
if (refp->user1p()) { needs_expanded++; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needs_expanded == 0) {
|
|
||||||
// This var has no tristate logic, so we leave it alone.
|
// This var has no tristate logic, so we leave it alone.
|
||||||
UINFO(8, " NO TRISTATE ON:" << invarp << endl);
|
UINFO(8, " NO TRISTATE ON:" << invarp << endl);
|
||||||
m_lhsmap.erase(invarp);
|
m_lhsmap.erase(invarp);
|
||||||
|
|
@ -271,6 +508,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_statTriSigs++;
|
m_statTriSigs++;
|
||||||
|
m_tgraph.didProcess(invarp);
|
||||||
UINFO(8, " TRISTATE EXPANDING:" << invarp << endl);
|
UINFO(8, " TRISTATE EXPANDING:" << invarp << endl);
|
||||||
|
|
||||||
// If the lhs var is a port, then we need to create ports for
|
// If the lhs var is a port, then we need to create ports for
|
||||||
|
|
@ -285,9 +523,10 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
// This var becomes an input
|
// This var becomes an input
|
||||||
invarp->varType2In(); // convert existing port to type input
|
invarp->varType2In(); // convert existing port to type input
|
||||||
// Create an output port (__out)
|
// Create an output port (__out)
|
||||||
AstVar* outvarp = getCreateOutVarp(invarp);
|
outvarp = getCreateOutVarp(invarp);
|
||||||
outvarp->varType2Out();
|
outvarp->varType2Out();
|
||||||
lhsp = outvarp; // Must assign to __out, not to normal input signal
|
lhsp = outvarp; // Must assign to __out, not to normal input signal
|
||||||
|
UINFO(9, " TRISTATE propagates up with "<<lhsp<<endl);
|
||||||
// Create an output enable port (__en)
|
// Create an output enable port (__en)
|
||||||
envarp = getCreateEnVarp(invarp); // May already be created if have foo === 1'bz somewhere
|
envarp = getCreateEnVarp(invarp); // May already be created if have foo === 1'bz somewhere
|
||||||
envarp->varType2Out();
|
envarp->varType2Out();
|
||||||
|
|
@ -324,8 +563,9 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstVarType::MODULETEMP,
|
AstVarType::MODULETEMP,
|
||||||
lhsp->name()+"__en"+cvtToStr(m_unique++),
|
lhsp->name()+"__en"+cvtToStr(m_unique++),
|
||||||
VFlagBitPacked(), w); // 2-state ok
|
VFlagBitPacked(), w); // 2-state ok
|
||||||
|
UINFO(9," newenp "<<newenp<<endl);
|
||||||
nodep->addStmtp(newenp);
|
nodep->addStmtp(newenp);
|
||||||
|
|
||||||
AstNode* enassp = new AstAssignW(refp->fileline(),
|
AstNode* enassp = new AstAssignW(refp->fileline(),
|
||||||
new AstVarRef(refp->fileline(), newenp, true),
|
new AstVarRef(refp->fileline(), newenp, true),
|
||||||
getEnp(refp));
|
getEnp(refp));
|
||||||
|
|
@ -380,6 +620,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
lhsp,
|
lhsp,
|
||||||
true),
|
true),
|
||||||
orp);
|
orp);
|
||||||
|
assp->user2(U2_BOTH); // Don't process further; already resolved
|
||||||
if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: ");
|
if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: ");
|
||||||
nodep->addStmtp(assp);
|
nodep->addStmtp(assp);
|
||||||
// Delete the map and vector list now that we have expanded it.
|
// Delete the map and vector list now that we have expanded it.
|
||||||
|
|
@ -391,6 +632,11 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstConst* nodep, AstNUser*) {
|
virtual void visit(AstConst* nodep, AstNUser*) {
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (m_graphing) {
|
||||||
|
if (!m_alhs && nodep->num().hasZ()) {
|
||||||
|
m_tgraph.setTristate(nodep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Detect any Z consts and convert them to 0's with an enable that is also 0.
|
// Detect any Z consts and convert them to 0's with an enable that is also 0.
|
||||||
if (m_alhs && nodep->user1p()) {
|
if (m_alhs && nodep->user1p()) {
|
||||||
// A pin with 1'b0 or similar connection results in an assign with constant on LHS
|
// A pin with 1'b0 or similar connection results in an assign with constant on LHS
|
||||||
|
|
@ -401,9 +647,9 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
UINFO(9," const->"<<newp<<endl);
|
UINFO(9," const->"<<newp<<endl);
|
||||||
nodep->replaceWith(newp);
|
nodep->replaceWith(newp);
|
||||||
pushDeletep(nodep); nodep = NULL;
|
pushDeletep(nodep); nodep = NULL;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (nodep->num().hasZ()) {
|
else if (m_tgraph.isTristate(nodep)) {
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
FileLine* fl = nodep->fileline();
|
FileLine* fl = nodep->fileline();
|
||||||
V3Number numz (fl,nodep->width()); numz.opBitsZ(nodep->num()); //Z->1, else 0
|
V3Number numz (fl,nodep->width()); numz.opBitsZ(nodep->num()); //Z->1, else 0
|
||||||
V3Number numz0(fl,nodep->width()); numz0.opNot(numz); // Z->0, else 1
|
V3Number numz0(fl,nodep->width()); numz0.opNot(numz); // Z->0, else 1
|
||||||
|
|
@ -415,8 +661,19 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
newconstp->user1p(enp);
|
newconstp->user1p(enp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visit(AstCond* nodep, AstNUser*) {
|
virtual void visit(AstCond* nodep, AstNUser*) {
|
||||||
|
if (m_graphing) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
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()) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
|
@ -431,6 +688,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstNode* expr1p = nodep->expr1p();
|
AstNode* expr1p = nodep->expr1p();
|
||||||
AstNode* expr2p = nodep->expr2p();
|
AstNode* expr2p = nodep->expr2p();
|
||||||
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
AstNode* en1p = getEnp(expr1p);
|
AstNode* en1p = getEnp(expr1p);
|
||||||
AstNode* en2p = getEnp(expr2p);
|
AstNode* en2p = getEnp(expr2p);
|
||||||
// The output enable of a cond is a cond of the output enable of the
|
// The output enable of a cond is a cond of the output enable of the
|
||||||
|
|
@ -442,8 +700,17 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
expr2p->user1p(NULL);
|
expr2p->user1p(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visit(AstSel* nodep, AstNUser*) {
|
virtual void visit(AstSel* nodep, AstNUser*) {
|
||||||
|
if (m_graphing) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
if (m_alhs) {
|
||||||
|
associateLogic(nodep, nodep->fromp());
|
||||||
|
} else {
|
||||||
|
associateLogic(nodep->fromp(), nodep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (m_alhs) {
|
if (m_alhs) {
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
if (nodep->user1p()) {
|
if (nodep->user1p()) {
|
||||||
|
|
@ -451,17 +718,39 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstNode* newp = newEnableDeposit(nodep, nodep->user1p()->castNode());
|
AstNode* newp = newEnableDeposit(nodep, nodep->user1p()->castNode());
|
||||||
nodep->fromp()->user1p(newp); // Push to varref (etc)
|
nodep->fromp()->user1p(newp); // Push to varref (etc)
|
||||||
if (debug()>=9) newp->dumpTree(cout,"-assign-sel; ");
|
if (debug()>=9) newp->dumpTree(cout,"-assign-sel; ");
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
}
|
}
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
} else {
|
} else {
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
if (nodep->fromp()->user1p() || nodep->lsbp()->user1p())
|
if (nodep->lsbp()->user1p()) {
|
||||||
nodep->v3error("Unsupported RHS tristate construct: "<<nodep->prettyTypeName());
|
nodep->v3error("Unsupported RHS tristate construct: "<<nodep->prettyTypeName());
|
||||||
}
|
}
|
||||||
|
if (nodep->fromp()->user1p()) { // SEL(VARREF,lsb)
|
||||||
|
AstNode* en1p = getEnp(nodep->fromp());
|
||||||
|
AstNode* enp = new AstSel(nodep->fileline(), en1p,
|
||||||
|
nodep->lsbp()->cloneTree(true), nodep->widthp()->cloneTree(true));
|
||||||
|
UINFO(9," newsel "<<enp<<endl);
|
||||||
|
nodep->user1p(enp);
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstConcat* nodep, AstNUser*) {
|
virtual void visit(AstConcat* nodep, AstNUser*) {
|
||||||
|
if (m_graphing) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
if (m_alhs) {
|
||||||
|
associateLogic(nodep, nodep->lhsp());
|
||||||
|
associateLogic(nodep, nodep->rhsp());
|
||||||
|
} else {
|
||||||
|
associateLogic(nodep->lhsp(), nodep);
|
||||||
|
associateLogic(nodep->rhsp(), nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (m_alhs) {
|
if (m_alhs) {
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
if (nodep->user1p()) {
|
if (nodep->user1p()) {
|
||||||
|
|
@ -476,6 +765,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
enp,
|
enp,
|
||||||
0,
|
0,
|
||||||
nodep->rhsp()->width()));
|
nodep->rhsp()->width()));
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
}
|
}
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -485,6 +775,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstNode* expr1p = nodep->lhsp();
|
AstNode* expr1p = nodep->lhsp();
|
||||||
AstNode* expr2p = nodep->rhsp();
|
AstNode* expr2p = nodep->rhsp();
|
||||||
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
if (expr1p->user1p() || expr2p->user1p()) { // else no tristates
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
AstNode* en1p = getEnp(expr1p);
|
AstNode* en1p = getEnp(expr1p);
|
||||||
AstNode* en2p = getEnp(expr2p);
|
AstNode* en2p = getEnp(expr2p);
|
||||||
AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p);
|
AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p);
|
||||||
|
|
@ -495,13 +786,19 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visit(AstBufIf1* nodep, AstNUser*) {
|
virtual void visit(AstBufIf1* nodep, AstNUser*) {
|
||||||
// For BufIf1, the enable is the LHS expression
|
// For BufIf1, the enable is the LHS expression
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (m_graphing) {
|
||||||
|
associateLogic(nodep->rhsp(), nodep);
|
||||||
|
m_tgraph.setTristate(nodep);
|
||||||
|
} else {
|
||||||
if (debug()>=9) nodep->backp()->dumpTree(cout,"-bufif: ");
|
if (debug()>=9) nodep->backp()->dumpTree(cout,"-bufif: ");
|
||||||
if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
AstNode* expr1p = nodep->lhsp()->unlinkFrBack();
|
AstNode* expr1p = nodep->lhsp()->unlinkFrBack();
|
||||||
AstNode* expr2p = nodep->rhsp()->unlinkFrBack();
|
AstNode* expr2p = nodep->rhsp()->unlinkFrBack();
|
||||||
AstNode* enp;
|
AstNode* enp;
|
||||||
|
|
@ -518,11 +815,15 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
UINFO(9," bufif enp="<<enp<<endl);
|
UINFO(9," bufif enp="<<enp<<endl);
|
||||||
pushDeletep(nodep); nodep = NULL;
|
pushDeletep(nodep); nodep = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void visitAndOr(AstNodeBiop* nodep, bool isAnd) {
|
void visitAndOr(AstNodeBiop* nodep, bool isAnd) {
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
{
|
if (m_graphing) {
|
||||||
|
associateLogic(nodep->lhsp(), nodep);
|
||||||
|
associateLogic(nodep->rhsp(), nodep);
|
||||||
|
} else {
|
||||||
if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
||||||
// ANDs and Z's have issues. Earlier optimizations convert
|
// ANDs and Z's have issues. Earlier optimizations convert
|
||||||
// expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we
|
// expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we
|
||||||
|
|
@ -542,6 +843,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
if (!expr1p->user1p() && !expr2p->user1p()) {
|
if (!expr1p->user1p() && !expr2p->user1p()) {
|
||||||
return; // no tristates in either expression, so nothing to do
|
return; // no tristates in either expression, so nothing to do
|
||||||
}
|
}
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
AstNode* en1p = getEnp(expr1p);
|
AstNode* en1p = getEnp(expr1p);
|
||||||
AstNode* en2p = getEnp(expr2p);
|
AstNode* en2p = getEnp(expr2p);
|
||||||
AstNode* subexpr1p = expr1p->cloneTree(false);
|
AstNode* subexpr1p = expr1p->cloneTree(false);
|
||||||
|
|
@ -560,6 +862,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
new AstAnd(nodep->fileline(),
|
new AstAnd(nodep->fileline(),
|
||||||
en2p->cloneTree(false),
|
en2p->cloneTree(false),
|
||||||
subexpr2p)));
|
subexpr2p)));
|
||||||
|
UINFO(9," neweqn "<<enp<<endl);
|
||||||
nodep->user1p(enp);
|
nodep->user1p(enp);
|
||||||
expr1p->user1p(NULL);
|
expr1p->user1p(NULL);
|
||||||
expr2p->user1p(NULL);
|
expr2p->user1p(NULL);
|
||||||
|
|
@ -573,8 +876,23 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitAssign(AstNodeAssign* nodep) {
|
void visitAssign(AstNodeAssign* nodep) {
|
||||||
|
if (m_graphing) {
|
||||||
|
if (nodep->user2() & U2_GRAPHING) return;
|
||||||
|
nodep->user2(U2_GRAPHING);
|
||||||
|
m_logicp = nodep;
|
||||||
nodep->rhsp()->iterateAndNext(*this);
|
nodep->rhsp()->iterateAndNext(*this);
|
||||||
UINFO(9," "<<nodep<<endl);
|
m_alhs = true;
|
||||||
|
nodep->lhsp()->iterateAndNext(*this);
|
||||||
|
m_alhs = false;
|
||||||
|
associateLogic(nodep->rhsp(), nodep);
|
||||||
|
associateLogic(nodep, nodep->lhsp());
|
||||||
|
m_logicp = NULL;
|
||||||
|
} else {
|
||||||
|
if (nodep->user2() & U2_NONGRAPH) return; // Iterated here, or created assignment to ignore
|
||||||
|
nodep->user2(U2_NONGRAPH);
|
||||||
|
nodep->rhsp()->iterateAndNext(*this);
|
||||||
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (debug()>=9) nodep->dumpTree(cout,"-assign: ");
|
||||||
// if the rhsp of this assign statement has an output enable driver,
|
// if the rhsp of this assign statement has an output enable driver,
|
||||||
// then propage the corresponding output enable assign statement.
|
// then propage the corresponding output enable assign statement.
|
||||||
// down the lvalue tree by recursion for eventual attachment to
|
// down the lvalue tree by recursion for eventual attachment to
|
||||||
|
|
@ -583,11 +901,13 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
nodep->lhsp()->user1p(nodep->rhsp()->user1p());
|
nodep->lhsp()->user1p(nodep->rhsp()->user1p());
|
||||||
nodep->rhsp()->user1p(NULL);
|
nodep->rhsp()->user1p(NULL);
|
||||||
UINFO(9," enp<-rhs "<<nodep->lhsp()->user1p()<<endl);
|
UINFO(9," enp<-rhs "<<nodep->lhsp()->user1p()<<endl);
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
}
|
}
|
||||||
m_alhs = true; // And user1p() will indicate tristate equation, if any
|
m_alhs = true; // And user1p() will indicate tristate equation, if any
|
||||||
nodep->lhsp()->iterateAndNext(*this);
|
nodep->lhsp()->iterateAndNext(*this);
|
||||||
m_alhs = false;
|
m_alhs = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||||
visitAssign(nodep);
|
visitAssign(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -596,6 +916,9 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitCaseEq(AstNodeBiop* nodep, bool neq) {
|
void visitCaseEq(AstNodeBiop* nodep, bool neq) {
|
||||||
|
if (m_graphing) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
} else {
|
||||||
checkUnhandled(nodep);
|
checkUnhandled(nodep);
|
||||||
// Unsupported: A === 3'b000 should compare with the enables, but we don't do
|
// 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.
|
// so at present, we only compare if there is a z in the equation.
|
||||||
|
|
@ -627,6 +950,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
checkUnhandled(nodep);
|
checkUnhandled(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstEqCase* nodep, AstNUser*) {
|
virtual void visit(AstEqCase* nodep, AstNUser*) {
|
||||||
visitCaseEq(nodep,false);
|
visitCaseEq(nodep,false);
|
||||||
}
|
}
|
||||||
|
|
@ -636,31 +960,53 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
|
|
||||||
virtual void visit(AstPull* nodep, AstNUser*) {
|
virtual void visit(AstPull* nodep, AstNUser*) {
|
||||||
UINFO(9,dbgState()<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (m_graphing) {
|
||||||
|
if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) {
|
||||||
|
lhsp->lvalue(true);
|
||||||
|
m_logicp = nodep;
|
||||||
|
m_tgraph.setTristate(nodep);
|
||||||
|
associateLogic(nodep, lhsp->varp());
|
||||||
|
m_logicp = NULL;
|
||||||
|
} else {
|
||||||
|
nodep->v3error("Unsupported pullup/down (weak driver) construct.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Replace any pullup/pulldowns with assignw logic and set the
|
// Replace any pullup/pulldowns with assignw logic and set the
|
||||||
// direction of the pull in the user2() data on the var. Given
|
// direction of the pull in the user3() data on the var. Given
|
||||||
// the complexity of merging tristate drivers at any level, the
|
// the complexity of merging tristate drivers at any level, the
|
||||||
// current limitation of this implementation is that a pullup/down
|
// current limitation of this implementation is that a pullup/down
|
||||||
// gets applied to all bits of a bus and a bus cannot have drivers
|
// gets applied to all bits of a bus and a bus cannot have drivers
|
||||||
// in opposite directions on indvidual pins.
|
// in opposite directions on indvidual pins.
|
||||||
if (nodep->lhsp()->castVarRef()) {
|
if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) {
|
||||||
AstVarRef* lhsp = nodep->lhsp()->unlinkFrBack()->castVarRef();
|
|
||||||
lhsp->lvalue(true);
|
lhsp->lvalue(true);
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
|
m_tgraph.didProcess(lhsp->varp());
|
||||||
AstVar* varp = lhsp->varp();
|
AstVar* varp = lhsp->varp();
|
||||||
setPullDirection(varp, nodep);
|
setPullDirection(varp, nodep);
|
||||||
V3Number zeros (nodep->fileline(), varp->width());
|
|
||||||
zeros.setAllBits0();
|
|
||||||
AstConst* constp = new AstConst(nodep->fileline(), zeros);
|
|
||||||
constp->user1p(new AstConst(nodep->fileline(), zeros));//set output enable to always be off on this assign statement.
|
|
||||||
AstAssignW* assp = new AstAssignW(nodep->fileline(), lhsp, constp);
|
|
||||||
nodep->replaceWith(assp);
|
|
||||||
assp->iterateChildren(*this);
|
|
||||||
if (!varp->user3p()) {
|
|
||||||
varp->user3p(nodep); //save off to indicate the pull direction
|
|
||||||
pushDeletep(nodep); nodep = NULL;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
nodep->v3error("Unsupported pullup/down (weak driver) construct.");
|
nodep->v3error("Unsupported pullup/down (weak driver) construct.");
|
||||||
}
|
}
|
||||||
|
nodep->unlinkFrBack();
|
||||||
|
pushDeletep(nodep); nodep = NULL; // Node must persist as user3p points to it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void iteratePinGuts(AstPin* nodep) {
|
||||||
|
if (m_graphing) {
|
||||||
|
m_logicp = nodep;
|
||||||
|
if (nodep->exprp()) {
|
||||||
|
associateLogic(nodep->exprp(), nodep);
|
||||||
|
associateLogic(nodep, nodep->exprp());
|
||||||
|
}
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
m_logicp = NULL;
|
||||||
|
} else {
|
||||||
|
// All heavy lifting completed in graph visitor.
|
||||||
|
if (nodep->exprp()) {
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
|
}
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .tri(SEL(trisig,x)) becomes
|
// .tri(SEL(trisig,x)) becomes
|
||||||
|
|
@ -682,29 +1028,42 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
// const inout Spec says illegal
|
// const inout Spec says illegal
|
||||||
// const output Unsupported; Illegal?
|
// const output Unsupported; Illegal?
|
||||||
virtual void visit(AstPin* nodep, AstNUser*) {
|
virtual void visit(AstPin* nodep, AstNUser*) {
|
||||||
UINFO(9," "<<nodep<<endl);
|
if (m_graphing) {
|
||||||
|
if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded
|
||||||
|
nodep->user2(U2_GRAPHING);
|
||||||
AstVar* enModVarp = (AstVar*) nodep->modVarp()->user1p();
|
AstVar* enModVarp = (AstVar*) nodep->modVarp()->user1p();
|
||||||
if (!enModVarp) { // no __en signals on this pin
|
if (!enModVarp) {
|
||||||
nodep->iterateChildren(*this);
|
iteratePinGuts(nodep);
|
||||||
return;
|
return; // No __en signals on this pin
|
||||||
}
|
}
|
||||||
if (nodep->user2()) { // this pin is already expanded
|
// Tristate exists:
|
||||||
return;
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
}
|
|
||||||
nodep->user2(true); // mark this pin already expanded
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout,"-pin-pre: ");
|
if (debug()>=9) nodep->dumpTree(cout,"-pin-pre: ");
|
||||||
|
|
||||||
// pinReconnectSimple needs to presume input or output behavior; we need both
|
// Empty/in-only; need Z to propagate
|
||||||
// Therefore, create the enable, output and separate input pin, then pinReconnectSimple all
|
bool inOnlyProcessing = (nodep->exprp()
|
||||||
|
&& nodep->modVarp()->isInOnly()
|
||||||
|
// 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()->isDeclOutput());
|
||||||
if (!nodep->exprp()) { // No-connect; covert to empty connection
|
if (!nodep->exprp()) { // No-connect; covert to empty connection
|
||||||
UINFO(5,"Unconnected pin terminate "<<nodep<<endl);
|
UINFO(5,"Unconnected pin terminate "<<nodep<<endl);
|
||||||
AstVar* ucVarp = getCreateUnconnVarp(nodep, nodep->modVarp()->dtypep());
|
AstVar* ucVarp = getCreateUnconnVarp(nodep, nodep->modVarp()->dtypep());
|
||||||
nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp,
|
nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp,
|
||||||
nodep->modVarp()->isOutput()));
|
nodep->modVarp()->isOutput()));
|
||||||
|
m_tgraph.setTristate(ucVarp);
|
||||||
// We don't need a driver on the wire; the lack of one will default to tristate
|
// We don't need a driver on the wire; the lack of one will default to tristate
|
||||||
|
} else if (inOnlyProcessing) { // 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.
|
||||||
|
AstAssignW* reAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, true, true);
|
||||||
|
UINFO(5,"Input pin buffering: "<<reAssignp<<endl);
|
||||||
|
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
|
// Create the output enable pin, connect to new signal
|
||||||
AstNode* enrefp;
|
AstNode* enrefp;
|
||||||
{
|
{
|
||||||
|
|
@ -712,13 +1071,15 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
AstVarType::MODULETEMP,
|
AstVarType::MODULETEMP,
|
||||||
nodep->name() + "__en" + cvtToStr(m_unique++),
|
nodep->name() + "__en" + cvtToStr(m_unique++),
|
||||||
VFlagLogicPacked(), enModVarp->width());
|
VFlagLogicPacked(), enModVarp->width());
|
||||||
|
UINFO(9," newenv "<<enVarp<<endl);
|
||||||
AstPin* enpinp = new AstPin(nodep->fileline(),
|
AstPin* enpinp = new AstPin(nodep->fileline(),
|
||||||
nodep->pinNum(),
|
nodep->pinNum(),
|
||||||
enModVarp->name(), // should be {var}"__en"
|
enModVarp->name(), // should be {var}"__en"
|
||||||
new AstVarRef(nodep->fileline(), enVarp, true));
|
new AstVarRef(nodep->fileline(), enVarp, true));
|
||||||
enpinp->modVarp(enModVarp);
|
enpinp->modVarp(enModVarp);
|
||||||
enpinp->user2(true); // mark this visited
|
UINFO(9," newpin "<<enpinp<<endl);
|
||||||
m_cellp->addPinsp(enpinp);
|
enpinp->user2(U2_BOTH); // don't iterate the pin later
|
||||||
|
nodep->addNextHere(enpinp);
|
||||||
m_modp->addStmtp(enVarp);
|
m_modp->addStmtp(enVarp);
|
||||||
enrefp = new AstVarRef(nodep->fileline(), enVarp, false);
|
enrefp = new AstVarRef(nodep->fileline(), enVarp, false);
|
||||||
UINFO(9," newvrf "<<enrefp<<endl);
|
UINFO(9," newvrf "<<enrefp<<endl);
|
||||||
|
|
@ -735,19 +1096,28 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
outModVarp->name(), // should be {var}"__out"
|
outModVarp->name(), // should be {var}"__out"
|
||||||
outexprp);
|
outexprp);
|
||||||
outpinp->modVarp(outModVarp);
|
outpinp->modVarp(outModVarp);
|
||||||
outpinp->user2(true); // mark this visited
|
UINFO(9," newpin "<<outpinp<<endl);
|
||||||
m_cellp->addPinsp(outpinp);
|
outpinp->user2(U2_BOTH); // don't iterate the pin later
|
||||||
|
nodep->addNextHere(outpinp);
|
||||||
// Simplify
|
// Simplify
|
||||||
|
if (inOnlyProcessing) { // Not an input that was a converted tristate
|
||||||
|
// The pin is an input, but we need an output
|
||||||
|
// The if() above is neded 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
|
||||||
|
TristatePinVisitor visitor (outexprp, m_tgraph, true);
|
||||||
|
}
|
||||||
if (debug()>=9) outpinp->dumpTree(cout,"-pin-opr: ");
|
if (debug()>=9) outpinp->dumpTree(cout,"-pin-opr: ");
|
||||||
outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, m_modp, true); // Note may change outpinp->exprp()
|
outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, m_modp, true); // Note may change outpinp->exprp()
|
||||||
if (debug()>=9) outpinp->dumpTree(cout,"-pin-out: ");
|
if (debug()>=9) outpinp->dumpTree(cout,"-pin-out: ");
|
||||||
if (debug()>=9 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: ");
|
if (debug()>=9 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: ");
|
||||||
|
// Must still iterate the outAssignp, as need to build output equation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Existing pin becomes an input
|
// Existing pin becomes an input, and we mark each resulting signal as tristate
|
||||||
TristatePinVisitor visitor (nodep->exprp());
|
TristatePinVisitor visitor (nodep->exprp(), m_tgraph, false);
|
||||||
V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, true); // Note may change nodep->exprp()
|
AstNode* inAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, true); // Note may change nodep->exprp()
|
||||||
if (debug()>=9) nodep->dumpTree(cout,"-pin-in: ");
|
if (debug()>=9) nodep->dumpTree(cout,"-pin-in: ");
|
||||||
|
if (debug()>=9 && inAssignp) inAssignp->dumpTree(cout,"-pin-as: ");
|
||||||
|
|
||||||
// Connect enable to output signal
|
// Connect enable to output signal
|
||||||
AstVarRef* outrefp;
|
AstVarRef* outrefp;
|
||||||
|
|
@ -757,71 +1127,122 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
outrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin
|
outrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin
|
||||||
}
|
}
|
||||||
if (!outrefp) { // deal with simple varref port
|
if (!outrefp) { // deal with simple varref port
|
||||||
|
// pinReconnect should have converted this
|
||||||
nodep->v3error("Unsupported tristate port expression: "<<nodep->exprp()->prettyTypeName());
|
nodep->v3error("Unsupported tristate port expression: "<<nodep->exprp()->prettyTypeName());
|
||||||
} else {
|
} else {
|
||||||
|
UINFO(9,"outref "<<outrefp<<endl);
|
||||||
outrefp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there
|
outrefp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there
|
||||||
visit(outrefp, NULL); // visit this var ref to get it in the varmap
|
if (!outAssignp) {
|
||||||
|
mapInsertLhsVarRef(outrefp); // insertTristates will convert
|
||||||
|
// // to a varref to the __out# variable
|
||||||
|
} // else the assignment deals with the connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propagate any pullups/pulldowns upwards if necessary
|
// Propagate any pullups/pulldowns upwards if necessary
|
||||||
if (outrefp) {
|
if (outrefp) {
|
||||||
if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) {
|
if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) {
|
||||||
UINFO(9, "propagate pull on "<<outrefp);
|
UINFO(9, "propagate pull on "<<outrefp<<endl);
|
||||||
//selp: Note we don't currently obey selects; all bits must be consistently pulled
|
|
||||||
setPullDirection(outrefp->varp(), pullp);
|
setPullDirection(outrefp->varp(), pullp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't need to visit the created assigns, as it was added at the end of the next links
|
// 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.
|
// 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);
|
||||||
|
UINFO(9," "<<nodep<<endl);
|
||||||
|
iteratePinGuts(nodep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||||
UINFO(9,(m_alhs?"alhs":"")<<" "<<nodep<<endl);
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (m_graphing) {
|
||||||
|
if (nodep->lvalue()) {
|
||||||
|
associateLogic(nodep, nodep->varp());
|
||||||
|
} else {
|
||||||
|
associateLogic(nodep->varp(), nodep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nodep->user2() & U2_NONGRAPH) return; // Processed
|
||||||
|
nodep->user2(U2_NONGRAPH);
|
||||||
// Detect all var lhs drivers and adds them to the
|
// Detect all var lhs drivers and adds them to the
|
||||||
// VarMap so that after the walk through the module we can expand
|
// VarMap so that after the walk through the module we can expand
|
||||||
// any tristate logic on the driver.
|
// any tristate logic on the driver.
|
||||||
if (nodep->lvalue() && !nodep->user2()) {
|
if (nodep->lvalue() && m_tgraph.isTristate(nodep->varp())) {
|
||||||
nodep->user2(true); // mark this ref as visited
|
UINFO(9," Ref-to-lvalue "<<nodep<<endl);
|
||||||
AstVar* key = nodep->varp();
|
m_tgraph.didProcess(nodep);
|
||||||
VarMap::iterator it = m_lhsmap.find(key);
|
mapInsertLhsVarRef(nodep);
|
||||||
if (it == m_lhsmap.end()) { // Not found
|
|
||||||
RefVec* refs = new RefVec();
|
|
||||||
refs->push_back(nodep);
|
|
||||||
m_lhsmap.insert(make_pair(key, refs));
|
|
||||||
} else {
|
|
||||||
(*it).second->push_back(nodep);
|
|
||||||
}
|
}
|
||||||
|
else if (!nodep->lvalue()
|
||||||
|
&& !nodep->user1p() // Not already processed, nor varref from visit(AstPin) creation
|
||||||
|
// 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)) {
|
||||||
|
// Then propage the enable from the original variable
|
||||||
|
UINFO(9," Ref-to-tri "<<nodep<<endl);
|
||||||
|
AstVar* enVarp = getCreateEnVarp(nodep->varp());
|
||||||
|
nodep->user1p(new AstVarRef(nodep->fileline(), enVarp, false));
|
||||||
}
|
}
|
||||||
if (m_alhs) {} // NOP; user1() already passed down from assignment
|
if (m_alhs) {} // NOP; user1() already passed down from assignment
|
||||||
nodep->iterateChildren(*this);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||||
if (nodep->user2SetOnce()) return; // Already processed
|
|
||||||
UINFO(9," "<<nodep<<endl);
|
|
||||||
// Adds all vars to the m_varvec list so that we can detect undriven
|
|
||||||
// inouts and output and make them drive Z.
|
|
||||||
m_varvec.push_back(nodep);
|
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
|
UINFO(9,dbgState()<<nodep<<endl);
|
||||||
|
if (m_graphing) {
|
||||||
// If tri0/1 force a pullup
|
// If tri0/1 force a pullup
|
||||||
|
if (nodep->user2() & U2_GRAPHING) return; // Already processed
|
||||||
|
nodep->user2(U2_GRAPHING);
|
||||||
if (nodep->isPulldown() || nodep->isPullup()) {
|
if (nodep->isPulldown() || nodep->isPullup()) {
|
||||||
AstNode* newp = new AstPull(nodep->fileline(),
|
AstNode* newp = new AstPull(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(), nodep, true),
|
new AstVarRef(nodep->fileline(), nodep, true),
|
||||||
nodep->isPullup());
|
nodep->isPullup());
|
||||||
UINFO(9,"New pull "<<newp);
|
UINFO(9," newpul "<<newp<<endl);
|
||||||
m_modp->addStmtp(newp);
|
nodep->addNextHere(newp);
|
||||||
// We'll iterate on the new AstPull later
|
// We'll iterate on the new AstPull later
|
||||||
}
|
}
|
||||||
|
if (nodep->isInout()
|
||||||
|
//|| 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"
|
||||||
|
) {
|
||||||
|
UINFO(9," setTristate-inout "<<nodep<<endl);
|
||||||
|
m_tgraph.setTristate(nodep);
|
||||||
|
}
|
||||||
|
} else { // !graphing
|
||||||
|
if (m_tgraph.isTristate(nodep)) {
|
||||||
|
// nodep->isPulldown() || nodep->isPullup() handled in TristateGraphVisitor
|
||||||
|
m_tgraph.didProcess(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||||
UINFO(8, nodep<<endl);
|
UINFO(8, nodep<<endl);
|
||||||
// expand tristate nodes and build the LHS drivers map for this module
|
if (m_graphing) nodep->v3fatalSrc("Modules under modules not supported"); // Lots of per-module state breaks
|
||||||
|
// Clear state
|
||||||
|
m_tgraph.clear();
|
||||||
m_unique = 0;
|
m_unique = 0;
|
||||||
|
m_logicp = false;
|
||||||
m_lhsmap.clear();
|
m_lhsmap.clear();
|
||||||
m_varvec.clear();
|
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
|
// Walk the graph, finding all variables and tristate constructs
|
||||||
|
{
|
||||||
|
m_graphing = true;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
m_graphing = false;
|
||||||
|
}
|
||||||
|
// Use graph to find tristate signals
|
||||||
|
m_tgraph.graphWalk(nodep);
|
||||||
// Build the LHS drivers map for this module
|
// Build the LHS drivers map for this module
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
// Insert new logic for all tristates
|
// Insert new logic for all tristates
|
||||||
|
|
@ -858,10 +1279,13 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTUCTORS
|
||||||
TristateVisitor(AstNode* nodep) {
|
TristateVisitor(AstNode* nodep) {
|
||||||
|
m_graphing = false;
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_cellp = NULL;
|
m_cellp = NULL;
|
||||||
m_unique = 0;
|
m_unique = 0;
|
||||||
m_alhs = false;
|
m_alhs = false;
|
||||||
|
m_logicp = false;
|
||||||
|
m_tgraph.clear();
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
}
|
}
|
||||||
virtual ~TristateVisitor() {
|
virtual ~TristateVisitor() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||||
|
# redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
|
||||||
|
# Compile only test
|
||||||
|
compile (
|
||||||
|
verilator_flags2 => ["-Wno-WIDTH"],
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// DESCRIPTION: Verilator: Test of selection with unsized Z.
|
||||||
|
//
|
||||||
|
// Test selecting Z when size is not explicit. Issue 510.
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2012 by Jeremy Bennett.
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
wire [1:0] b;
|
||||||
|
wire [1:0] c;
|
||||||
|
wire [0:0] d; // Explicit width due to issue 508
|
||||||
|
wire [0:0] e;
|
||||||
|
|
||||||
|
// This works if we use 1'bz, or 1'bx, but not with just 'bz or 'bx. It
|
||||||
|
// does require the tri-state Z. Since we get the same effect if b is
|
||||||
|
// dimensioned [0:0], this may be connected to issue 508.
|
||||||
|
assign b[1:0] = clk ? 2'bx : 'bz;
|
||||||
|
assign c[1:0] = clk ? 2'bz : 'bx;
|
||||||
|
assign d = clk ? 1'bx : 'bz;
|
||||||
|
assign e = clk ? 1'bz : 'bx;
|
||||||
|
|
||||||
|
endmodule // t
|
||||||
|
|
@ -16,39 +16,52 @@ module t (/*AUTOARG*/
|
||||||
wire z1 = 'z;
|
wire z1 = 'z;
|
||||||
wire z2 = 'z;
|
wire z2 = 'z;
|
||||||
wire z3 = 'z;
|
wire z3 = 'z;
|
||||||
|
wire tog = cyc[0];
|
||||||
|
|
||||||
// verilator lint_off PINMISSING
|
// verilator lint_off PINMISSING
|
||||||
t_tri0 tri0a (); // Error/warning
|
t_tri0 tri0a (.line(`__LINE__), .expval(1'b0)); // Pin missing
|
||||||
t_tri0 tri0b (.tn());
|
t_tri0 tri0b (.line(`__LINE__), .expval(1'b0), .tn());
|
||||||
t_tri0 tri0z (.tn(z0));
|
t_tri0 tri0z (.line(`__LINE__), .expval(1'b0), .tn(z0));
|
||||||
t_tri0 #(.EXPECT(1'b0)) tri0c (.tn(1'b0));
|
t_tri0 tri0Z (.line(`__LINE__), .expval(1'b0), .tn(1'bz));
|
||||||
t_tri0 #(.EXPECT(1'b1)) tri0d (.tn(1'b1)); // Warning would be reasonable given tri0 connect
|
t_tri0 tri0c (.line(`__LINE__), .expval(1'b0), .tn(1'b0));
|
||||||
t_tri0 #(.EXPECT(1'b0)) tri0e (.tn(~one));
|
t_tri0 tri0d (.line(`__LINE__), .expval(1'b1), .tn(1'b1)); // Warning would be reasonable given tri0 connect
|
||||||
t_tri0 #(.EXPECT(1'b1)) tri0f (.tn(one));
|
t_tri0 tri0e (.line(`__LINE__), .expval(1'b0), .tn(~one));
|
||||||
|
t_tri0 tri0f (.line(`__LINE__), .expval(1'b1), .tn(one));
|
||||||
|
t_tri0 tri0g (.line(`__LINE__), .expval(~cyc[0]), .tn(~tog));
|
||||||
|
t_tri0 tri0h (.line(`__LINE__), .expval(cyc[0]), .tn(tog));
|
||||||
|
|
||||||
t_tri1 tri1a ();
|
t_tri1 tri1a (.line(`__LINE__), .expval(1'b1)); // Pin missing
|
||||||
t_tri1 tri1b (.tn());
|
t_tri1 tri1b (.line(`__LINE__), .expval(1'b1), .tn());
|
||||||
t_tri1 tri1z (.tn(z1));
|
t_tri1 tri1z (.line(`__LINE__), .expval(1'b1), .tn(z1));
|
||||||
t_tri1 #(.EXPECT(1'b0)) tri1c (.tn(1'b0)); // Warning would be reasonable given tri1 connect
|
t_tri1 tri1Z (.line(`__LINE__), .expval(1'b1), .tn(1'bz));
|
||||||
t_tri1 #(.EXPECT(1'b1)) tri1d (.tn(1'b1));
|
t_tri1 tri1c (.line(`__LINE__), .expval(1'b0), .tn(1'b0)); // Warning would be reasonable given tri1 connect
|
||||||
t_tri1 #(.EXPECT(1'b0)) tri1e (.tn(~one));
|
t_tri1 tri1d (.line(`__LINE__), .expval(1'b1), .tn(1'b1));
|
||||||
t_tri1 #(.EXPECT(1'b1)) tri1f (.tn(one));
|
t_tri1 tri1e (.line(`__LINE__), .expval(1'b0), .tn(~one));
|
||||||
|
t_tri1 tri1f (.line(`__LINE__), .expval(1'b1), .tn(one));
|
||||||
|
t_tri1 tri1g (.line(`__LINE__), .expval(~cyc[0]), .tn(~tog));
|
||||||
|
t_tri1 tri1h (.line(`__LINE__), .expval(cyc[0]), .tn(tog));
|
||||||
|
|
||||||
t_tri2 tri2a ();
|
t_tri2 tri2a (.line(`__LINE__), .expval(1'b0)); // Pin missing
|
||||||
t_tri2 tri2b (.tn());
|
t_tri2 tri2b (.line(`__LINE__), .expval(1'b0), .tn());
|
||||||
t_tri2 tri2z (.tn(z2));
|
t_tri2 tri2z (.line(`__LINE__), .expval(1'b0), .tn(z2));
|
||||||
t_tri2 #(.EXPECT(1'b0)) tri2c (.tn(1'b0));
|
t_tri2 tri2Z (.line(`__LINE__), .expval(1'b0), .tn(1'bz));
|
||||||
t_tri2 #(.EXPECT(1'b1)) tri2d (.tn(1'b1));
|
t_tri2 tri2c (.line(`__LINE__), .expval(1'b0), .tn(1'b0));
|
||||||
t_tri2 #(.EXPECT(1'b0)) tri2e (.tn(~one));
|
t_tri2 tri2d (.line(`__LINE__), .expval(1'b1), .tn(1'b1));
|
||||||
t_tri2 #(.EXPECT(1'b1)) tri2f (.tn(one));
|
t_tri2 tri2e (.line(`__LINE__), .expval(1'b0), .tn(~one));
|
||||||
|
t_tri2 tri2f (.line(`__LINE__), .expval(1'b1), .tn(one));
|
||||||
|
t_tri2 tri2g (.line(`__LINE__), .expval(~cyc[0]), .tn(~tog));
|
||||||
|
t_tri2 tri2h (.line(`__LINE__), .expval(cyc[0]), .tn(tog));
|
||||||
|
|
||||||
t_tri3 tri3a ();
|
t_tri3 tri3a (.line(`__LINE__), .expval(1'b1)); // Pin missing
|
||||||
t_tri3 tri3b (.tn());
|
t_tri3 tri3b (.line(`__LINE__), .expval(1'b1), .tn());
|
||||||
t_tri3 tri3z (.tn(z3));
|
t_tri3 tri3z (.line(`__LINE__), .expval(1'b1), .tn(z3));
|
||||||
t_tri3 #(.EXPECT(1'b0)) tri3c (.tn(1'b0));
|
t_tri3 tri3Z (.line(`__LINE__), .expval(1'b1), .tn(1'bz));
|
||||||
t_tri3 #(.EXPECT(1'b1)) tri3d (.tn(1'b1));
|
t_tri3 tri3c (.line(`__LINE__), .expval(1'b0), .tn(1'b0));
|
||||||
t_tri3 #(.EXPECT(1'b0)) tri3e (.tn(~one));
|
t_tri3 tri3d (.line(`__LINE__), .expval(1'b1), .tn(1'b1));
|
||||||
t_tri3 #(.EXPECT(1'b1)) tri3f (.tn(one));
|
t_tri3 tri3e (.line(`__LINE__), .expval(1'b0), .tn(~one));
|
||||||
|
t_tri3 tri3f (.line(`__LINE__), .expval(1'b1), .tn(one));
|
||||||
|
t_tri3 tri3g (.line(`__LINE__), .expval(~cyc[0]), .tn(~tog));
|
||||||
|
t_tri3 tri3h (.line(`__LINE__), .expval(cyc[0]), .tn(tog));
|
||||||
// verilator lint_on PINMISSING
|
// verilator lint_on PINMISSING
|
||||||
|
|
||||||
// Test loop
|
// Test loop
|
||||||
|
|
@ -63,37 +76,49 @@ module t (/*AUTOARG*/
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module t_tri0
|
module t_tri0
|
||||||
#(parameter EXPECT=1'b0)
|
(line, expval, tn);
|
||||||
(tn);
|
input integer line;
|
||||||
|
input expval;
|
||||||
input tn; // Illegal to be inout; spec requires net connection to any inout
|
input tn; // Illegal to be inout; spec requires net connection to any inout
|
||||||
tri0 tn;
|
tri0 tn;
|
||||||
wire clk = t.clk;
|
wire clk = t.clk;
|
||||||
always @(posedge clk) if (tn !== EXPECT) $stop;
|
always @(posedge clk) if (tn !== expval) begin
|
||||||
|
$display("%%Error: from line %0d got=%x exp=%x",line,tn,expval); $stop;
|
||||||
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module t_tri1
|
module t_tri1
|
||||||
#(parameter EXPECT=1'b1)
|
(line, expval, tn);
|
||||||
(tn);
|
input integer line;
|
||||||
|
input expval;
|
||||||
input tn;
|
input tn;
|
||||||
tri1 tn;
|
tri1 tn;
|
||||||
wire clk = t.clk;
|
wire clk = t.clk;
|
||||||
always @(posedge clk) if (tn !== EXPECT) $stop;
|
always @(posedge clk) if (tn !== expval) begin
|
||||||
|
$display("%%Error: from line %0d got=%x exp=%x",line,tn,expval); $stop;
|
||||||
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module t_tri2
|
module t_tri2
|
||||||
#(parameter EXPECT=1'b0)
|
(line, expval, tn);
|
||||||
(tn);
|
input integer line;
|
||||||
|
input expval;
|
||||||
input tn;
|
input tn;
|
||||||
pulldown(tn);
|
pulldown(tn);
|
||||||
wire clk = t.clk;
|
wire clk = t.clk;
|
||||||
always @(posedge clk) if (tn !== EXPECT) $stop;
|
always @(posedge clk) if (tn !== expval) begin
|
||||||
|
$display("%%Error: from line %0d got=%x exp=%x",line,tn,expval); $stop;
|
||||||
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module t_tri3
|
module t_tri3
|
||||||
#(parameter EXPECT=1'b1)
|
(line, expval, tn);
|
||||||
(tn);
|
input integer line;
|
||||||
|
input expval;
|
||||||
input tn;
|
input tn;
|
||||||
pullup(tn);
|
pullup(tn);
|
||||||
wire clk = t.clk;
|
wire clk = t.clk;
|
||||||
always @(posedge clk) if (tn !== EXPECT) $stop;
|
always @(negedge clk) if (tn !== expval) begin
|
||||||
|
$display("%%Error: from line %0d got=%x exp=%x",line,tn,expval); $stop;
|
||||||
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue