void relinkPins(VarCloneMap* clonemapp, AstPin* startpinp) { for (AstPin* pinp = startpinp; pinp; pinp=pinp->nextp()->castPin()) { if (!pinp->modVarp()) pinp->v3fatalSrc("Not linked?\n"); // Find it in the clone structure //UINFO(8,"Clone find 0x"<<hex<<(uint32_t)pinp->modVarp()<<endl); VarCloneMap::iterator cloneiter = clonemapp->find(pinp->modVarp()); UASSERT(cloneiter != clonemapp->end(), "Couldn't find pin in clone list"); pinp->modVarp(cloneiter->second); } }
virtual void visit(AstCell* nodep, AstNUser*) { UINFO(4," CELL "<<nodep<<endl); m_cellp = nodep; //VV***** We reset user1p() on each cell!!! AstNode::user1ClearTree(); // Collect pin expressions, so submod->varp->user1p() points to expression it connects to for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { pinp->modVarp()->user1p(pinp->exprp()); } nodep->iterateChildren(*this); m_cellp = NULL; }
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())); }
virtual void visit(AstCell* nodep, AstNUser*) { if (nodep->modp()->user1()) { // Marked with inline request UINFO(5," Inline CELL "<<nodep<<endl); UINFO(5," To MOD "<<m_modp<<endl); ++m_statCells; // Before cloning simplify pin assignments // Better off before, as if module has multiple instantiations // we'll save work, and we can't call pinReconnectSimple in // this loop as it clone()s itself. for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (!pinp->exprp()) continue; V3Inst::pinReconnectSimple(pinp, nodep, m_modp, false); } // Clone original module if (debug()>=9) { nodep->dumpTree(cout,"inlcell:"); } //if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); } AstNodeModule* newmodp = nodep->modp()->cloneTree(false); if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); } // Clear var markings and find cell cross references AstNode::user2ClearTree(); AstNode::user4ClearTree(); { InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately // Create data for dotted variable resolution AstCellInline* inlinep = new AstCellInline(nodep->fileline(), nodep->name(), nodep->modp()->origName()); m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells // Create assignments to the pins for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (!pinp->exprp()) continue; UINFO(6," Pin change from "<<pinp->modVarp()<<endl); // Make new signal; even though we'll optimize the interconnect, we // need an alias to trace correctly. If tracing is disabled, we'll // delete it in later optimizations. AstVar* pinOldVarp = pinp->modVarp(); AstVar* pinNewVarp = pinOldVarp->clonep()->castVar(); AstNode* connectRefp = pinp->exprp(); if (!connectRefp->castConst() && !connectRefp->castVarRef()) { pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); } if (pinNewVarp->isOutOnly() && connectRefp->castConst()) { pinp->v3error("Output port is connected to a constant pin, electrical short"); } // Propagate any attributes across the interconnect pinNewVarp->propagateAttrFrom(pinOldVarp); if (connectRefp->castVarRef()) { connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp); } // One to one interconnect won't make a temporary variable. // This prevents creating a lot of extra wires for clock signals. // It will become a tracing alias. UINFO(6,"One-to-one "<<connectRefp<<endl); UINFO(6," -to "<<pinNewVarp<<endl); pinNewVarp->user2p(connectRefp); // Public output inside the cell must go via an assign rather than alias // Else the public logic will set the alias, loosing the value to be propagated up // (InOnly isn't a problem as the AssignAlias will create the assignment for us) pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly()); } // Cleanup var names, etc, to not conflict { InlineRelinkVisitor(newmodp, m_modp, nodep); } // Move statements to top module if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); } AstNode* stmtsp = newmodp->stmtsp(); if (stmtsp) stmtsp->unlinkFrBackWithNext(); if (stmtsp) m_modp->addStmtp(stmtsp); // Remove the cell newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc nodep->unlinkFrBack(); pushDeletep(nodep); nodep = NULL; if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); } } }
virtual void visit(AstCell* nodep, AstNUser*) { // Cell: Resolve its filename. If necessary, parse it. if (nodep->user1SetOnce()) return; // AstBind and AstNodeModule may call a cell twice if (!nodep->modp()) { UINFO(4,"Link Cell: "<<nodep<<endl); // Use findIdFallback instead of findIdFlat; it doesn't matter for now // but we might support modules-under-modules someday. AstNodeModule* modp = resolveModule(nodep,nodep->modName()); if (modp) { nodep->modp(modp); // Track module depths, so can sort list from parent down to children new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false); } } // Remove AstCell(AstPin("",NULL)), it's a side effect of how we parse "()" // the empty middle is identical to the empty rule that must find pins in "(,)". if (nodep->pinsp() && !nodep->pinsp()->nextp() && nodep->pinsp()->name() == "" && !nodep->pinsp()->exprp()) { pushDeletep(nodep->pinsp()->unlinkFrBackWithNext()); } if (nodep->paramsp() && !nodep->paramsp()->nextp() && nodep->paramsp()->name() == "" && !nodep->paramsp()->exprp()) { pushDeletep(nodep->paramsp()->unlinkFrBackWithNext()); } // Convert .* to list of pins bool pinStar = false; for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) { nextp = pinp->nextp()->castPin(); if (pinp->dotStar()) { if (pinStar) pinp->v3error("Duplicate .* in a cell"); pinStar = true; // Done with this fake pin pinp->unlinkFrBack()->deleteTree(); pinp=NULL; } } // Convert unnamed pins to pin number based assignments for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum())); } for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum())); } if (nodep->modp()) { // Note what pins exist set<string> ports; // Symbol table of all connected port names for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells"); if (!pinp->exprp()) pinp->v3warn(PINNOCONNECT,"Cell pin is not connected: "<<pinp->prettyName()); if (ports.find(pinp->name()) == ports.end()) { ports.insert(pinp->name()); } } // We search ports, rather than in/out declarations as they aren't resolved yet, // and it's easier to do it now than in V3LinkDot when we'd need to repeat steps. for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep; portnodep=portnodep->nextp()) { if (AstPort* portp = portnodep->castPort()) { if (ports.find(portp->name()) == ports.end() && ports.find("__pinNumber"+cvtToStr(portp->pinNum())) == ports.end()) { if (pinStar) { UINFO(9," need .* PORT "<<portp<<endl); // Create any not already connected AstPin* newp = new AstPin(nodep->fileline(),0,portp->name(), new AstVarRef(nodep->fileline(),portp->name(),false)); newp->svImplicit(true); nodep->addPinsp(newp); } else { // warn on the CELL that needs it, not the port nodep->v3warn(PINMISSING, "Cell has missing pin: "<<portp->prettyName()); AstPin* newp = new AstPin(nodep->fileline(),0,portp->name(),NULL); nodep->addPinsp(newp); } } } } } if (nodep->modp()->castIface()) { // Cell really is the parent's instantiation of an interface, not a normal module // Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember> // in the same way that a child does. Rename though to avoid conflict with cell. // This is quite similar to how classes work; when unpacked classes are better supported // may remap interfaces to be more like a class. if (!nodep->hasIfaceVar()) { string varName = nodep->name()+"__Viftop"; // V3LinkDot looks for this naming AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), nodep->modp()->name()); idtypep->cellp(nodep); // Only set when real parent cell known idtypep->ifacep(NULL); // cellp overrides AstVar* varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep); varp->isIfaceParent(true); nodep->addNextHere(varp); nodep->hasIfaceVar(true); } } if (nodep->modp()) { nodep->iterateChildren(*this); } UINFO(4," Link Cell done: "<<nodep<<endl); }