virtual void visit(AstUdpTable* nodep, AstNUser*) { UINFO(5,"UDPTABLE "<<nodep<<endl); if (!v3Global.opt.bboxUnsup()) { // We don't warn until V3Inst, so that UDPs that are in libraries and // never used won't result in any warnings. } else { // Massive hack, just tie off all outputs so our analysis can proceed AstVar* varoutp = NULL; for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = stmtp->castVar()) { if (varp->isInput()) { } else if (varp->isOutput()) { if (varoutp) { varp->v3error("Multiple outputs not allowed in udp modules"); } varoutp = varp; // Tie off m_modp->addStmtp(new AstAssignW(varp->fileline(), new AstVarRef(varp->fileline(), varp, true), new AstConst(varp->fileline(), AstConst::LogicFalse()))); } else { varp->v3error("Only inputs and outputs are allowed in udp modules"); } } } nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } }
virtual void visit(AstDefImplicitDType* nodep, AstNUser*) { cleanFileline(nodep); UINFO(8," DEFIMPLICIT "<<nodep<<endl); // Must remember what names we've already created, and combine duplicates // so that for "var enum {...} a,b" a & b will share a common typedef // Unique name space under each containerp() so that an addition of a new type won't change every verilated module. AstTypedef* defp = NULL; ImplTypedefMap::iterator it = m_implTypedef.find(make_pair(nodep->containerp(), nodep->name())); if (it != m_implTypedef.end()) { defp = it->second; } else { // Definition must be inserted right after the variable (etc) that needed it // AstVar, AstTypedef, AstNodeFTask are common containers AstNode* backp = nodep->backp(); for (; backp; backp=backp->backp()) { if (backp->castVar()) break; else if (backp->castTypedef()) break; else if (backp->castNodeFTask()) break; } if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type"); AstNodeDType* dtypep = nodep->dtypep(); dtypep->unlinkFrBack(); if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing // For typedefs just remove the AstRefDType level of abstraction nodep->replaceWith(dtypep); nodep->deleteTree(); nodep=NULL; return; } else { defp = new AstTypedef(nodep->fileline(), nodep->name(), dtypep); m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp)); backp->addNextHere(defp); } } nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name())); nodep->deleteTree(); nodep=NULL; }
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { // Put label under given node, and if WHILE optionally at end of iteration UINFO(4,"Create label for "<<nodep<<endl); if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done AstNode* underp = NULL; bool under_and_next = true; if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp(); else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp(); else if (nodep->castWhile()) { if (endOfIter) { // Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip underp = nodep->castWhile()->bodysp(); } else { underp = nodep; under_and_next=false; // IE we skip the entire while } } else { nodep->v3fatalSrc("Unknown jump point for break/disable/continue"); return NULL; } // Skip over variables as we'll just move them in a momement // Also this would otherwise prevent us from using a label twice // see t_func_return test. while (underp && underp->castVar()) underp = underp->nextp(); if (underp) UINFO(5," Underpoint is "<<underp<<endl); if (!underp) { nodep->v3fatalSrc("Break/disable/continue not under expected statement"); return NULL; } else if (underp->castJumpLabel()) { return underp->castJumpLabel(); } else { // Move underp stuff to be under a new label AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL); AstNRelinker repHandle; if (under_and_next) underp->unlinkFrBackWithNext(&repHandle); else underp->unlinkFrBack(&repHandle); repHandle.relink(labelp); labelp->addStmtsp(underp); // Keep any AstVars under the function not under the new JumpLabel for (AstNode* nextp, *varp=underp; varp; varp = nextp) { nextp = varp->nextp(); if (varp->castVar()) { labelp->addPrev(varp->unlinkFrBack()); } } return labelp; } }
void makeSmallNames(AstNodeModule* modp) { vector<int> usedLetter; usedLetter.resize(256); // Pass 1, assign first letter to each gparam's name for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = stmtp->castVar()) { if (varp->isGParam()||varp->isIfaceRef()) { char ch = varp->name()[0]; ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z'; varp->user4(usedLetter[static_cast<int>(ch)]*256 + ch); usedLetter[static_cast<int>(ch)]++; } } } }
void EmitCSyms::emitDpiImp() { UINFO(6,__FUNCTION__<<": "<<endl); string filename = v3Global.opt.makeDir()+"/"+topClassName()+"__Dpi.cpp"; AstCFile* cfilep = newCFile(filename, false/*slow*/, true/*source*/); cfilep->support(true); V3OutCFile hf (filename); m_ofp = &hf; m_ofp->putsHeader(); puts("// DESCR" "IPTION: Verilator output: Implementation of DPI export functions.\n"); puts("//\n"); puts("// Verilator compiles this file in when DPI functions are used.\n"); puts("// If you have multiple Verilated designs with the same DPI exported\n"); puts("// function names, you will get multiple definition link errors from here.\n"); puts("// This is an unfortunate result of the DPI specification.\n"); puts("// To solve this, either\n"); puts("// 1. Call "+topClassName()+"::{export_function} instead,\n"); puts("// and do not even bother to compile this file\n"); puts("// or 2. Compile all __Dpi.cpp files in the same compiler run,\n"); puts("// and #ifdefs already inserted here will sort everything out.\n"); puts("\n"); puts("#include \""+topClassName()+"__Dpi.h\"\n"); puts("#include \""+topClassName()+".h\"\n"); puts("\n"); for (vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) { AstCFunc* nodep = *it; if (nodep->dpiExportWrapper()) { puts("#ifndef _VL_DPIDECL_"+nodep->name()+"\n"); puts("#define _VL_DPIDECL_"+nodep->name()+"\n"); puts(nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+") {\n"); puts("// DPI Export at "+nodep->fileline()->ascii()+"\n"); puts("return "+topClassName()+"::"+nodep->name()+"("); string args; for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO() && !portp->isFuncReturn()) { if (args != "") args+= ", "; args += portp->name(); } } } puts(args+");\n"); puts("}\n"); puts("#endif\n"); puts("\n"); } } }
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { AstNode* pinp = nodep->pinsp(); AstNodeFTask* taskp = nodep->taskp(); // We'll deal with mismatching pins later if (!taskp) return; for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO()) { if (portp->isInput()) { pinp->iterate(*this); } else { // Output or Inout m_setRefLvalue = true; pinp->iterate(*this); m_setRefLvalue = false; } // Advance pin pinp = pinp->nextp(); } } } }
void makePublicFuncWrappers() { // We recorded all public functions in m_modFuncs. // If for any given name only one function exists, we can use that function directly. // If multiple functions exist, we need to select the appropriate scope. for (FuncMmap::iterator it = m_modFuncs.begin(); it!=m_modFuncs.end(); ++it) { string name = it->first; AstCFunc* topFuncp = it->second; FuncMmap::iterator nextIt1 = it; ++nextIt1; bool moreOfSame1 = (nextIt1!=m_modFuncs.end() && nextIt1->first == name); if (moreOfSame1) { // Multiple functions under this name, need a wrapper function UINFO(6," Wrapping "<<name<<" multifuncs\n"); AstCFunc* newfuncp = topFuncp->cloneTree(false); if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree(); if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree(); newfuncp->name(name); newfuncp->isStatic(false); newfuncp->addInitsp( new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); newfuncp->addInitsp(new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); topFuncp->addNextHere(newfuncp); // In the body, call each function if it matches the given scope for (FuncMmap::iterator eachIt = it; eachIt!=m_modFuncs.end() && eachIt->first==name; ++eachIt) { it = eachIt; AstCFunc* funcp = eachIt->second; FuncMmap::iterator nextIt2 = eachIt; ++nextIt2; bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name); if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped"); UINFO(6," Wrapping "<<name<<" "<<funcp<<endl); UINFO(6," at "<<newfuncp->argTypes()<<" und "<<funcp->argTypes()<<endl); funcp->declPrivate(true); AstNode* argsp = NULL; for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO() && !portp->isFuncReturn()) { argsp = argsp->addNextNull(new AstVarRef(portp->fileline(), portp, portp->isOutput())); } } } AstNode* returnp = new AstCReturn (funcp->fileline(), new AstCCall (funcp->fileline(), funcp, argsp)); if (moreOfSame) { AstIf* ifp = new AstIf (funcp->fileline(), new AstEq(funcp->fileline(), new AstCMath(funcp->fileline(), "this", 64), new AstCMath(funcp->fileline(), string("&(") +funcp->scopep()->nameVlSym() +")", 64)), returnp, NULL); newfuncp->addStmtsp(ifp); } else { newfuncp->addStmtsp(returnp); } } // Not really any way the user could do this, and we'd need to come up with some return value //newfuncp->addStmtsp(new AstDisplay (newfuncp->fileline(), AstDisplayType::DT_DISPLAY, // string("%%Error: ")+name+"() called with bad scope", NULL)); //newfuncp->addStmtsp(new AstStop (newfuncp->fileline())); if (debug()>=9) newfuncp->dumpTree(cout," newfunc: "); } else { // Only a single function under this name, we can simply rename it UINFO(6," Wrapping "<<name<<" just one "<<topFuncp<<endl); topFuncp->name(name); } } }