void genChangeDet(AstVarScope* vscp) { #ifdef NEW_ORDERING vscp->v3fatalSrc("Not applicable\n"); #endif AstVar* varp = vscp->varp(); vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp); AstUnpackArrayDType* arrayp = varp->dtypeSkipRefp()->castUnpackArrayDType(); AstStructDType *structp = varp->dtypeSkipRefp()->castStructDType(); bool isArray = arrayp; bool isStruct = structp && structp->packedUnsup(); int elements = isArray ? arrayp->elementsConst() : 1; if (isArray && (elements > DETECTARRAY_MAX_INDEXES)) { vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<<cvtToStr(DETECTARRAY_MAX_INDEXES) <<" array indexes (probably with UNOPTFLAT warning suppressed): "<<varp->prettyName()<<endl <<vscp->warnMore() <<"... Could recompile with DETECTARRAY_MAX_INDEXES increased to at least "<<cvtToStr(elements)); } else if (!isArray && !isStruct && !varp->dtypeSkipRefp()->castBasicDType()) { if (debug()) varp->dumpTree(cout,"-DETECTARRAY-"); vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable (probably with UNOPTFLAT warning suppressed): "<<varp->prettyName()); } else { string newvarname = "__Vchglast__"+vscp->scopep()->nameDotless()+"__"+varp->shortName(); // Create: VARREF(_last) // ASSIGN(VARREF(_last), VARREF(var)) // ... // CHANGEDET(VARREF(_last), VARREF(var)) AstVar* newvarp = new AstVar (varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); m_topModp->addStmtp(newvarp); AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp); m_scopetopp->addVarp(newvscp); for (int index=0; index<elements; ++index) { AstChangeDet* changep = new AstChangeDet (vscp->fileline(), aselIfNeeded(isArray, index, new AstVarRef(vscp->fileline(), vscp, false)), aselIfNeeded(isArray, index, new AstVarRef(vscp->fileline(), newvscp, false)), false); m_chgFuncp->addStmtsp(changep); AstAssign* initp = new AstAssign (vscp->fileline(), aselIfNeeded(isArray, index, new AstVarRef(vscp->fileline(), newvscp, true)), aselIfNeeded(isArray, index, new AstVarRef(vscp->fileline(), vscp, false))); m_chgFuncp->addFinalsp(initp); } } }
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())); }
void reportViolations() { // Combine bits into overall state AstVar* nodep = m_varp; if (!nodep->isParam() && !nodep->isGenVar()) { bool allU=true; bool allD=true; bool anyU=m_usedWhole; bool anyD=m_drivenWhole; bool anyUnotD=false; bool anyDnotU=false; bool anynotDU=false; for (unsigned bit=0; bit<m_flags.size()/FLAGS_PER_BIT; bit++) { bool used = usedFlag(bit); bool driv = drivenFlag(bit); allU &= used; anyU |= used; allD &= driv; anyD |= driv; anyUnotD |= used && !driv; anyDnotU |= !used && driv; anynotDU |= !used && !driv; } if (allU) m_usedWhole = true; if (allD) m_drivenWhole = true; // Test results if (allU && allD) { // It's fine } else if (!anyD && !anyU) { // UNDRIVEN is considered more serious - as is more likely a bug, // thus undriven+unused bits get UNUSED warnings, as they're not as buggy. if (!unusedMatch(nodep)) { nodep->v3warn(UNUSED, "Signal is not driven, nor used: "<<nodep->prettyName()); nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once } } else if (allD && !anyU) { if (!unusedMatch(nodep)) { nodep->v3warn(UNUSED, "Signal is not used: "<<nodep->prettyName()); nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once } } else if (!anyD && allU) { nodep->v3warn(UNDRIVEN, "Signal is not driven: "<<nodep->prettyName()); nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once } else { // Bits have different dispositions bool setU=false; bool setD=false; if (anynotDU && !unusedMatch(nodep)) { nodep->v3warn(UNUSED, "Bits of signal are not driven, nor used: "<<nodep->prettyName() <<bitNames(BN_BOTH)); setU=true; } if (anyDnotU && !unusedMatch(nodep)) { nodep->v3warn(UNUSED, "Bits of signal are not used: "<<nodep->prettyName() <<bitNames(BN_UNUSED)); setU=true; } if (anyUnotD) { nodep->v3warn(UNDRIVEN, "Bits of signal are not driven: "<<nodep->prettyName() <<bitNames(BN_UNDRIVEN)); setD=true; } if (setU) nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once if (setD) nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once } } }
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"); // // 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"); 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->prettyName()<<endl); } if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change // This check shouldn't be needed when using V3Simulate // however, for repeat loops, the loop variable is auto-generated // and the initp statements will reference a variable outside of the initp scope // alas, failing to simulate. AstConst* constInitp = initAssp->rhsp()->castConst(); if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); // // 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"); // 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: "); if (!m_generate) { AstAssign *incpAssign = incp->castAssign(); if (!canSimulate(incpAssign->rhsp())) return cantUnroll(incp, "Unable to simulate increment"); if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition"); // Check whether to we actually want to try and unroll. int loops; if (!countLoops(initAssp, condp, incp, unrollCount(), loops)) return cantUnroll(nodep, "Unable to simulate loop"); // 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"); } } // Finally, we can do it if (!forUnroller(nodep, initAssp, condp, precondsp, incp, bodysp)) { return cantUnroll(nodep, "Unable to unroll loop"); } VL_DANGLING(nodep); // Cleanup return true; }
AstVarScope* getCreateLastClk(AstVarScope* vscp) { if (vscp->user1p()) return ((AstVarScope*)vscp->user1p()); AstVar* varp = vscp->varp(); if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName()); string newvarname = ((string)"__Vclklast__"+vscp->scopep()->nameDotless()+"__"+varp->name()); AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1); newvarp->noReset(true); // Reset by below assign m_modp->addStmtp(newvarp); AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp); vscp->user1p(newvscp); m_scopep->addVarp(newvscp); // Add init AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false); if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp); AstNode* newinitp = new AstAssign(vscp->fileline(), new AstVarRef(newvarp->fileline(), newvscp, true), fromp); addToInitial(newinitp); // At bottom, assign them AstAssign* finalp = new AstAssign(vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, true), new AstVarRef(vscp->fileline(), vscp, false)); m_evalFuncp->addFinalsp(finalp); // UINFO(4,"New Last: "<<newvscp<<endl); return newvscp; }