diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 4707a390f..4874cbe6c 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -202,6 +202,174 @@ class TristateVisitor : public TristateBaseVisitor { nodep->v3error("Unsupported tristate construct: "<prettyTypeName()); } + void insertTristates(AstNodeModule* nodep) { + // 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()); + if (invarp->user3p()) UINFO(9, "propagate pull to "<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(); + UINFO(9,"Has pullup "<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; + } + } + // VISITORS virtual void visit(AstConst* nodep, AstNUser*) { UINFO(9,(m_alhs?"alhs":"")<<" "<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()); - if (invarp->user3p()) UINFO(9, "propagate pull to "<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(); - UINFO(9,"Has pullup "<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; - } + // Insert new logic for all tristates + insertTristates(nodep); m_modp = NULL; }