diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index a9b7285911..74ec2b09d8 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -23,7 +23,26 @@ // // Modify the design to expand tristate logic into its // 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. // These enable signals get transformed and regenerated through any // logic that they may go through until they hit the module level. At @@ -52,6 +71,7 @@ #include "V3Stats.h" #include "V3Inst.h" #include "V3Stats.h" +#include "V3Graph.h" //###################################################################### @@ -65,24 +85,229 @@ class TristateBaseVisitor : public AstNVisitor { } }; +//###################################################################### +// 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 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 "<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 "<varp()) { // not a var where we stop the recursion + for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep=edgep->outNextp()) { + TristateVertex* vvertexp = dynamic_cast(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(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 "<varp()) { // not a var where we stop the recursion + for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TristateVertex* vvertexp = dynamic_cast(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(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): "<nodep()->prettyTypeName()); + } + } + m_graph.clear(); + } + void graphWalk(AstNodeModule* nodep) { + //if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pre__"+nodep->name()); + UINFO(9," Walking "<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): "<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(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) // See also V3LinkLValue::linkLValueSet class TristatePinVisitor : public TristateBaseVisitor { + TristateGraph& m_tgraph; + bool m_lvalue; // Flip to be an LVALUE // VISITORS virtual void visit(AstVarRef* nodep, AstNUser*) { - if (nodep->lvalue()) { + if (m_lvalue && !nodep->lvalue()) { + UINFO(9," Flip-to-LValue "<lvalue(true); + } else if (!m_lvalue && nodep->lvalue()) { UINFO(9," Flip-to-RValue "<lvalue(false); + // Mark the ex-output as tristated + UINFO(9," setTristate-subpin "<varp()<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*) { nodep->iterateChildren(*this); } public: // CONSTUCTORS - TristatePinVisitor(AstNode* nodep) { + TristatePinVisitor(AstNode* nodep, TristateGraph& tgraph, bool lvalue) + : m_tgraph(tgraph), m_lvalue(lvalue) { nodep->accept(*this); } virtual ~TristatePinVisitor() {} @@ -92,39 +317,51 @@ class TristatePinVisitor : public TristateBaseVisitor { class TristateVisitor : public TristateBaseVisitor { // NODE STATE - // *::user1p -> pointer to output enable __en expressions - // AstVarRef::user2 -> bool - already visited - // AstVar::user2 -> bool - already visited - // AstPin::user2 -> bool - already visited + // *::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) + // See TristateGraph: + // AstVar::user5p -> TristateVertex* for variable being built + // AstStmt*::user5p -> TristateVertex* for this statement AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; AstUser3InUse m_inuser3; AstUser4InUse m_inuser4; + AstUser5InUse m_inuser5; // TYPES - typedef std::vector VarVec; typedef std::vector RefVec; typedef std::map 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 + bool m_graphing; // Major mode - creating graph + // AstNodeModule* m_modp; // Current module AstCell* m_cellp; // current cell 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; bool m_alhs; // On LHS of assignment + AstNode* m_logicp; // Current logic being built + TristateGraph m_tgraph; // Logic graph // STATS V3Double0 m_statTriSigs; // stat tracking // METHODS string dbgState() { - string o; + string o = (m_graphing?" gr ":" ng "); if (m_alhs) o += "alhs "; return o; } + void associateLogic(AstNode* fromp, AstNode* top) { + if (m_logicp) { + m_tgraph.associate(fromp, top); + } + } AstNode* getEnp(AstNode* nodep) { // checks if user1p() is null, and if so, adds a constant output // enable driver of all 1's. Otherwise returns the user1p() data. @@ -178,6 +415,19 @@ class TristateVisitor : public TristateBaseVisitor { return newp; } + void mapInsertLhsVarRef(AstVarRef* nodep) { + AstVar* key = nodep->varp(); + VarMap::iterator it = m_lhsmap.find(key); + UINFO(9," mapInsertLhsVarRef "<push_back(nodep); + m_lhsmap.insert(make_pair(key, refs)); + } else { + (*it).second->push_back(nodep); + } + } + AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) { // Form a "deposit" instruction for given enable, using existing select as a template. // 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 // 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. - 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); - if (varp->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" - ) { + if (m_tgraph.isTristate(varp)) { VarMap::iterator it = m_lhsmap.find(varp); if (it == m_lhsmap.end()) { // 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); AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true); AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp); + UINFO(9," newoev "<user1p(new AstConst(varp->fileline(),zeros)); nodep->addStmtp(newp); - visit(varrefp, NULL); - varrefp->user1p(new AstConst(varp->fileline(),zeros));//set output enable to always be off on this assign statement so that this var is floating + mapInsertLhsVarRef(varrefp); // insertTristates will convert + // // to a varref to the __out# variable } } } @@ -251,18 +499,7 @@ class TristateVisitor : public TristateBaseVisitor { RefVec* refs = (*it).second; // Figure out if this var needs tristate expanded. - int needs_expanded = 0; - // 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) { + 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); @@ -271,6 +508,7 @@ class TristateVisitor : public TristateBaseVisitor { } 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 @@ -285,9 +523,10 @@ class TristateVisitor : public TristateBaseVisitor { // This var becomes an input invarp->varType2In(); // convert existing port to type input // Create an output port (__out) - AstVar* outvarp = getCreateOutVarp(invarp); + outvarp = getCreateOutVarp(invarp); outvarp->varType2Out(); lhsp = outvarp; // Must assign to __out, not to normal input signal + UINFO(9, " TRISTATE propagates up with "<varType2Out(); @@ -324,8 +563,9 @@ class TristateVisitor : public TristateBaseVisitor { AstVarType::MODULETEMP, lhsp->name()+"__en"+cvtToStr(m_unique++), VFlagBitPacked(), w); // 2-state ok - + UINFO(9," newenp "<addStmtp(newenp); + AstNode* enassp = new AstAssignW(refp->fileline(), new AstVarRef(refp->fileline(), newenp, true), getEnp(refp)); @@ -380,6 +620,7 @@ class TristateVisitor : public TristateBaseVisitor { lhsp, true), orp); + assp->user2(U2_BOTH); // Don't process further; already resolved if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: "); nodep->addStmtp(assp); // Delete the map and vector list now that we have expanded it. @@ -391,138 +632,198 @@ class TristateVisitor : public TristateBaseVisitor { // VISITORS virtual void visit(AstConst* nodep, AstNUser*) { UINFO(9,dbgState()<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()); - AstNode* newp = new AstVarRef(nodep->fileline(), varp, true); - UINFO(9," const->"<replaceWith(newp); - pushDeletep(nodep); nodep = NULL; - return; - } - if (nodep->num().hasZ()) { - FileLine* fl = nodep->fileline(); - 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 num1 (fl,nodep->width()); num1.opAnd(nodep->num(),numz0); // 01X->01X, Z->0 - AstConst* newconstp = new AstConst(fl, num1); - AstConst* enp = new AstConst(fl, numz0); - nodep->replaceWith(newconstp); - pushDeletep(nodep); nodep = NULL; - newconstp->user1p(enp); + 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. + 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()); + AstNode* newp = new AstVarRef(nodep->fileline(), varp, true); + UINFO(9," const->"<replaceWith(newp); + pushDeletep(nodep); nodep = NULL; + } + else if (m_tgraph.isTristate(nodep)) { + m_tgraph.didProcess(nodep); + FileLine* fl = nodep->fileline(); + 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 num1 (fl,nodep->width()); num1.opAnd(nodep->num(),numz0); // 01X->01X, Z->0 + AstConst* newconstp = new AstConst(fl, num1); + AstConst* enp = new AstConst(fl, numz0); + nodep->replaceWith(newconstp); + pushDeletep(nodep); nodep = NULL; + newconstp->user1p(enp); + } } } virtual void visit(AstCond* nodep, AstNUser*) { - if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } - nodep->iterateChildren(*this); - UINFO(9,dbgState()<condp(); - if (condp->user1p()) { - condp->v3error("Unsupported: don't know how to deal with tristate logic in the conditional expression"); - } - AstNode* expr1p = nodep->expr1p(); - AstNode* expr2p = nodep->expr2p(); - if (expr1p->user1p() || expr2p->user1p()) { // else no tristates - 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); - UINFO(9," newcond "<user1p(enp); - expr1p->user1p(NULL); - expr2p->user1p(NULL); + 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: "<prettyTypeName()); return; } + nodep->iterateChildren(*this); + UINFO(9,dbgState()<condp(); + if (condp->user1p()) { + condp->v3error("Unsupported: don't know how to deal with tristate logic in the conditional expression"); + } + 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); + UINFO(9," newcond "<user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } } } virtual void visit(AstSel* nodep, AstNUser*) { - if (m_alhs) { - UINFO(9,dbgState()<user1p()) { - // Form a "deposit" instruction. Would be nicer if we made this a new AST type - AstNode* newp = newEnableDeposit(nodep, nodep->user1p()->castNode()); - nodep->fromp()->user1p(newp); // Push to varref (etc) - if (debug()>=9) newp->dumpTree(cout,"-assign-sel; "); - } + if (m_graphing) { nodep->iterateChildren(*this); + if (m_alhs) { + associateLogic(nodep, nodep->fromp()); + } else { + associateLogic(nodep->fromp(), nodep); + } } else { - nodep->iterateChildren(*this); - UINFO(9,dbgState()<fromp()->user1p() || nodep->lsbp()->user1p()) - nodep->v3error("Unsupported RHS tristate construct: "<prettyTypeName()); + if (m_alhs) { + UINFO(9,dbgState()<user1p()) { + // Form a "deposit" instruction. Would be nicer if we made this a new AST type + AstNode* newp = newEnableDeposit(nodep, nodep->user1p()->castNode()); + nodep->fromp()->user1p(newp); // Push to varref (etc) + if (debug()>=9) newp->dumpTree(cout,"-assign-sel; "); + m_tgraph.didProcess(nodep); + } + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + UINFO(9,dbgState()<lsbp()->user1p()) { + nodep->v3error("Unsupported RHS tristate construct: "<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 "<user1p(enp); + m_tgraph.didProcess(nodep); + } + } } } virtual void visit(AstConcat* nodep, AstNUser*) { - if (m_alhs) { - UINFO(9,dbgState()<user1p()) { - // Each half of the concat gets a select of the enable expression - AstNode* enp = nodep->user1p()->castNode(); - nodep->user1p(NULL); - nodep->lhsp()->user1p(new AstSel(nodep->fileline(), - enp->cloneTree(true), - nodep->rhsp()->width(), - nodep->lhsp()->width())); - nodep->rhsp()->user1p(new AstSel(nodep->fileline(), - enp, - 0, - nodep->rhsp()->width())); - } + if (m_graphing) { nodep->iterateChildren(*this); - } else { - nodep->iterateChildren(*this); - UINFO(9,dbgState()<lhsp(); - AstNode* expr2p = nodep->rhsp(); - if (expr1p->user1p() || expr2p->user1p()) { // else no tristates - AstNode* en1p = getEnp(expr1p); - AstNode* en2p = getEnp(expr2p); - AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p); - UINFO(9," newconc "<user1p(enp); - expr1p->user1p(NULL); - expr2p->user1p(NULL); + if (m_alhs) { + associateLogic(nodep, nodep->lhsp()); + associateLogic(nodep, nodep->rhsp()); + } else { + associateLogic(nodep->lhsp(), nodep); + associateLogic(nodep->rhsp(), nodep); } - } + } + else { + if (m_alhs) { + UINFO(9,dbgState()<user1p()) { + // Each half of the concat gets a select of the enable expression + AstNode* enp = nodep->user1p()->castNode(); + nodep->user1p(NULL); + nodep->lhsp()->user1p(new AstSel(nodep->fileline(), + enp->cloneTree(true), + nodep->rhsp()->width(), + nodep->lhsp()->width())); + nodep->rhsp()->user1p(new AstSel(nodep->fileline(), + enp, + 0, + nodep->rhsp()->width())); + m_tgraph.didProcess(nodep); + } + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + UINFO(9,dbgState()<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); + UINFO(9," newconc "<user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + } + } } virtual void visit(AstBufIf1* nodep, AstNUser*) { // For BufIf1, the enable is the LHS expression nodep->iterateChildren(*this); UINFO(9,dbgState()<=9) nodep->backp()->dumpTree(cout,"-bufif: "); - if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } - AstNode* expr1p = nodep->lhsp()->unlinkFrBack(); - AstNode* expr2p = nodep->rhsp()->unlinkFrBack(); - AstNode* enp; - if (AstNode* en2p = expr2p->user1p()->castNode()) { - enp = new AstAnd(nodep->fileline(), expr1p, en2p); + if (m_graphing) { + associateLogic(nodep->rhsp(), nodep); + m_tgraph.setTristate(nodep); } else { - enp = expr1p; + if (debug()>=9) nodep->backp()->dumpTree(cout,"-bufif: "); + if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + m_tgraph.didProcess(nodep); + AstNode* expr1p = nodep->lhsp()->unlinkFrBack(); + AstNode* expr2p = nodep->rhsp()->unlinkFrBack(); + AstNode* enp; + if (AstNode* en2p = expr2p->user1p()->castNode()) { + enp = new AstAnd(nodep->fileline(), expr1p, en2p); + } else { + enp = expr1p; + } + expr1p->user1p(NULL); + expr2p->user1p(enp); // Becomes new node + // Don't need the BufIf any more, can just have the data direct + nodep->replaceWith(expr2p); + UINFO(9," bufif datap="<prettyTypeName()); return; } // ANDs and Z's have issues. Earlier optimizations convert // 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()) { 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); @@ -560,6 +862,7 @@ class TristateVisitor : public TristateBaseVisitor { new AstAnd(nodep->fileline(), en2p->cloneTree(false), subexpr2p))); + UINFO(9," neweqn "<user1p(enp); expr1p->user1p(NULL); expr2p->user1p(NULL); @@ -573,20 +876,37 @@ class TristateVisitor : public TristateBaseVisitor { } void visitAssign(AstNodeAssign* nodep) { - nodep->rhsp()->iterateAndNext(*this); - UINFO(9," "<rhsp()->user1p()) { - nodep->lhsp()->user1p(nodep->rhsp()->user1p()); - nodep->rhsp()->user1p(NULL); - UINFO(9," enp<-rhs "<lhsp()->user1p()<lhsp()->iterateAndNext(*this); - m_alhs = false; + if (m_graphing) { + if (nodep->user2() & U2_GRAPHING) return; + nodep->user2(U2_GRAPHING); + m_logicp = nodep; + nodep->rhsp()->iterateAndNext(*this); + 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()<=9) nodep->dumpTree(cout,"-assign: "); + // if the rhsp of this assign statement has an output enable driver, + // then propage the corresponding output enable assign statement. + // 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()); + nodep->rhsp()->user1p(NULL); + UINFO(9," enp<-rhs "<lhsp()->user1p()<lhsp()->iterateAndNext(*this); + m_alhs = false; + } } virtual void visit(AstAssignW* nodep, AstNUser*) { visitAssign(nodep); @@ -596,35 +916,39 @@ class TristateVisitor : public TristateBaseVisitor { } void visitCaseEq(AstNodeBiop* nodep, bool neq) { - 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 then - // away later when we determine the signal has no tristate - nodep->iterateChildren(*this); - UINFO(9,dbgState()<lhsp()->castConst(); // Constification always moves const to LHS - AstVarRef* varrefp = nodep->rhsp()->castVarRef(); // Input variable - if (constp && constp->user1p() && varrefp) { - // 3'b1z0 -> ((3'b101 == __en) && (3'b100 == __in)) - varrefp->unlinkFrBack(); - FileLine* fl = nodep->fileline(); - V3Number oneIfEn = constp->user1p()->castNode()->castConst()->num(); // visit(AstConst) already split into en/ones - V3Number oneIfEnOne = constp->num(); - AstVar* envarp = getCreateEnVarp(varrefp->varp()); - AstNode* newp = new AstLogAnd (fl, new AstEq (fl, new AstConst(fl, oneIfEn), - new AstVarRef(fl, envarp, false)), - // Keep the caseeq if there are X's present - new AstEqCase(fl, new AstConst(fl, oneIfEnOne), - varrefp)); - if (neq) newp = new AstLogNot(fl, newp); - UINFO(9," newceq "<=9) nodep->dumpTree(cout,"-caseeq-old: "); - if (debug()>=9) newp->dumpTree(cout,"-caseeq-new: "); - nodep->replaceWith(newp); - pushDeletep(nodep); nodep=NULL; + if (m_graphing) { + nodep->iterateChildren(*this); } 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 then + // away later when we determine the signal has no tristate + nodep->iterateChildren(*this); + UINFO(9,dbgState()<lhsp()->castConst(); // Constification always moves const to LHS + AstVarRef* varrefp = nodep->rhsp()->castVarRef(); // Input variable + if (constp && constp->user1p() && varrefp) { + // 3'b1z0 -> ((3'b101 == __en) && (3'b100 == __in)) + varrefp->unlinkFrBack(); + FileLine* fl = nodep->fileline(); + V3Number oneIfEn = constp->user1p()->castNode()->castConst()->num(); // visit(AstConst) already split into en/ones + V3Number oneIfEnOne = constp->num(); + AstVar* envarp = getCreateEnVarp(varrefp->varp()); + AstNode* newp = new AstLogAnd (fl, new AstEq (fl, new AstConst(fl, oneIfEn), + new AstVarRef(fl, envarp, false)), + // Keep the caseeq if there are X's present + new AstEqCase(fl, new AstConst(fl, oneIfEnOne), + varrefp)); + if (neq) newp = new AstLogNot(fl, newp); + UINFO(9," newceq "<=9) nodep->dumpTree(cout,"-caseeq-old: "); + if (debug()>=9) newp->dumpTree(cout,"-caseeq-new: "); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + } else { + checkUnhandled(nodep); + } } } virtual void visit(AstEqCase* nodep, AstNUser*) { @@ -636,30 +960,52 @@ class TristateVisitor : public TristateBaseVisitor { virtual void visit(AstPull* nodep, AstNUser*) { UINFO(9,dbgState()<lhsp()->castVarRef()) { - AstVarRef* lhsp = nodep->lhsp()->unlinkFrBack()->castVarRef(); - lhsp->lvalue(true); - AstVar* varp = lhsp->varp(); - 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; + 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 + // 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 + // in opposite directions on indvidual pins. + if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) { + lhsp->lvalue(true); + m_tgraph.didProcess(nodep); + m_tgraph.didProcess(lhsp->varp()); + AstVar* varp = lhsp->varp(); + setPullDirection(varp, nodep); + } else { + 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 { - nodep->v3error("Unsupported pullup/down (weak driver) construct."); + // All heavy lifting completed in graph visitor. + if (nodep->exprp()) { + m_tgraph.didProcess(nodep); + } + nodep->iterateChildren(*this); } } @@ -682,146 +1028,221 @@ class TristateVisitor : public TristateBaseVisitor { // const inout Spec says illegal // const output Unsupported; Illegal? virtual void visit(AstPin* nodep, AstNUser*) { - UINFO(9," "<modVarp()->user1p(); - if (!enModVarp) { // no __en signals on this pin - nodep->iterateChildren(*this); - return; - } - if (nodep->user2()) { // this pin is already expanded - return; - } - nodep->user2(true); // mark this pin already expanded - if (debug()>=9) nodep->dumpTree(cout,"-pin-pre: "); + if (m_graphing) { + if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded + nodep->user2(U2_GRAPHING); + AstVar* enModVarp = (AstVar*) nodep->modVarp()->user1p(); + if (!enModVarp) { + iteratePinGuts(nodep); + return; // No __en signals on this pin + } + // Tristate exists: + UINFO(9,dbgState()<=9) nodep->dumpTree(cout,"-pin-pre: "); - // pinReconnectSimple needs to presume input or output behavior; we need both - // Therefore, create the enable, output and separate input pin, then pinReconnectSimple all + // Empty/in-only; need Z to propagate + 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 + UINFO(5,"Unconnected pin terminate "<modVarp()->dtypep()); + nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp, + nodep->modVarp()->isOutput())); + m_tgraph.setTristate(ucVarp); + // 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: "<lhsp()); + } - if (!nodep->exprp()) { // No-connect; covert to empty connection - UINFO(5,"Unconnected pin terminate "<modVarp()->dtypep()); - nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp, - nodep->modVarp()->isOutput())); - // We don't need a driver on the wire; the lack of one will default to tristate - } + // 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; - { - AstVar* enVarp = new AstVar(nodep->fileline(), - AstVarType::MODULETEMP, - nodep->name() + "__en" + cvtToStr(m_unique++), - VFlagLogicPacked(), enModVarp->width()); - AstPin* enpinp = new AstPin(nodep->fileline(), - nodep->pinNum(), - enModVarp->name(), // should be {var}"__en" - new AstVarRef(nodep->fileline(), enVarp, true)); - enpinp->modVarp(enModVarp); - enpinp->user2(true); // mark this visited - m_cellp->addPinsp(enpinp); - m_modp->addStmtp(enVarp); - enrefp = new AstVarRef(nodep->fileline(), enVarp, false); - UINFO(9," newvrf "<=9) enpinp->dumpTree(cout,"-pin-ena: "); - } - // Create new output pin - AstAssignW* outAssignp = NULL; // If reconnected, the related assignment - AstPin* outpinp; - { - AstVar* outModVarp = (AstVar*) nodep->modVarp()->user4p(); - AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set - outpinp = new AstPin(nodep->fileline(), - nodep->pinNum(), - outModVarp->name(), // should be {var}"__out" - outexprp); - outpinp->modVarp(outModVarp); - outpinp->user2(true); // mark this visited - m_cellp->addPinsp(outpinp); - // Simplify - if (debug()>=9) outpinp->dumpTree(cout,"-pin-opr: "); - 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 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: "); - } - - // Existing pin becomes an input - TristatePinVisitor visitor (nodep->exprp()); - V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, true); // Note may change nodep->exprp() - if (debug()>=9) nodep->dumpTree(cout,"-pin-in: "); - - // Connect enable to output signal - AstVarRef* outrefp; - if (!outAssignp) { - outrefp = outpinp->exprp()->castVarRef(); - } else { - outrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin - } - if (!outrefp) { // deal with simple varref port - nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); - } else { - 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 - } + // Create the output enable pin, connect to new signal + AstNode* enrefp; + { + AstVar* enVarp = new AstVar(nodep->fileline(), + AstVarType::MODULETEMP, + nodep->name() + "__en" + cvtToStr(m_unique++), + VFlagLogicPacked(), enModVarp->width()); + UINFO(9," newenv "<fileline(), + nodep->pinNum(), + enModVarp->name(), // should be {var}"__en" + new AstVarRef(nodep->fileline(), enVarp, true)); + enpinp->modVarp(enModVarp); + UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later + nodep->addNextHere(enpinp); + m_modp->addStmtp(enVarp); + enrefp = new AstVarRef(nodep->fileline(), enVarp, false); + UINFO(9," newvrf "<=9) enpinp->dumpTree(cout,"-pin-ena: "); + } + // Create new output pin + AstAssignW* outAssignp = NULL; // If reconnected, the related assignment + AstPin* outpinp; + { + AstVar* outModVarp = (AstVar*) nodep->modVarp()->user4p(); + AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set + outpinp = new AstPin(nodep->fileline(), + nodep->pinNum(), + outModVarp->name(), // should be {var}"__out" + outexprp); + outpinp->modVarp(outModVarp); + UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later + nodep->addNextHere(outpinp); + // 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: "); + 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 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: "); + // Must still iterate the outAssignp, as need to build output equation + } - // Propagate any pullups/pulldowns upwards if necessary - if (outrefp) { - if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) { - UINFO(9, "propagate pull on "<varp(), pullp); + // Existing pin becomes an input, and we mark each resulting signal as tristate + TristatePinVisitor visitor (nodep->exprp(), m_tgraph, false); + 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 && inAssignp) inAssignp->dumpTree(cout,"-pin-as: "); + + // Connect enable to output signal + AstVarRef* outrefp; + if (!outAssignp) { + outrefp = outpinp->exprp()->castVarRef(); + } else { + outrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin + } + if (!outrefp) { // deal with simple varref port + // pinReconnect should have converted this + nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); + } else { + UINFO(9,"outref "<user1p(enrefp); // Mark as now tristated; iteration will pick it up from there + 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 + if (outrefp) { + if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) { + UINFO(9, "propagate pull on "<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. + // 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); + UINFO(9," "<lvalue() && !nodep->user2()) { - nodep->user2(true); // mark this ref as visited - AstVar* key = nodep->varp(); - VarMap::iterator it = m_lhsmap.find(key); - if (it == m_lhsmap.end()) { // Not found - RefVec* refs = new RefVec(); - refs->push_back(nodep); - m_lhsmap.insert(make_pair(key, refs)); + UINFO(9,dbgState()<lvalue()) { + associateLogic(nodep, nodep->varp()); } else { - (*it).second->push_back(nodep); + 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 + // VarMap so that after the walk through the module we can expand + // any tristate logic on the driver. + if (nodep->lvalue() && m_tgraph.isTristate(nodep->varp())) { + UINFO(9," Ref-to-lvalue "<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 "<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*) { - if (nodep->user2SetOnce()) return; // Already processed - UINFO(9," "<iterateChildren(*this); - // If tri0/1 force a pullup - if (nodep->isPulldown() || nodep->isPullup()) { - AstNode* newp = new AstPull(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, true), - nodep->isPullup()); - UINFO(9,"New pull "<addStmtp(newp); - // We'll iterate on the new AstPull later + UINFO(9,dbgState()<user2() & U2_GRAPHING) return; // Already processed + nodep->user2(U2_GRAPHING); + if (nodep->isPulldown() || nodep->isPullup()) { + AstNode* newp = new AstPull(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + nodep->isPullup()); + UINFO(9," newpul "<addNextHere(newp); + // 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 "<isPulldown() || nodep->isPullup() handled in TristateGraphVisitor + m_tgraph.didProcess(nodep); + } } } virtual void visit(AstNodeModule* nodep, AstNUser*) { UINFO(8, nodep<v3fatalSrc("Modules under modules not supported"); // Lots of per-module state breaks + // Clear state + m_tgraph.clear(); m_unique = 0; + m_logicp = false; m_lhsmap.clear(); - m_varvec.clear(); 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 nodep->iterateChildren(*this); // Insert new logic for all tristates @@ -858,10 +1279,13 @@ class TristateVisitor : public TristateBaseVisitor { public: // CONSTUCTORS TristateVisitor(AstNode* nodep) { + m_graphing = false; m_modp = NULL; m_cellp = NULL; m_unique = 0; m_alhs = false; + m_logicp = false; + m_tgraph.clear(); nodep->accept(*this); } virtual ~TristateVisitor() { diff --git a/test_regress/t/t_tri_select_unsized.pl b/test_regress/t/t_tri_select_unsized.pl new file mode 100755 index 0000000000..2051c31227 --- /dev/null +++ b/test_regress/t/t_tri_select_unsized.pl @@ -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; diff --git a/test_regress/t/t_tri_select_unsized.v b/test_regress/t/t_tri_select_unsized.v new file mode 100644 index 0000000000..2e2dbde5f8 --- /dev/null +++ b/test_regress/t/t_tri_select_unsized.v @@ -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 diff --git a/test_regress/t/t_tri_unconn.v b/test_regress/t/t_tri_unconn.v index 074a294627..d90db96d71 100644 --- a/test_regress/t/t_tri_unconn.v +++ b/test_regress/t/t_tri_unconn.v @@ -16,39 +16,52 @@ module t (/*AUTOARG*/ wire z1 = 'z; wire z2 = 'z; wire z3 = 'z; + wire tog = cyc[0]; // verilator lint_off PINMISSING - t_tri0 tri0a (); // Error/warning - t_tri0 tri0b (.tn()); - t_tri0 tri0z (.tn(z0)); - t_tri0 #(.EXPECT(1'b0)) tri0c (.tn(1'b0)); - t_tri0 #(.EXPECT(1'b1)) tri0d (.tn(1'b1)); // Warning would be reasonable given tri0 connect - t_tri0 #(.EXPECT(1'b0)) tri0e (.tn(~one)); - t_tri0 #(.EXPECT(1'b1)) tri0f (.tn(one)); + t_tri0 tri0a (.line(`__LINE__), .expval(1'b0)); // Pin missing + t_tri0 tri0b (.line(`__LINE__), .expval(1'b0), .tn()); + t_tri0 tri0z (.line(`__LINE__), .expval(1'b0), .tn(z0)); + t_tri0 tri0Z (.line(`__LINE__), .expval(1'b0), .tn(1'bz)); + t_tri0 tri0c (.line(`__LINE__), .expval(1'b0), .tn(1'b0)); + t_tri0 tri0d (.line(`__LINE__), .expval(1'b1), .tn(1'b1)); // Warning would be reasonable given tri0 connect + 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 tri1b (.tn()); - t_tri1 tri1z (.tn(z1)); - t_tri1 #(.EXPECT(1'b0)) tri1c (.tn(1'b0)); // Warning would be reasonable given tri1 connect - t_tri1 #(.EXPECT(1'b1)) tri1d (.tn(1'b1)); - t_tri1 #(.EXPECT(1'b0)) tri1e (.tn(~one)); - t_tri1 #(.EXPECT(1'b1)) tri1f (.tn(one)); + t_tri1 tri1a (.line(`__LINE__), .expval(1'b1)); // Pin missing + t_tri1 tri1b (.line(`__LINE__), .expval(1'b1), .tn()); + t_tri1 tri1z (.line(`__LINE__), .expval(1'b1), .tn(z1)); + t_tri1 tri1Z (.line(`__LINE__), .expval(1'b1), .tn(1'bz)); + t_tri1 tri1c (.line(`__LINE__), .expval(1'b0), .tn(1'b0)); // Warning would be reasonable given tri1 connect + t_tri1 tri1d (.line(`__LINE__), .expval(1'b1), .tn(1'b1)); + 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 tri2b (.tn()); - t_tri2 tri2z (.tn(z2)); - t_tri2 #(.EXPECT(1'b0)) tri2c (.tn(1'b0)); - t_tri2 #(.EXPECT(1'b1)) tri2d (.tn(1'b1)); - t_tri2 #(.EXPECT(1'b0)) tri2e (.tn(~one)); - t_tri2 #(.EXPECT(1'b1)) tri2f (.tn(one)); + t_tri2 tri2a (.line(`__LINE__), .expval(1'b0)); // Pin missing + t_tri2 tri2b (.line(`__LINE__), .expval(1'b0), .tn()); + t_tri2 tri2z (.line(`__LINE__), .expval(1'b0), .tn(z2)); + t_tri2 tri2Z (.line(`__LINE__), .expval(1'b0), .tn(1'bz)); + t_tri2 tri2c (.line(`__LINE__), .expval(1'b0), .tn(1'b0)); + t_tri2 tri2d (.line(`__LINE__), .expval(1'b1), .tn(1'b1)); + 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 tri3b (.tn()); - t_tri3 tri3z (.tn(z3)); - t_tri3 #(.EXPECT(1'b0)) tri3c (.tn(1'b0)); - t_tri3 #(.EXPECT(1'b1)) tri3d (.tn(1'b1)); - t_tri3 #(.EXPECT(1'b0)) tri3e (.tn(~one)); - t_tri3 #(.EXPECT(1'b1)) tri3f (.tn(one)); + t_tri3 tri3a (.line(`__LINE__), .expval(1'b1)); // Pin missing + t_tri3 tri3b (.line(`__LINE__), .expval(1'b1), .tn()); + t_tri3 tri3z (.line(`__LINE__), .expval(1'b1), .tn(z3)); + t_tri3 tri3Z (.line(`__LINE__), .expval(1'b1), .tn(1'bz)); + t_tri3 tri3c (.line(`__LINE__), .expval(1'b0), .tn(1'b0)); + t_tri3 tri3d (.line(`__LINE__), .expval(1'b1), .tn(1'b1)); + 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 // Test loop @@ -63,37 +76,49 @@ module t (/*AUTOARG*/ endmodule module t_tri0 - #(parameter EXPECT=1'b0) - (tn); + (line, expval, tn); + input integer line; + input expval; input tn; // Illegal to be inout; spec requires net connection to any inout tri0 tn; 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 module t_tri1 - #(parameter EXPECT=1'b1) - (tn); + (line, expval, tn); + input integer line; + input expval; input tn; tri1 tn; 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 module t_tri2 - #(parameter EXPECT=1'b0) - (tn); + (line, expval, tn); + input integer line; + input expval; input tn; pulldown(tn); 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 module t_tri3 - #(parameter EXPECT=1'b1) - (tn); + (line, expval, tn); + input integer line; + input expval; input tn; pullup(tn); 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