virtual void visit(AstArraySel* nodep, AstNUser*) { if (!m_assignp) return; if (nodep->user3()) return; // Prevent recursion on just created nodes unsigned dim = explicitDimensions(nodep); AstVarRef* refp = nodep->user1p()->castNode()->castVarRef(); pair<uint32_t,uint32_t> arrDim = refp->varp()->dtypep()->dimensions(false); uint32_t implicit = (arrDim.second) - dim; if (implicit > 0) { AstArraySel* newp = insertImplicit(nodep->cloneTree(false), dim+1, implicit); nodep->replaceWith(newp); nodep = newp; nodep->user3(true); } int clones = countClones(nodep); if (m_assignp->user2() > 0 && m_assignp->user2() != clones) { m_assignp->v3error("Slices of arrays in assignments must have the same unpacked dimensions"); } else if (!m_assignp->user2()) { if (m_extend && clones > 1 && !m_assignError) { m_assignp->v3error("Unsupported: Assignment between unpacked arrays of different dimensions"); m_assignError = true; } if (clones > 1 && !refp->lvalue() && refp->varp() == m_lhsVarRefp->varp() && !m_assignp->castAssignDly() && !m_assignError) { // LHS Var != RHS Var for a non-delayed assignment m_assignp->v3error("Unsupported: Slices in a non-delayed assignment with the same Var on both sides"); m_assignError = true; } m_assignp->user2(clones); } }
virtual void visit(AstVar* nodep, AstNUser*) { if (nodep->user2p()) { // Make an assignment, so we'll trace it properly // user2p is either a const or a var. AstConst* exprconstp = nodep->user2p()->castNode()->castConst(); AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef(); UINFO(8,"connectto: "<<nodep->user2p()->castNode()<<endl); if (!exprconstp && !exprvarrefp) { nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); } if (exprconstp) { m_modp->addStmtp(new AstAssignW(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), exprconstp->cloneTree(true))); } else if (nodep->user3()) { // Public variable at the lower module end - we need to make sure we propagate // the logic changes up and down; if we aliased, we might remove the change detection // on the output variable. UINFO(9,"public pin assign: "<<exprvarrefp<<endl); if (nodep->isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias"); m_modp->addStmtp(new AstAssignW(nodep->fileline(), new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true), new AstVarRef(nodep->fileline(), nodep, false))); } else if (nodep->isIfaceRef()) { m_modp->addStmtp(new AstAssignVarScope(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); AstNode* nodebp=exprvarrefp->varp(); nodep ->fileline()->modifyStateInherit(nodebp->fileline()); nodebp->fileline()->modifyStateInherit(nodep ->fileline()); } else { // Do to inlining child's variable now within the same module, so a AstVarRef not AstVarXRef below m_modp->addStmtp(new AstAssignAlias(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); AstNode* nodebp=exprvarrefp->varp(); nodep ->fileline()->modifyStateInherit(nodebp->fileline()); nodebp->fileline()->modifyStateInherit(nodep ->fileline()); } } // Variable under the inline cell, need to rename to avoid conflicts // Also clear I/O bits, as it is now local. string name = m_cellp->name() + "__DOT__" + nodep->name(); if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name); if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); } if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); } // Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module if (AstIfaceRefDType* ifacerefp = nodep->dtypep()->castIfaceRefDType()) { // Relink to point to newly cloned cell if (ifacerefp->cellp()) { if (AstCell* newcellp = ifacerefp->cellp()->user4p()->castNode()->castCell()) { ifacerefp->cellp(newcellp); ifacerefp->cellName(newcellp->name()); } } } nodep->iterateChildren(*this); }
AstArraySel* insertImplicit(AstNode* nodep, unsigned start, unsigned count) { // Insert any implicit slices as explicit slices (ArraySel nodes). // Return a new pointer to replace nodep() in the ArraySel. UINFO(9," insertImplicit (start="<<start<<",c="<<count<<") "<<nodep<<endl); AstVarRef* refp = nodep->user1p()->castNode()->castVarRef(); if (!refp) nodep->v3fatalSrc("No VarRef in user1 of node "<<nodep); AstVar* varp = refp->varp(); AstNode* topp = nodep; for (unsigned i = start; i < start + count; ++i) { AstNodeDType* dtypep = varp->dtypep()->dtypeDimensionp(i-1); AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType(); if (!adtypep) nodep->v3fatalSrc("insertImplicit tried to expand an array without an ArrayDType"); vlsint32_t msb = adtypep->msb(); vlsint32_t lsb = adtypep->lsb(); if (lsb > msb) { // Below code assumes big bit endian; just works out if we swap int x = msb; msb = lsb; lsb = x; } UINFO(9," ArraySel-child: "<<topp<<endl); AstArraySel* newp = new AstArraySel(nodep->fileline(), topp, // "lsb-lsb": Arrays are zero-based so index 0 is always lsb new AstConst(nodep->fileline(), lsb-lsb)); if (!newp->dtypep()) { newp->v3fatalSrc("ArraySel dtyping failed when resolving slice"); // see ArraySel constructor } newp->user1p(refp); newp->start(lsb); newp->length(msb - lsb + 1); topp = newp; } return topp->castArraySel(); }
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); }
virtual void visit(AstSel* nodep, AstNUser*) { AstVarRef* varrefp = nodep->fromp()->castVarRef(); AstConst* constp = nodep->lsbp()->castConst(); if (varrefp && constp && !constp->num().isFourState()) { UndrivenVarEntry* entryp = getEntryp (varrefp->varp()); int lsb = constp->toUInt(); if (m_markBoth || varrefp->lvalue()) entryp->drivenBit(lsb, nodep->width()); if (m_markBoth || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width()); } else { // else other varrefs handled as unknown mess in AstVarRef nodep->iterateChildren(*this); } }
virtual void visit(AstPin* nodep, AstNUser*) { if (m_state == CONVERT_PINS) { if (nodep->modVarp()->user1p()) { // create the input pin AstVarRef* refp = nodep->exprp()->castVarRef(); if (!refp) nodep->v3fatal("Unsupported: Tristate pin not connected to simple net"); AstVar* inp; if (refp->varp()->user1p()) { // this is a tristate inp = (AstVar*) refp->varp()->user1p(); } else { inp = refp->varp(); } AstPin* pinp = new AstPin(nodep->fileline(), nodep->pinNum(), nodep->name() + "__in", new AstVarRef(nodep->fileline(), inp, false)); m_cellp->addPinsp(pinp); // now link it pinp->modVarp((AstVar*) nodep->modVarp()->user1p()); } } }
// VISITORS virtual void visit(AstArraySel* nodep, AstNUser*) { if (!nodep->backp()->castArraySel()) { // This is the top of an ArraySel, setup for iteration m_refp = nodep->user1p()->castNode()->castVarRef(); m_vecIdx += 1; if (m_vecIdx == (int)m_selBits.size()) { m_selBits.push_back(vector<unsigned>()); AstVar* varp = m_refp->varp(); pair<uint32_t,uint32_t> arrDim = varp->dtypep()->dimensions(false); uint32_t dimensions = arrDim.second; // for 3-dimensions we want m_selBits[m_vecIdx]=[0,0,0] for (uint32_t i = 0; i < dimensions; ++i) { m_selBits[m_vecIdx].push_back(0); } } } nodep->iterateChildren(*this); if (nodep->fromp()->castVarRef()) { m_depth = 0; } else { ++m_depth; } // Check if m_selBits has overflowed if (m_selBits[m_vecIdx][m_depth] >= nodep->length()) { m_selBits[m_vecIdx][m_depth] = 0; if (m_depth + 1 < m_selBits[m_vecIdx].size()) m_selBits[m_vecIdx][m_depth+1] += 1; } // Reassign the bitp() if (nodep->length() > 1) { if (AstConst* bitp = nodep->bitp()->castConst()) { AstUnpackArrayDType* adtypep = nodep->fromp()->dtypep()->skipRefp()->castUnpackArrayDType(); if (!adtypep) nodep->v3fatalSrc("slice select tried to expand an array without an ArrayDType"); unsigned idx = nodep->start() + m_selBits[m_vecIdx][m_depth] - adtypep->lsb(); AstNode* constp = new AstConst(bitp->fileline(), V3Number(bitp->fileline(), bitp->castConst()->num().width(), idx)); bitp->replaceWith(constp); } else { nodep->v3error("Unsupported: Only constants supported in slices"); } } if (!nodep->backp()->castArraySel()) { // Top ArraySel, increment m_selBits m_selBits[m_vecIdx][0] += 1; } nodep->length(1); }
void expandUniOp(AstNodeUniop* nodep) { nodep->user1(true); unsigned dim = 0; if (AstArraySel* selp = nodep->lhsp()->castArraySel()) { // We have explicit dimensions, either packed or unpacked dim = explicitDimensions(selp); } if (dim == 0 && !nodep->lhsp()->castVarRef()) { // No ArraySel nor VarRef, not something we can expand nodep->iterateChildren(*this); } else { AstVarRef* refp = findVarRefRecurse(nodep->lhsp()); ArrayDimensions varDim = refp->varp()->dtypep()->dimensions(false); if ((int)(dim - varDim.second) < 0) { // Unpacked dimensions are referenced first, make sure we have them all nodep->v3error("Unary operator used across unpacked dimensions"); } } }
//******************************************************************* // 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(AstVarRef* nodep, AstNUser*) { if (nodep->varp()->user2p() // It's being converted to an alias. && !nodep->varp()->user3() && !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made) AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst(); AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef(); if (exprconstp) { nodep->replaceWith(exprconstp->cloneTree(true)); nodep->deleteTree(); nodep=NULL; return; } else if (exprvarrefp) { nodep->varp( exprvarrefp->varp() ); } else { nodep->v3fatalSrc("Null connection?\n"); } } nodep->name(nodep->varp()->name()); nodep->iterateChildren(*this); }
virtual void visit(AstSel* nodep, AstNUser*) { AstVarRef* varrefp = nodep->fromp()->castVarRef(); AstConst* constp = nodep->lsbp()->castConst(); if (varrefp && constp && !constp->num().isFourState()) { for (int usr=1; usr<(m_alwaysp?3:2); ++usr) { UndrivenVarEntry* entryp = getEntryp (varrefp->varp(), usr); int lsb = constp->toUInt(); if (m_markBoth || varrefp->lvalue()) { // Don't warn if already driven earlier as "a=0; if(a) a=1;" is fine. if (usr==2 && m_alwaysp && entryp->isUsedNotDrivenBit(lsb, nodep->width())) { UINFO(9," Select. Entryp="<<(void*)entryp<<endl); warnAlwCombOrder(varrefp); } entryp->drivenBit(lsb, nodep->width()); } if (m_markBoth || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width()); } } else { // else other varrefs handled as unknown mess in AstVarRef nodep->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); }
static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModule*, bool forTristate, bool alwaysCvt) { // If a pin connection is "simple" leave it as-is // Else create a intermediate wire to perform the interconnect // Return the new assignment, if one was made // Note this module calles cloneTree() via new AstVar AstVar* pinVarp = pinp->modVarp(); AstVarRef* connectRefp = pinp->exprp()->castVarRef(); AstBasicDType* pinBasicp = pinVarp->dtypep()->basicp(); // Maybe NULL AstBasicDType* connBasicp = NULL; AstAssignW* assignp = NULL; if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->basicp(); // if (!alwaysCvt && connectRefp && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types // Done. Same data type } else if (!alwaysCvt && connectRefp && connectRefp->varp()->isIfaceRef()) { // Done. Interface } else if (!alwaysCvt && connBasicp && pinBasicp && connBasicp->width() == pinBasicp->width() && connBasicp->lsb() == pinBasicp->lsb() && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types && connBasicp->width() == pinVarp->width() && 1) { // Done. One to one interconnect won't need a temporary variable. } else if (!alwaysCvt && !forTristate && pinp->exprp()->castConst()) { // Done. Constant. } else { // Make a new temp wire //if (1||debug()>=9) { pinp->dumpTree(cout,"-in_pin:"); } AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname = ((string)(pinVarp->isOutput() ? "__Vcellout" : "__Vcellinp") +(forTristate?"t":"") // Prevent name conflict if both tri & non-tri add signals +"__"+cellp->name()+"__"+pinp->name()); AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp); // Important to add statement next to cell, in case there is a generate with same named cell cellp->addNextHere(newvarp); if (pinVarp->isInout()) { pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be direct one-to-one connection (without any expression)"); } else if (pinVarp->isOutput()) { // See also V3Inst AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false); UINFO(5,"pinRecon width "<<pinVarp->width()<<" >? "<<rhsp->width()<<" >? "<<pinexprp->width()<<endl); rhsp = extendOrSel (pinp->fileline(), rhsp, pinVarp); pinp->exprp(new AstVarRef (newvarp->fileline(), newvarp, true)); AstNode* rhsSelp = extendOrSel (pinp->fileline(), rhsp, pinexprp); assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsSelp); } else { // V3 width should have range/extended to make the widths correct assignp = new AstAssignW (pinp->fileline(), new AstVarRef(pinp->fileline(), newvarp, true), pinexprp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false)); } if (assignp) cellp->addNextHere(assignp); //if (debug()) { pinp->dumpTree(cout,"- out:"); } //if (debug()) { assignp->dumpTree(cout,"- aout:"); } } return assignp; }
bool forUnrollCheck(AstNode* nodep, AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) AstNode* precondsp, AstNode* condp, AstNode* incp, // Maybe under nodep or in bodysp AstNode* bodysp) { // To keep the IF levels low, we return as each test fails. UINFO(4, " FOR Check "<<nodep<<endl); if (initp) UINFO(6, " Init "<<initp<<endl); if (precondsp) UINFO(6, " Pcon "<<precondsp<<endl); if (condp) UINFO(6, " Cond "<<condp<<endl); if (incp) UINFO(6, " Inc "<<incp<<endl); // Initial value check AstAssign* initAssp = initp->castAssign(); if (!initAssp) return cantUnroll(nodep, "no initial assignment"); if (initp->nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list"); if (!initAssp->lhsp()->castVarRef()) return cantUnroll(nodep, "no initial assignment to simple variable"); m_forVarp = initAssp->lhsp()->castVarRef()->varp(); m_forVscp = initAssp->lhsp()->castVarRef()->varScopep(); if (nodep->castGenFor() && !m_forVarp->isGenVar()) { nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->name()<<endl); } if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change AstConst* constInitp = initAssp->rhsp()->castConst(); if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); // // Condition check if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list"); // // Assignment of next value check AstAssign* incAssp = incp->castAssign(); if (!incAssp) return cantUnroll(nodep, "no increment assignment"); if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list"); AstNodeBiop* incInstrp = incAssp->rhsp()->castNodeBiop(); // if (m_forVscp) { UINFO(8, " Loop Variable: "<<m_forVscp<<endl); } else { UINFO(8, " Loop Variable: "<<m_forVarp<<endl); } if (debug()>=9) nodep->dumpTree(cout,"- for: "); // // Extract the constant loop bounds bool subtract = incInstrp->castSub(); { if (!subtract && !incInstrp->castAdd()) return cantUnroll(nodep, "missing add/sub for incrementer"); AstVarRef* incVarrp = (subtract ? incInstrp->lhsp()->castVarRef() : incInstrp->rhsp()->castVarRef()); if (!incVarrp) return cantUnroll(nodep, "missing variable in incrementer"); if (incVarrp->varp() != m_forVarp || incVarrp->varScopep() != m_forVscp) { return cantUnroll(nodep, "different variables in incrementer"); } } // // Adds have the # on the lhsp because V3Const pushes rhs consts over to the lhs // Subtracts have it on the rhs, because you write i=i-1; i=1-i is non-sensible. AstConst* preconstIncp = (subtract ? incInstrp->rhsp()->castConst() : incInstrp->lhsp()->castConst()); if (m_generate) preconstIncp = V3Const::constifyParamsEdit(preconstIncp)->castConst(); AstConst* constIncp = (subtract ? incInstrp->rhsp()->castConst() : incInstrp->lhsp()->castConst()); UINFO(8, " Inc expr ok: "<<constIncp<<endl); if (!constIncp) return cantUnroll(nodep, "non-constant increment"); if (constIncp->isZero()) return cantUnroll(nodep, "zero increment"); // Or we could loop forever below... bool lt = condp->castLt() || condp->castLtS(); bool lte = condp->castLte() || condp->castLteS(); bool gt = condp->castGt() || condp->castGtS(); bool gte = condp->castGte() || condp->castGteS(); if (!lt && !lte && !gt && !gte) return cantUnroll(nodep, "condition not <= or <"); AstNodeBiop* condBip = condp->castNodeBiop(); if (!condBip->lhsp()->castVarRef()) return cantUnroll(nodep, "no variable on lhs of condition"); if (condBip->lhsp()->castVarRef()->varp() != m_forVarp || condBip->lhsp()->castVarRef()->varScopep() != m_forVscp) return cantUnroll(nodep, "different variable in condition"); if (m_generate) V3Const::constifyParamsEdit(condBip->rhsp()); // rhsp may change AstConst* constStopp = condBip->rhsp()->castConst(); if (!constStopp) return cantUnroll(nodep, "non-constant final value"); UINFO(8, " Stop expr ok: "<<constStopp<<endl); // if (constInitp->width()>32 || constInitp->num().isFourState() || constStopp->width()>32 || constStopp->num().isFourState() || constIncp->width()>32 || constIncp->num().isFourState()) return cantUnroll(nodep, "init/final/increment too large or four state"); vlsint32_t valInit = constInitp->num().toSInt(); vlsint32_t valStop = constStopp->num().toSInt(); if (lte) valStop++; if (gte) valStop--; vlsint32_t valInc = constIncp->num().toSInt(); if (subtract) valInc = -valInc; UINFO(8," In Numbers: for (v="<<valInit<<"; v<"<<valStop<<"; v=v+"<<valInc<<")\n"); // if (!m_generate) { int loops = ((valStop - valInit)/valInc); if (loops < 0) { loops += (1ULL<<constStopp->width()); } // Will roll around UINFO(8, " ~Iters: "<<loops<<" c="<<unrollCount()<<endl); if (loops > unrollCount()) return cantUnroll(nodep, "too many iterations"); // Less than 10 statements in the body? int bodySize = 0; int bodyLimit = v3Global.opt.unrollStmts(); if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops; if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) { return cantUnroll(nodep, "too many statements"); } } // // Now, make sure there's no assignment to this variable in the loop m_varModeCheck = true; m_varAssignHit = false; m_ignoreIncp = incp; precondsp->iterateAndNext(*this); bodysp->iterateAndNext(*this); incp->iterateAndNext(*this); m_varModeCheck = false; m_ignoreIncp = NULL; if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); // // Finally, we can do it forUnroller(nodep, initp, precondsp, condp, incp, bodysp, constInitp->num(), condBip, constStopp->num(), incInstrp, constIncp->num()); nodep = NULL; // Cleanup return true; }
void V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModule* modp) { // If a pin connection is "simple" leave it as-is // Else create a intermediate wire to perform the interconnect // Note this module calles cloneTree() via new AstVar AstVar* pinVarp = pinp->modVarp(); AstVarRef* connectRefp = pinp->exprp()->castVarRef(); AstBasicDType* pinBasicp = pinVarp->dtypep()->basicp(); // Maybe NULL AstBasicDType* connBasicp = NULL; if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->basicp(); // if (connectRefp && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types // Done. Same data type } else if (connBasicp && pinBasicp && connBasicp->width() == pinBasicp->width() && connBasicp->lsb() == pinBasicp->lsb() && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types && pinp->width() == pinVarp->width() && 1) { // Done. One to one interconnect won't need a temporary variable. } else if (pinp->exprp()->castConst()) { // Done. Constant. } else { // Make a new temp wire //if (1||debug()>=9) { pinp->dumpTree(cout,"in_pin:"); } AstAssignW* assignp = NULL; AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname = "__Vcellinp__"+cellp->name()+"__"+pinp->name(); AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp); modp->addStmtp(newvarp); if (pinVarp->isInout()) { pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be direct one-to-one connection (without any expression)"); } else if (pinVarp->isOutput()) { // See also V3Inst AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false); if (pinp->width() > rhsp->width()) { if (rhsp->isSigned()) { rhsp = new AstExtendS(pinp->fileline(), rhsp); } else { rhsp = new AstExtend (pinp->fileline(), rhsp); } } else if (pinp->width() < rhsp->width()) { rhsp = new AstSel (pinp->fileline(), rhsp, 0, pinp->width()); } rhsp->widthSignedFrom(pinp); assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, true)); } else { // V3 width should have range/extended to make the widths correct if (pinexprp->width() != pinVarp->width()) pinp->v3fatalSrc("Input pin width mismatch"); assignp = new AstAssignW (pinp->fileline(), new AstVarRef(pinp->fileline(), newvarp, true), pinexprp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false)); } pinp->widthSignedFrom(pinp->exprp()); if (assignp) modp->addStmtp(assignp); //if (1||debug()) { pinp->dumpTree(cout," out:"); } //if (1||debug()) { assignp->dumpTree(cout," aout:"); } } }
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) { // Create delayed assignment // See top of this file for transformation // Return the new LHS for the assignment, Null = unlink // Find selects AstNode* newlhsp = NULL; // NULL = unlink old assign AstSel* bitselp = NULL; AstArraySel* arrayselp = NULL; if (lhsp->castSel()) { bitselp = lhsp->castSel(); arrayselp = bitselp->fromp()->castArraySel(); } else { arrayselp = lhsp->castArraySel(); } if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?"); if (arrayselp->length()!=1) nodep->v3fatalSrc("ArraySel with length!=1 should have been removed in V3Slice"); UINFO(4,"AssignDlyArray: "<<nodep<<endl); // //=== Dimensions: __Vdlyvdim__ deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment AstNode* dimselp=arrayselp; for (; dimselp->castArraySel(); dimselp=dimselp->castArraySel()->fromp()) { AstNode* valp = dimselp->castArraySel()->bitp()->unlinkFrBack(); dimvalp.push_front(valp); } AstVarRef* varrefp = dimselp->castVarRef(); if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels\n"); if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n"); varrefp->unlinkFrBack(); AstVar* oldvarp = varrefp->varp(); int modVecNum = oldvarp->user4(); oldvarp->user4(modVecNum+1); // deque<AstNode*> dimreadps; // Read value for each dimension of assignment for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) { AstNode* dimp = dimvalp[dimension]; if (dimp->castConst()) { // bit = const, can just use it dimreadps.push_front(dimp); } else { string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension) +"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width()); AstAssign* bitassignp = new AstAssign (nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), dimp); nodep->addNextHere(bitassignp); dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false)); } } // //=== Bitselect: __Vdlyvlsb__ AstNode* bitreadp=NULL; // Code to read Vdlyvlsb if (bitselp) { AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack(); if (bitselp->fromp()->castConst()) {// vlsb = constant, can just push constant into where we use it bitreadp = lsbvaluep; } else { string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width()); AstAssign* bitassignp = new AstAssign (nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), lsbvaluep); nodep->addNextHere(bitassignp); bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false); } } // //=== Value: __Vdlyvval__ AstNode* valreadp; // Code to read Vdlyvval if (nodep->rhsp()->castConst()) { // vval = constant, can just push constant into where we use it valreadp = nodep->rhsp()->unlinkFrBack(); } else { string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, nodep->rhsp()->width()); newlhsp = new AstVarRef(nodep->fileline(), valvscp, true); valreadp = new AstVarRef(nodep->fileline(), valvscp, false); } // //=== Setting/not setting boolean: __Vdlyvset__ AstVarScope* setvscp; AstAssignPre* setinitp = NULL; if (nodep->user3p()) { // Simplistic optimization. If the previous statement in same scope was also a =>, // then we told this nodep->user3 we can use its Vdlyvset rather than making a new one. // This is good for code like: // for (i=0; i<5; i++) vector[i] <= something; setvscp = nodep->user3p()->castNode()->castVarScope(); ++m_statSharedSet; } else { // Create new one string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); setvscp = createVarSc(varrefp->varScopep(), setvarname, 1); setinitp = new AstAssignPre (nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true), new AstConst(nodep->fileline(), 0)); AstAssign* setassignp = new AstAssign (nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true), new AstConst(nodep->fileline(), V3Number(nodep->fileline(),1,true))); nodep->addNextHere(setassignp); } if (m_nextDlyp) { // Tell next assigndly it can share the variable m_nextDlyp->user3p(setvscp); } // // Create ALWAYSPOST for delayed variable // We add all logic to the same block if it's for the same memory // This insures that multiple assignments to the same memory will result // in correctly ordered code - the last assignment must be last. // It also has the nice side effect of assisting cache locality. AstNode* selectsp = varrefp; for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) { selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]); } if (bitselp) { selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp, bitselp->widthp()->cloneTree(false)); } // Build "IF (changeit) ... UINFO(9," For "<<setvscp<<endl); UINFO(9," & "<<varrefp<<endl); AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castNode()->castAlwaysPost(); if (finalp) { AstActive* oldactivep = finalp->user2p()->castNode()->castActive(); checkActivePost(varrefp, oldactivep); if (setinitp) oldactivep->addStmtsp(setinitp); } else { // first time we've dealt with this memory finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/); UINFO(9," Created "<<finalp<<endl); AstActive* newactp = createActivePost(varrefp); newactp->addStmtsp(finalp); varrefp->varScopep()->user4p(finalp); finalp->user2p(newactp); if (setinitp) newactp->addStmtsp(setinitp); } AstIf* postLogicp; if (finalp->user3p()->castNode() == setvscp) { // Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*, // we can share the IF statement too postLogicp = finalp->user4p()->castNode()->castIf(); if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF"); } else { postLogicp = new AstIf (nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, false), NULL, NULL); UINFO(9," Created "<<postLogicp<<endl); finalp->addBodysp(postLogicp); finalp->user3p(setvscp); // Remember IF's vset variable finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it } postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp)); return newlhsp; }