// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Deals with tristate logic // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Lane Brooks with Wilson Snyder, Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2012 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* // V3Tristate's Transformations: // // This module modifies 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 // drivers and corresponding output enable signals are generated. // These enable signals get transformed and regenerated through any // logic that they may go through until they hit the module level. At // the module level, all the output enable signals from what can be // many tristate drivers are combined together to produce a single // driver and output enable. If the signal propigates up into higher // modules, then new ports are created with for the signal with // suffixes __en and __out. The original port is turned from an inout // to an input and the __out port carries the output driver signal and // the __en port carried the output enable for that driver. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include "V3Global.h" #include "V3Tristate.h" #include "V3Ast.h" #include "V3Const.h" #include "V3Stats.h" #include "V3Inst.h" #include "V3Stats.h" //###################################################################### class TristateBaseVisitor : public AstNVisitor { public: // METHODS static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } }; //###################################################################### // Given a node, flip any VarRef from LValue to RValue (i.e. make it an input) class TristateInPinVisitor : public TristateBaseVisitor { // VISITORS virtual void visit(AstVarRef* nodep, AstNUser*) { if (nodep->lvalue()) { UINFO(9," Flip-to-RValue "<lvalue(false); } } virtual void visit(AstNode* nodep, AstNUser*) { nodep->iterateChildren(*this); } public: // CONSTUCTORS TristateInPinVisitor(AstNode* nodep) { nodep->accept(*this); } virtual ~TristateInPinVisitor() {} }; //###################################################################### 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 // AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p) // AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p) // TYPES typedef std::vector VarVec; typedef std::vector RefVec; typedef std::map VarMap; // MEMBERS 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 // STATS V3Double0 m_statTriSigs; // stat tracking AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; AstUser3InUse m_inuser3; AstUser4InUse m_inuser4; // METHODS 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. if (!nodep->user1p()) { V3Number num(nodep->fileline(), nodep->width()); num.setAllBits1(); AstNode* enp = new AstConst(nodep->fileline(), num); nodep->user1p(enp); } return nodep->user1p()->castNode(); } AstVar* getCreateEnVarp(AstVar* invarp) { // Return the master __en for the specified input variable if (!invarp->user1p()) { AstVar* newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP, invarp->name()+"__en", invarp); if (!m_modp) { invarp->v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } else m_modp->addStmtp(newp); invarp->user1p(newp); // find envar given invarp } return invarp->user1p()->castNode()->castVar(); } AstVar* getCreateOutVarp(AstVar* invarp) { // Return the master __out for the specified input variable if (!invarp->user4p()) { AstVar* newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP, invarp->name()+"__out", invarp); if (!m_modp) { invarp->v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } else m_modp->addStmtp(newp); invarp->user4p(newp); // find outvar given invarp } return invarp->user4p()->castNode()->castVar(); } 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 AstNode* newp = new AstShiftL(selp->fileline(), new AstExtend(selp->fileline(), enp, selp->fromp()->width()), selp->lsbp()->cloneTree(false), selp->fromp()->width()); return newp; } void checkPullDirection(AstPull* pullp1, AstPull* pullp2) { if (pullp1 && pullp2 && pullp1->direction() != pullp2->direction()) { pullp1->v3error("Unsupported: Conflicting pull directions."); pullp2->v3error("... Location of conflicing pull."); } } void checkUnhandled(AstNode* nodep) { // Check for unsupported tristate constructs. This is not a 100% check. // The best way would be to visit the tree again and find any user1p() // pointers that did not get picked up and expanded. if (m_alhs && nodep->user1p()) nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); if ((nodep->op1p() && nodep->op1p()->user1p()) || (nodep->op2p() && nodep->op2p()->user1p()) || (nodep->op3p() && nodep->op3p()->user1p()) || (nodep->op4p() && nodep->op4p()->user1p())) nodep->v3error("Unsupported tristate construct: "<prettyTypeName()); } // VISITORS virtual void visit(AstConst* nodep, AstNUser*) { UINFO(9,(m_alhs?"alhs":"")<<" "<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); } } virtual void visit(AstCond* nodep, AstNUser*) { if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } nodep->iterateChildren(*this); UINFO(9,(m_alhs?"alhs":"")<<" "<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()) { return; // no tristates in either expression, so nothing to do } 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); nodep->user1p(enp); expr1p->user1p(NULL); expr2p->user1p(NULL); } virtual void visit(AstSel* nodep, AstNUser*) { if (m_alhs) { UINFO(9,"alhs "<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; "); } nodep->iterateChildren(*this); } else { nodep->iterateChildren(*this); UINFO(9," "<fromp()->user1p() || nodep->lsbp()->user1p()) nodep->v3error("Unsupported RHS tristate construct: "<prettyTypeName()); } } virtual void visit(AstConcat* nodep, AstNUser*) { if (m_alhs) { UINFO(9,(m_alhs?"alhs":"")<<" "<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())); } nodep->iterateChildren(*this); } else { nodep->iterateChildren(*this); UINFO(9,(m_alhs?"alhs":"")<<" "<lhsp(); AstNode* expr2p = nodep->rhsp(); if (!expr1p->user1p() && !expr2p->user1p()) { return; // no tristates in either expression, so nothing to do } AstNode* en1p = getEnp(expr1p); AstNode* en2p = getEnp(expr2p); AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p); nodep->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,(m_alhs?"alhs":"")<<" "<=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); } 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="<user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } // ANDs and Z's have issues. Earlier optimizations convert // expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we // have to define what is means to AND 1'bz with other // expressions. I don't think this is spec, but here I take the // approach that when one expression is 1, that the Z passes. This // makes the COND's work. It is probably better to not perform the // conditional optimization if the bits are Z. AstNode* expr1p = nodep->lhsp(); AstNode* expr2p = nodep->rhsp(); if (!expr1p->user1p() && !expr2p->user1p()) { return; // no tristates in either expression, so nothing to do } AstNode* en1p = getEnp(expr1p); AstNode* en2p = getEnp(expr2p); // calc new output enable. AstNode* enp = new AstOr(nodep->fileline(), new AstAnd(nodep->fileline(), en1p, en2p), new AstOr(nodep->fileline(), new AstAnd(nodep->fileline(), en1p->cloneTree(false), new AstNot(nodep->fileline(), expr1p->cloneTree(false))), new AstAnd(nodep->fileline(), en2p->cloneTree(false), new AstNot(nodep->fileline(), expr2p->cloneTree(false))))); nodep->user1p(enp); expr1p->user1p(NULL); expr2p->user1p(NULL); } virtual void visit(AstOr* nodep, AstNUser*) { nodep->iterateChildren(*this); UINFO(9,(m_alhs?"alhs":"")<<" "<user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } // ORs have the same issues as ANDs. Earlier optimizations convert // expressions like "(COND) ? 1'bz : 1'b1" to "COND | 1'bz". So we // have to define what is means to OR 1'bz with other // expressions. Here I take the approach that when one expression // is 0, that is passes the other. AstNode* expr1p = nodep->lhsp(); AstNode* expr2p = nodep->rhsp(); if (!expr1p->user1p() && !expr2p->user1p()) { return; // no tristates in either expression, so nothing to do } AstNode* en1p = getEnp(expr1p); AstNode* en2p = getEnp(expr2p); // calc new output enable AstNode* enp = new AstOr(nodep->fileline(), new AstAnd(nodep->fileline(), en1p, en2p), new AstOr(nodep->fileline(), new AstAnd(nodep->fileline(), en1p->cloneTree(false), expr1p->cloneTree(false)), new AstAnd(nodep->fileline(), en2p->cloneTree(false), expr2p->cloneTree(false)))); nodep->user1p(enp); expr1p->user1p(NULL); expr2p->user1p(NULL); } 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; } virtual void visit(AstAssignW* nodep, AstNUser*) { visitAssign(nodep); } virtual void visit(AstAssign* nodep, AstNUser*) { visitAssign(nodep); } 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," "<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); if (debug()>=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*) { visitCaseEq(nodep,false); } virtual void visit(AstNeqCase* nodep, AstNUser*) { visitCaseEq(nodep,true); } virtual void visit(AstPull* nodep, AstNUser*) { UINFO(9," "<lhsp()->castVarRef()) { AstVarRef* lhsp = nodep->lhsp()->unlinkFrBack()->castVarRef(); lhsp->lvalue(true); AstVar* varp = lhsp->varp(); AstPull* pullp = (AstPull*)varp->user3p(); checkPullDirection(pullp, 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 { nodep->v3error("Unsupported pullup/down (weak driver) construct."); } } virtual void visit(AstPin* nodep, AstNUser*) { // .tri(SEL(trisig,x)) becomes // INPUT: -> (VARREF(trisig__pinin)), // trisig__pinin = SEL(trisig,x) // via pinReconnectSimple // OUTPUT: -> (VARREF(trisig__pinout)) // ENABLE: -> (VARREF(trisig__pinen) // SEL(trisig,x) = BUFIF1(enable__temp, trisig__pinen) UINFO(9," "<exprp()) return; // No-connect AstVar* enModVarp = (AstVar*) nodep->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: "); // pinReconnectSimple needs to presume input or output behavior; we need both // Therefore, create the BUFIF1 on output and separate input pin, then pinReconnectSimple both // 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(), nodep->name() + "__en" + cvtToStr(m_unique++), new AstVarRef(nodep->fileline(), enVarp, true)); enpinp->widthSignedFrom(enModVarp); enpinp->modVarp(enModVarp); enpinp->user2(true); // mark this visited m_cellp->addPinsp(enpinp); m_modp->addStmtp(enVarp); enrefp = new AstVarRef(nodep->fileline(), enVarp, false); if (debug()>=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(), nodep->name() + "__out"+cvtToStr(m_unique), outexprp); outpinp->widthSignedFrom(outModVarp); outpinp->modVarp(outModVarp); outpinp->user2(true); // mark this visited m_cellp->addPinsp(outpinp); // Simplify outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, m_modp); // 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 TristateInPinVisitor visitor (nodep->exprp()); V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp); // Note may change nodep->exprp() if (debug()>=9) nodep->dumpTree(cout,"-pin-in: "); // Connect enable to output signal AstVarRef* refp; if (!outAssignp) { refp = outpinp->exprp()->castVarRef(); } else { refp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin } if (!refp) { // deal with simple varref port nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); } else { refp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there visit(refp, NULL); // visit this var ref to get it in the varmap } // Propagate any pullups/pulldowns upwards if necessary if (refp) { if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) { if (!refp->varp()->user3p()) { refp->varp()->user3p(pullp); } else { //selp: Note we don't currently obey selects; all bits must be consistently pulled checkPullDirection(pullp, (AstPull*) refp->varp()->user3p()); } } } // 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. } virtual void visit(AstVarRef* nodep, AstNUser*) { UINFO(9,(m_alhs?"alhs":"")<<" "<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)); } else { (*it).second->push_back(nodep); } } if (m_alhs) {} // NOP; user1() already passed down from assignment nodep->iterateChildren(*this); } virtual void visit(AstVar* nodep, AstNUser*) { if (nodep->user2Inc()) 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()); m_modp->addStmtp(newp); // We'll iterate on the new AstPull later } } virtual void visit(AstNodeModule* nodep, AstNUser*) { UINFO(8, nodep<iterateChildren(*this); // 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) { 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" ) { VarMap::iterator it = m_lhsmap.find(varp); if (it == m_lhsmap.end()) { UINFO(8," Adding driver to var "<fileline(), varp->width()); zeros.setAllBits0(); AstConst* constp = new AstConst(varp->fileline(), zeros); AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true); nodep->addStmtp(new AstAssignW(varp->fileline(), varrefp, constp)); 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 } } } // Now go through the lhs driver map and generate the output // enable logic for any tristates. for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) { nextit = it; ++nextit; AstVar* invarp = (*it).first; 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) { // This var has no tristate logic, so we leave it alone. UINFO(8, " NO TRISTATE ON:" << invarp << endl); m_lhsmap.erase(invarp); delete refs; continue; } m_statTriSigs++; UINFO(8, " TRISTATE EXPANDING("<isTop() && invarp->isIO()) { // This var becomes an input invarp->varType2In(); // convert existing port to type input // Create an output port (__out) AstVar* outvarp = getCreateOutVarp(invarp); outvarp->varType2Out(); lhsp = outvarp; // Must assign to __out, not to normal input signal // Create an output enable port (__en) envarp = getCreateEnVarp(invarp); // May already be created if have foo === 1'bz somewhere envarp->varType2Out(); // outvarp->user1p(envarp); outvarp->user3p(invarp->user3p()); } else if (invarp->user1p()) { envarp = invarp->user1p()->castNode()->castVar(); // From CASEEQ, foo === 1'bz } AstNode* orp = NULL; AstNode* andp = NULL; AstNode* enp = NULL; AstNode* undrivenp = NULL; // loop through the lhs drivers to build the driver resolution logic for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { AstVarRef* refp = (*ii); int w = lhsp->width(); // create the new lhs driver for this var AstVar* newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP, lhsp->name()+"__out"+cvtToStr(m_unique), VFlagLogicPacked(), w); nodep->addStmtp(newlhsp); refp->varp(newlhsp); // assign the new var to the varref refp->name(newlhsp->name()); // create a new var for this drivers enable signal AstVar* newenp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP, lhsp->name()+"__en"+cvtToStr(m_unique++), VFlagLogicPacked(), w); nodep->addStmtp(newenp); nodep->addStmtp(new AstAssignW(refp->fileline(), new AstVarRef(refp->fileline(), newenp, true), getEnp(refp))); // now append this driver to the driver logic. AstNode* ref1p = new AstVarRef(nodep->fileline(), newlhsp,false); AstNode* ref2p = new AstVarRef(nodep->fileline(), newenp, false); andp = new AstAnd(nodep->fileline(), ref1p, ref2p); // or this to the others orp = (!orp) ? andp : new AstOr(nodep->fileline(), orp, andp); if (envarp) { AstNode* ref3p = new AstVarRef(nodep->fileline(), newenp, false); enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p); } AstNode* tmp = new AstNot(newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, false)); undrivenp = ((!undrivenp) ? tmp : new AstAnd(nodep->fileline(), tmp, undrivenp)); } if (!undrivenp) { // No drivers on the bus V3Number ones(nodep->fileline(), lhsp->width()); ones.setAllBits1(); undrivenp = new AstConst(nodep->fileline(), ones); } if (!outvarp) { // This is the final resolution of the tristate, so we apply // the pull direction to any undriven pins. V3Number pull(nodep->fileline(), lhsp->width()); AstPull* pullp = (AstPull*)lhsp->user3p(); if (pullp && pullp->direction() == 1) { pull.setAllBits1(); } else { pull.setAllBits0(); // default pull direction is down. } undrivenp = new AstAnd(nodep->fileline(), undrivenp, new AstConst(nodep->fileline(), pull)); orp = new AstOr(nodep->fileline(), orp, undrivenp); } if (envarp) { nodep->addStmtp(new AstAssignW(enp->fileline(), new AstVarRef(envarp->fileline(), envarp, true), enp)); } AstNode* assp = new AstAssignW(lhsp->fileline(), new AstVarRef(lhsp->fileline(), lhsp, true), orp); if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: "); nodep->addStmtp(assp); // Delete the map and vector list now that we have expanded it. m_lhsmap.erase(invarp); delete refs; } m_modp = NULL; } virtual void visit(AstNodeFTask* nodep, AstNUser*) { // don't deal with functions } virtual void visit(AstCaseItem* nodep, AstNUser*) { // don't deal with casez compare '???? values nodep->bodysp()->iterateAndNext(*this); } virtual void visit(AstCell* nodep, AstNUser*) { m_cellp = nodep; m_alhs = false; nodep->iterateChildren(*this); m_cellp = NULL; } virtual void visit(AstNetlist* nodep, AstNUser*) { nodep->iterateChildrenBackwards(*this); } // Default: Just iterate virtual void visit(AstNode* nodep, AstNUser*) { nodep->iterateChildren(*this); UINFO(9," "<accept(*this); } virtual ~TristateVisitor() { V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs); } }; //###################################################################### // Tristate class functions void V3Tristate::tristateAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<