bool countLoops(AstAssign *initp, AstNode *condp, AstNode *incp, int max, int &outLoopsr) { outLoopsr = 0; V3Number loopValue = V3Number(initp->fileline()); if (!simulateTree(initp->rhsp(), NULL, initp, loopValue)) { return false; } while (1) { V3Number res = V3Number(initp->fileline()); if (!simulateTree(condp, &loopValue, NULL, res)) { return false; } if (!res.isEqOne()) { break; } outLoopsr++; // Run inc AstAssign* incpass = incp->castAssign(); V3Number newLoopValue = V3Number(initp->fileline()); if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) { return false; } loopValue.opAssign(newLoopValue); if (outLoopsr > max) { return false; } } return true; }
AstVar* createEnableVar(AstNode* outp, AstVarRef* outrefp, AstNode* enrhsp, int width, string suffix="") { // this function creates an __en Var that corresponds to // the outp and outrefp and creates an assignw to enrhsp AstVar* enp = new AstVar (outrefp->varp()->fileline(), AstVarType::MODULETEMP, outrefp->name() + "__en" + suffix + cvtToStr(m_unique++), AstLogicPacked(), width); enp->varType2Out(); if (enp->width() != enrhsp->width()) { if (enrhsp->width1()) { // it seems from my futzing that the linter guarantees this condition enrhsp = new AstReplicate(enrhsp->fileline(), enrhsp, new AstConst(enrhsp->fileline(), V3Number(enrhsp->fileline(), 32, enp->width()))); enrhsp->width(enp->width(), enp->width()); //minwidth==width } else { enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit"); } } AstNode* newassp = new AstAssignW (enp->fileline(), new AstVarRef (enp->fileline(), enp, true), enrhsp); if (debug()>=9) enp->dumpTreeAndNext(cout,"- cev-out: "); if (debug()>=9) newassp->dumpTreeAndNext(cout,"- cev-out: "); m_modp->addStmtp(enp); m_modp->addStmtp(newassp); outrefp->user1p(enp); // put __en signal into varref for later usage outrefp->varp()->user1p(enp); // put __en signal into var as well in the event this is a single lhs driver and this needs passed up one level return enp; }
AstNode* newSubNeg(AstNode* lhsp, vlsint32_t rhs) { // Return lhs-rhs, but if rhs is negative use an add, so we won't // have to deal with signed math and related 32bit sign extension problems if (rhs == 0) { return lhsp; } else if (lhsp->castConst()) { // Optional vs just making add/sub below, but saves constification some work V3Number num (lhsp->fileline(), lhsp->width()); num.opSub(lhsp->castConst()->num(), V3Number(lhsp->fileline(), 32, rhs)); num.isSigned(lhsp->isSigned()); AstNode* newp = new AstConst(lhsp->fileline(), num); return newp; } else if (rhs > 0) { AstNode* newp = new AstSub(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs)); // We must make sure sub gets sign of original value, not from the constant newp->dtypeFrom(lhsp); return newp; } else { // rhs < 0; AstNode* newp = new AstAdd(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs)); // We must make sure sub gets sign of original value, not from the constant newp->dtypeFrom(lhsp); return newp; } }
// 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 ParamVisitor::visitCell(AstCell* nodep) { // Cell: Check for parameters in the instantiation. nodep->iterateChildren(*this); if (!nodep->modp()) nodep->v3fatalSrc("Not linked?"); if (nodep->paramsp() || 1 // Need to look for interfaces; could track when one exists, but should be harmless to always do this ) { UINFO(4,"De-parameterize: "<<nodep<<endl); // Create new module name with _'s between the constants if (debug()>=10) nodep->dumpTree(cout,"-cell:\t"); // Evaluate all module constants V3Const::constifyParamsEdit(nodep); // Make sure constification worked // Must be a separate loop, as constant conversion may have changed some pointers. //if (debug()) nodep->dumpTree(cout,"-cel2:\t"); string longname = nodep->modp()->name(); bool any_overrides = false; longname += "_"; if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t"); for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { if (!pinp->exprp()) continue; // No-connect AstVar* modvarp = pinp->modVarp(); if (!modvarp) { pinp->v3error("Parameter not found in sub-module: Param "<<pinp->name()<<" of "<<nodep->prettyName()); } else if (!modvarp->isGParam()) { pinp->v3error("Attempted parameter setting of non-parameter: Param "<<pinp->name()<<" of "<<nodep->prettyName()); } else { AstConst* constp = pinp->exprp()->castConst(); AstConst* origconstp = modvarp->valuep()->castConst(); if (!constp) { //if (debug()) pinp->dumpTree(cout,"error:"); pinp->v3error("Can't convert defparam value to constant: Param "<<pinp->name()<<" of "<<nodep->prettyName()); pinp->exprp()->replaceWith(new AstConst(pinp->fileline(), V3Number(pinp->fileline(), modvarp->width(), 0))); } else if (origconstp && constp->sameTree(origconstp)) { // Setting parameter to its default value. Just ignore it. // This prevents making additional modules, and makes coverage more // obvious as it won't show up under a unique module page name. } else { longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+constp->num().ascii(false); any_overrides = true; } } } IfaceRefRefs ifaceRefRefs; for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { AstVar* modvarp = pinp->modVarp(); if (modvarp->isIfaceRef()) { AstIfaceRefDType* portIrefp = modvarp->subDTypep()->castIfaceRefDType(); //UINFO(9," portIfaceRef "<<portIrefp<<endl); if (!pinp->exprp() || !pinp->exprp()->castVarRef() || !pinp->exprp()->castVarRef()->varp() || !pinp->exprp()->castVarRef()->varp()->subDTypep() || !pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType()) { pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not connected to interface/modport pin expression"); } else { AstIfaceRefDType* pinIrefp = pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType(); //UINFO(9," pinIfaceRef "<<pinIrefp<<endl); if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) { UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl); longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+paramValueNumber(pinIrefp); any_overrides = true; ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp)); } } } } if (!any_overrides) { UINFO(8,"Cell parameters all match original values, skipping expansion.\n"); } else { // If the name is very long, we don't want to overwhelm the filename limit // We don't do this always, as it aids debugability to have intuitive naming. // TODO can use new V3Name hash replacement instead of this string newname = longname; if (longname.length()>30) { LongMap::iterator iter = m_longMap.find(longname); if (iter != m_longMap.end()) { newname = iter->second; } else { newname = nodep->modp()->name(); newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict m_longMap.insert(make_pair(longname, newname)); } } UINFO(4,"Name: "<<nodep->modp()->name()<<"->"<<longname<<"->"<<newname<<endl); // // Already made this flavor? AstNodeModule* modp = NULL; ModNameMap::iterator iter = m_modNameMap.find(newname); if (iter != m_modNameMap.end()) modp = iter->second.m_modp; if (!modp) { // Deep clone of new module // Note all module internal variables will be re-linked to the new modules by clone // However links outside the module (like on the upper cells) will not. modp = nodep->modp()->cloneTree(false); modp->name(newname); modp->user5(false); // We need to re-recurse this module once changed nodep->modp()->addNextHere(modp); // Keep tree sorted by cell occurrences m_modNameMap.insert(make_pair(modp->name(), ModInfo(modp))); iter = m_modNameMap.find(newname); VarCloneMap* clonemapp = &(iter->second.m_cloneMap); UINFO(4," De-parameterize to new: "<<modp<<endl); // Grab all I/O so we can remap our pins later // Note we allow multiple users of a parameterized model, thus we need to stash this info. for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* varp = stmtp->castVar()) { if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) { // Cloning saved a pointer to the new node for us, so just follow that link. AstVar* oldvarp = varp->clonep()->castVar(); //UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp<<" -> 0x"<<(uint32_t)varp<<endl); clonemapp->insert(make_pair(oldvarp, varp)); } } } // Relink parameter vars to the new module relinkPins(clonemapp, nodep->paramsp()); // Fix any interface references for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) { AstIfaceRefDType* portIrefp = it->first; AstIfaceRefDType* pinIrefp = it->second; AstIfaceRefDType* cloneIrefp = portIrefp->clonep()->castIfaceRefDType(); UINFO(8," IfaceOld "<<portIrefp<<endl); UINFO(8," IfaceTo "<<pinIrefp<<endl); if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType"); UINFO(8," IfaceClo "<<cloneIrefp<<endl); cloneIrefp->ifacep(pinIrefp->ifaceViaCellp()); UINFO(8," IfaceNew "<<cloneIrefp<<endl); } // Assign parameters to the constants specified // DOES clone() so must be finished with module clonep() before here for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { AstVar* modvarp = pinp->modVarp(); if (modvarp && pinp->exprp()) { AstConst* constp = pinp->exprp()->castConst(); // Remove any existing parameter if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree(); // Set this parameter to value requested by cell modvarp->valuep(constp->cloneTree(false)); } } } else { UINFO(4," De-parameterize to old: "<<modp<<endl); } // Have child use this module instead. nodep->modp(modp); nodep->modName(newname); // We need to relink the pins to the new module VarCloneMap* clonemapp = &(iter->second.m_cloneMap); relinkPins(clonemapp, nodep->pinsp()); UINFO(8," Done with "<<modp<<endl); } // if any_overrides // Delete the parameters from the cell; they're not relevant any longer. if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); UINFO(8," Done with "<<nodep<<endl); //if (debug()>=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree")); } // Now remember to process the child module at the end of the module m_todoModps.insert(make_pair(nodep->modp()->level(),nodep->modp())); }
bool forUnroller(AstNode* nodep, AstAssign* initp, AstNode* condp, AstNode* precondsp, AstNode* incp, AstNode* bodysp) { V3Number loopValue = V3Number(nodep->fileline()); if (!simulateTree(initp->rhsp(), NULL, initp, loopValue)) { return false; } AstNode* stmtsp = NULL; if (initp) { initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep // Don't add to list, we do it once, and setting loop index isn't needed as we're constant propagating it } if (precondsp) { precondsp->unlinkFrBackWithNext(); // cppcheck-suppress nullPointer // addNextNull deals with it stmtsp = stmtsp->addNextNull(precondsp); } if (bodysp) { bodysp->unlinkFrBackWithNext(); // cppcheck-suppress nullPointer // addNextNull deals with it stmtsp = stmtsp->addNextNull(bodysp); // Maybe null if no body } if (incp && !nodep->castGenFor()) { // Generates don't need to increment loop index incp->unlinkFrBackWithNext(); // cppcheck-suppress nullPointer // addNextNull deals with it stmtsp = stmtsp->addNextNull(incp); // Maybe null if no body } // Mark variable to disable some later warnings m_forVarp->usedLoopIdx(true); AstNode* newbodysp = NULL; ++m_statLoops; if (stmtsp) { int times = 0; while (1) { UINFO(8," Looping "<<loopValue<<endl); V3Number res = V3Number(nodep->fileline()); if (!simulateTree(condp, &loopValue, NULL, res)) { nodep->v3error("Loop unrolling failed."); return false; } if (!res.isEqOne()) { break; // Done with the loop } else { // Replace iterator values with constant. AstNode* oneloopp = stmtsp->cloneTree(true); m_varValuep = new AstConst(nodep->fileline(), loopValue); // Iteration requires a back, so put under temporary node if (oneloopp) { AstBegin* tempp = new AstBegin(oneloopp->fileline(),"[EditWrapper]",oneloopp); m_varModeReplace = true; tempp->stmtsp()->iterateAndNext(*this); m_varModeReplace = false; oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); } if (m_generate) { string index = AstNode::encodeNumber(m_varValuep->toSInt()); string nname = m_beginName + "__BRA__" + index + "__KET__"; oneloopp = new AstBegin(oneloopp->fileline(),nname,oneloopp,true); } pushDeletep(m_varValuep); m_varValuep=NULL; if (newbodysp) newbodysp->addNext(oneloopp); else newbodysp = oneloopp; ++m_statIters; if (++times > unrollCount()*3) { nodep->v3error("Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "<<unrollCount()); break; } // loopValue += valInc AstAssign *incpass = incp->castAssign(); V3Number newLoopValue = V3Number(nodep->fileline()); if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) { nodep->v3error("Loop unrolling failed"); return false; } loopValue.opAssign(newLoopValue); } } } // Replace the FOR() if (newbodysp) nodep->replaceWith(newbodysp); else nodep->unlinkFrBack(); if (bodysp) { pushDeletep(bodysp); VL_DANGLING(bodysp); } if (precondsp) { pushDeletep(precondsp); VL_DANGLING(precondsp); } if (initp) { pushDeletep(initp); VL_DANGLING(initp); } if (incp && !incp->backp()) { pushDeletep(incp); VL_DANGLING(incp); } if (debug()>=9) newbodysp->dumpTree(cout,"- _new: "); return true; }
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); }
virtual void visit(AstArraySel* nodep) { nodep->iterateChildren(*this); if (!nodep->user1SetOnce()) { if (debug()==9) nodep->dumpTree(cout,"-in: "); // Guard against reading/writing past end of arrays AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); bool lvalue = false; if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { lvalue = varrefp->lvalue(); } else if (basefromp->castConst()) { // If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp } else { nodep->v3fatalSrc("No VarRef or Const under ArraySel"); } // Find range of dtype we are selecting from int declElements = -1; AstNodeDType* dtypep = nodep->fromp()->dtypep()->skipRefp(); if (!dtypep) nodep->v3fatalSrc("Select of non-selectable type"); if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) { declElements = adtypep->elementsConst(); } else { nodep->v3error("Select from non-array "<<dtypep->prettyTypeName()); } if (debug()>=9) nodep->dumpTree(cout,"arraysel_old: "); V3Number widthnum (nodep->fileline(), nodep->bitp()->width(), declElements-1); // See if the condition is constant true AstNode* condp = new AstGte (nodep->fileline(), new AstConst(nodep->fileline(), widthnum), nodep->bitp()->cloneTree(false)); // Note below has null backp(); the Edit function knows how to deal with that. condp = V3Const::constifyEdit(condp); if (condp->isOne()) { // We don't need to add a conditional; we know the existing expression is ok condp->deleteTree(); } else if (!lvalue && !nodep->backp()->castArraySel()) { // Too complicated and slow if mid-multidimension // ARRAYSEL(...) -> COND(LT(bit<maxbit), ARRAYSEL(...), {width{1'bx}}) AstNRelinker replaceHandle; nodep->unlinkFrBack(&replaceHandle); V3Number xnum (nodep->fileline(), nodep->width()); if (nodep->isString()) { xnum = V3Number(V3Number::String(), nodep->fileline(), ""); } else { xnum.setAllBitsX(); } AstNode* newp = new AstCondBound (nodep->fileline(), condp, nodep, new AstConst(nodep->fileline(), xnum)); if (debug()>=9) newp->dumpTree(cout," _new: "); // Link in conditional, can blow away temp xor replaceHandle.relink(newp); // Added X's, tristate them too newp->accept(*this); } else if (!lvalue) { // Mid-multidimension read, just use zero // ARRAYSEL(...) -> ARRAYSEL(COND(LT(bit<maxbit), bit, 0)) AstNRelinker replaceHandle; AstNode* bitp = nodep->bitp()->unlinkFrBack(&replaceHandle); V3Number zeronum (nodep->fileline(), bitp->width(), 0); AstNode* newp = new AstCondBound (bitp->fileline(), condp, bitp, new AstConst(bitp->fileline(), zeronum)); // Added X's, tristate them too if (debug()>=9) newp->dumpTree(cout," _new: "); replaceHandle.relink(newp); newp->accept(*this); } else { // lvalue replaceBoundLvalue(nodep, condp); } } }
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; }