virtual void visit(AstPin* nodep, AstNUser*) { // Check to see if any output pins have __en pins and create the __en pins to match AstVarRef* refp = findVarRef(nodep); if (refp && refp->lvalue() && nodep->modVarp()->user1p()) { AstVar* enchildp = (AstVar*)nodep->modVarp()->user1p(); UINFO(9, " Pulling __en var" << enchildp << endl); AstVar* enp = new AstVar(enchildp->fileline(), AstVarType::OUTPUT, enchildp->name()+cvtToStr(m_unique++), enchildp); enp->user2(enchildp->user2()); m_modp->addStmtp(enp); AstPin* pinp = new AstPin(nodep->fileline(), nodep->pinNum(), enp->name(), new AstVarRef(nodep->fileline(), enp, true)); AstVarRef *rp = findVarRef(pinp); rp->replaceWith(new AstVarRef(nodep->fileline(), enp, true)); rp->deleteTree(); rp=NULL; pinp->width(enp->width(),enp->width()); // minwidth==width pinp->modVarp(enchildp); m_cellp->addPinsp(pinp); refp->user1p(enp); refp->varp()->user1p(enp); } // Simplify interconnect in preperation for V3Inst // (This could be a separate visitor, but we're in the neighborhood) V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp); }
//******************************************************************* // The following visitor functions deal with detecting Z's in the // logic, stripping the Z's out and creating an __en signal and its // logic. //******************************************************************* virtual void visit(AstPull* nodep, AstNUser*) { // replace any pullup/pulldowns with assignw logic and an __en // signal just like it is any other tristate signal. The only // difference is that the user2() variable on the __en signal // will be given a pull direction--i.e. pulldown=1, pullup=2. // This will signal the driver exansion logic to put a default // pullup or pulldown state on the tristate bus under the high-Z // condition when no one is driving the bus. 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. AstNode* outp = nodep->lhsp()->unlinkFrBack();; AstVarRef* outrefp = NULL; int width=-1; if (outp->castVarRef()) { outrefp = outp->castVarRef(); } else if (outp->castSel()) { outrefp = outp->castSel()->fromp()->castVarRef(); width = outp->castSel()->widthConst(); } else { nodep->v3error("Can't find LHS varref"); } outrefp->lvalue(true); AstVar* varp = outrefp->varp(); if (width==-1) width=varp->width(); V3Number num0 (nodep->fileline(), width); num0.setAllBits0(); V3Number num1 (nodep->fileline(), width); num1.setAllBits1(); AstConst* enrhsp = new AstConst(nodep->fileline(), num0); AstVar* enp = createEnableVar(outp, outrefp, enrhsp, width, "pull"); enp->user2(nodep->direction()+1); // record the pull direction AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp, new AstConst(nodep->fileline(), nodep->direction() ? num1 : num0)); nodep->replaceWith(newassp); nodep->deleteTree(); nodep=NULL; newassp->iterateChildren(*this); }
virtual void visit(AstNodeModule* nodep, AstNUser*) { UINFO(9," MOD "<<nodep<<endl); m_unique = 0; VarMap* lhsmapp = new VarMap(); // expand tristate nodes and detect multiple LHS drivers for this module TristateExpander(nodep, lhsmapp); // iterate the children to grab any __en signals from subcells m_modp = nodep; nodep->iterateChildren(*this); m_modp = NULL; // go through each multiple lhs driver & collapse it to a single driver for (VarMap::iterator nextit, it=lhsmapp->begin(); it != lhsmapp->end(); it=nextit) { nextit = it; ++nextit; m_unique = 0; AstVar* lhsp = (*it).first; RefVec* refs = (*it).second; bool isOutput = (lhsp->varType() == AstVarType::OUTPUT) && (nodep->level() > 1); // force termination at top level if (refs->size() < 2 && isOutput) { // if only one driver and this is an output, then exit and // let the driver propagate on its own. If the signals // terminates at this level, then we need to let the // undriven state get generated. lhsmapp->erase(lhsp); delete refs; continue; } UINFO(9, " Checking " << refs->size() << " drivers for tristates signals on net " << lhsp << endl); int pull = 0; // initially assume no pull direction // Now remove and multple lhs signals that do not have __en for // all possible drivers. bool complete = true; int found_one = 0; for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { AstVarRef* refp = (*ii); if (!refp->user1p()) { // if no __en signal, then delete the entry complete = false; } else { found_one++; } } if (!complete) { if (found_one) { UINFO(9, " Problem mixing tristate and low-Z on " << lhsp << endl); UINFO(9, " Found " << found_one << " __en signals from of " << refs->size() << " possible drivers" << endl); // not sure what I should do here other than error that they are mixing low-Z and tristate drivers. // The other scenerio, and probably more likely, is that they are using a high-Z construct that // is not supported. Improving the high-Z detection logic will reduce the occurance of this failure. nodep->v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported"); } else { UINFO(9, " No tristates found on " << lhsp <<endl); } lhsmapp->erase(lhsp); delete refs; continue; } UINFO(9, " TRISTATE LHS DRIVER FOUND:" << lhsp << endl); AstNode* orp = NULL,* andp = NULL,* undrivenp = NULL,* newenlogicp = 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(); int wfill = 0; // width filler when necessary due to sels AstSel* selp = NULL; if (refp->user3p()) { // this varref has a sel selp = (AstSel*) refp->user3p(); w = selp->widthConst(); wfill = lhsp->width() - w; } // create a new var for this assignment. AstVar* enp = (AstVar*)refp->user1p(); AstVar* newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP, lhsp->name()+"__lhs"+cvtToStr(m_unique++), AstLogicPacked(), w); nodep->addStmtp(newlhsp); // now append this driver to the driver logic. AstNode* ref1 = new AstVarRef(nodep->fileline(), newlhsp,false); AstNode* ref2 = new AstVarRef(nodep->fileline(), enp, false); andp = new AstAnd(nodep->fileline(), ref1, ref2); AstVar* bitselp = NULL; if (selp) { // this varref has a sel int ws = V3Number::log2b(lhsp->width())+1; bitselp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP, lhsp->name()+"__sel"+cvtToStr(m_unique-1), AstLogicPacked(), ws); // nodep->addStmtp(bitselp); nodep->addStmtp(new AstAssignW(lhsp->fileline(), new AstVarRef(lhsp->fileline(), bitselp, true), selp->lsbp()->cloneTree(false))); andp = new AstShiftL(lhsp->fileline(), new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), andp), new AstVarRef(lhsp->fileline(), bitselp, false), lhsp->width() ); selp->replaceWith(new AstVarRef(refp->fileline(), newlhsp, true)); pushDeletep(selp); // Setting selp here or deleting immediately // breaks the t_tri_select test, this probably indicates a problem } else { refp->varp(newlhsp); // assign the new var to the varref refp->name(newlhsp->name()); } // or this to the others orp = (!orp) ? andp : new AstOr(nodep->fileline(), orp, andp); if (isOutput) { AstNode *en1p = new AstVarRef(nodep->fileline(), enp, false); if (selp) { en1p = new AstShiftL(enp->fileline(), new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), en1p), new AstVarRef(lhsp->fileline(), bitselp, false), lhsp->width() ); } if (!newenlogicp) { newenlogicp = en1p; } else { newenlogicp = new AstOr(nodep->fileline(), newenlogicp, en1p); } } else { if (!undrivenp) { undrivenp = new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), enp, false)); if (selp) undrivenp = new AstShiftL(enp->fileline(), new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), undrivenp), new AstVarRef(lhsp->fileline(), bitselp, false), lhsp->width()); } else { AstNode *tmp = new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), enp, false)); if (selp) { tmp = new AstShiftL(enp->fileline(), new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), tmp), new AstVarRef(lhsp->fileline(), bitselp, false), lhsp->width()); } undrivenp = new AstAnd(nodep->fileline(), tmp, undrivenp); } } refp->user1p(NULL); // clear the user1p() as we done with it in the VarRef at this point if (enp->user2()) { // if this net is pulled up/down int newpull = enp->user2(); if (pull == 0) { pull = newpull; } else if (newpull != pull) { pull = -1; // conflict over the pull direction } } } if (isOutput) { AstVar* newenp = new AstVar(lhsp->fileline(), AstVarType::OUTPUT, lhsp->name()+"__enout"+cvtToStr(m_unique++), lhsp); nodep->addStmtp(newenp); nodep->addStmtp(new AstAssignW(lhsp->fileline(), new AstVarRef(lhsp->fileline(), newenp, true), newenlogicp)); newenp->user2(pull); // put the pull direction in the next __en signal to pass it up lhsp->user1p(newenp); // put the new __en signal in the var so it can be pushed up the hierarchy. } else { // this is the level where the signal terminates, we do final conflict resolution here UINFO(9, " Terminating tristate logic for " << lhsp->name() << endl); UINFO(9, " Pull direction is " << pull << " where -1=X, 0=Z, 1=low, 2=high." << endl); // figure out what to drive when no one is driving the bus V3Number num(nodep->fileline(), lhsp->width()); if (pull==0) { num.setAllBitsZ(); } else if (pull==1) { num.setAllBits0(); } else if (pull==2) { num.setAllBits1(); } else { num.setAllBitsX(); } undrivenp = new AstAnd(nodep->fileline(), undrivenp, new AstConst(nodep->fileline(), num)); orp = new AstOr(nodep->fileline(), orp, undrivenp); } nodep->addStmtp(new AstAssignW(lhsp->fileline(), new AstVarRef(lhsp->fileline(), lhsp, true), orp)); // delete the map and vector list now that we have collapsed it. lhsmapp->erase(lhsp); delete refs; } delete lhsmapp; // delete the map now that we are done nodep->user1p(NULL); }