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); }
void varsExpand() { // We didn'e have all m_scopes loaded when we encountered variables, so expand them now // It would be less code if each module inserted its own variables. // Someday. For now public isn't common. for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { AstScope* scopep = it->first; AstNodeModule* smodp = it->second; for (vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) { AstNodeModule* modp = it->first; AstVar* varp = it->second; if (modp == smodp) { // Need to split the module + var name into the original-ish full scope and variable name under that scope. // The module instance name is included later, when we know the scopes this module is under string whole = scopep->name()+"__DOT__"+varp->name(); string scpName; string varBase; if (whole.substr(0,10) == "__DOT__TOP") whole.replace(0,10,""); string::size_type pos = whole.rfind("__DOT__"); if (pos != string::npos) { scpName = whole.substr(0,pos); varBase = whole.substr(pos+strlen("__DOT__")); } else { varBase = whole; } //UINFO(9,"For "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<" Var "<<varBase<<endl); string varBasePretty = AstNode::prettyName(varBase); string scpPretty = AstNode::prettyName(scpName); string scpSym; { string out = scpName; string::size_type pos; while ((pos=out.find("__PVT__")) != string::npos) { out.replace(pos, 7, ""); } if (out.substr(0,10) == "TOP__DOT__") out.replace(0,10,""); if (out.substr(0,4) == "TOP.") out.replace(0,4,""); while ((pos=out.find(".")) != string::npos) { out.replace(pos, 1, "__"); } while ((pos=out.find("__DOT__")) != string::npos) { out.replace(pos, 7, "__"); } scpSym = out; } //UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl); if (m_scopeNames.find(scpSym) == m_scopeNames.end()) { m_scopeNames.insert(make_pair(scpSym, ScopeNameData(scpSym, scpPretty))); } m_scopeVars.insert(make_pair(scpSym + " " + varp->name(), ScopeVarData(scpSym, varBasePretty, varp, modp, scopep))); } } } }
virtual void visit(AstVar* nodep, AstNUser*) { if (m_state == CONVERT_VARS) { if (nodep->isTristate() && !m_ftaskp) { // create the input var and leave the original as the output var AstVar* varinp = nodep->cloneTree(false)->castVar(); varinp->name(varinp->name() + "__in"); varinp->varType2In(); nodep->combineType(AstVarType::OUTPUT); nodep->varType2Out(); m_modp->addStmtp(varinp); nodep->user1p(varinp); } } }
void AstPrinterVisitor::visitBlockNode(BlockNode* node) { if (!isMainScope(node->scope())) { _output << "{" << endl; } Scope::VarIterator varIt(node->scope()); while(varIt.hasNext()) { AstVar* var = varIt.next(); printVarType(var->type()); _output << " " << var->name(); printSemicolon(); } Scope::FunctionIterator funcIt(node->scope()); while(funcIt.hasNext()) { FunctionNode* func = funcIt.next()->node(); func->visit(this); } for (uint32_t i = 0; i < node->nodes(); ++i) { node->nodeAt(i)->visit(this); if (!(node->nodeAt(i)->isIfNode() || node->nodeAt(i)->isWhileNode() || node->nodeAt(i)->isForNode() || node->nodeAt(i)->isReturnNode() || node->nodeAt(i)->isBlockNode())) { printSemicolon(); } } if (!isMainScope(node->scope())) { _output << "}" << endl; } }
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; }
virtual void visit(AstVarScope* nodep, AstNUser*) { nodep->iterateChildren(*this); // Avoid updating this if (), instead see varp->isTrace() if (!nodep->varp()->isTemp() && !nodep->varp()->isFuncLocal()) { UINFO(5, " vsc "<<nodep<<endl); AstVar* varp = nodep->varp(); AstScope* scopep = nodep->scopep(); // Compute show name // This code assumes SPTRACEVCDC_VERSION >= 1330; // it uses spaces to separate hierarchy components. m_traShowname = AstNode::vcdName(scopep->name() + " " + varp->name()); if (m_traShowname.substr(0,4) == "TOP ") m_traShowname.replace(0,4,""); if (!m_initSubFuncp) nodep->v3fatalSrc("NULL"); m_traVscp = nodep; m_traValuep = NULL; if (varIgnoreTrace(varp)) { addIgnore(varIgnoreTrace(varp)); } else { ++m_statSigs; if (nodep->valuep()) m_traValuep = nodep->valuep()->cloneTree(true); else m_traValuep = new AstVarRef(nodep->fileline(), nodep, false); { // Recurse into data type of the signal; the visitors will call addTraceDecl() varp->dtypeSkipRefp()->accept(*this); } // Cleanup if (m_traValuep) { m_traValuep->deleteTree(); m_traValuep=NULL; } } m_traVscp = NULL; m_traValuep = NULL; m_traShowname = ""; } }
void Printer::printBlockContents(BlockNode* node) { // functions delcarations Scope::FunctionIterator funIt(node->scope()); while (funIt.hasNext()) { funIt.next()->node()->visit(this); out << '\n'; } // variables declarations Scope::VarIterator varIt(node->scope()); while (varIt.hasNext()) { AstVar* var = varIt.next(); out << typeToName(var->type()) << " " << var->name() << ";\n"; } // nodes for (size_t i = 0; i < node->nodes(); ++i) { AstNode* subNode = node->nodeAt(i); subNode->visit(this); if (!subNode->isIfNode() && !subNode->isWhileNode() && !subNode->isForNode()) { out << ';'; } out << '\n'; } }
void drivenBit (int bit, int width) { UINFO(9, "set d["<<(bit+width-1)<<":"<<bit<<"] "<<m_varp->name()<<endl); for (int i=0; i<width; i++) { if (bitNumOk(bit+i)) { m_flags[(bit+i)*FLAGS_PER_BIT + FLAG_DRIVEN] = true; } } }
void printVars(Scope *scope) { Scope::VarIterator iter(scope); while (iter.hasNext()) { AstVar *astVar = iter.next(); out << typeToName(astVar->type()) << " " << astVar->name() << ";" << endl; } }
void variableDeclaration(Scope* scope) { Scope::VarIterator iter(scope); while (iter.hasNext()) { AstVar* x = iter.next(); indent(); _output << typeToName(x->type()) << " " << x->name() << ";" << endl; } }
ScopeHandler::ScopeHandler(uint16_t iniLocalId, Scope *scope, ScopeHandler *parent) : m_scope(scope) , m_parent(parent) { Scope::VarIterator it = Scope::VarIterator(m_scope); while(it.hasNext()) { AstVar *var = it.next(); m_varNameToId[var->name()] = iniLocalId; iniLocalId++; } }
void ASTAnalyzer::printScopeDeclarations (Scope* scope) { Scope::FunctionIterator fuctions(scope); while (fuctions.hasNext()) { fuctions.next()->node()->visit(this); } Scope::VarIterator vars(scope); while (vars.hasNext()) { AstVar* var = vars.next(); output << typeToName(var->type()) << " " << var->name() << ";\n"; } }
// private: // visitBlockNodeImpl void BytecodeGenerator::visitVarDecls(BlockNode* node) { Bytecode* bc = _state.currentBcToFill(); Scope::VarIterator varIt(node->scope()); while(varIt.hasNext()) { AstVar* var = varIt.next(); uint16_t varId = _state.currentCtxAddVar(var->name(), node->position()); Instruction bcLoad0 = typedInsn(var->type(), BC_ILOAD0, BC_DLOAD0, BC_SLOAD0); bc->addInsn(bcLoad0); Instruction bcStore = typedInsn(var->type(), BC_STOREIVAR, BC_STOREDVAR, BC_STORESVAR); genBcInsnWithId(bc, bcStore, varId); } }
void visitScope(Scope * scope) { Scope::VarIterator varIter(scope); while (varIter.hasNext()) { addIndent(); AstVar * var = varIter.next(); out<< typeToName(var->type())<< " "<< var->name()<< ";"<< endl; } Scope::FunctionIterator funcIter(scope); while (funcIter.hasNext()) { funcIter.next()->node()->visit(this); } }
void Ast2SrcVisitor::initScope(Scope* scope) { std::string indent(_indent, ' '); Scope::VarIterator varIt(scope); while (varIt.hasNext()) { AstVar* var = varIt.next(); _out << indent << mathvm::typeToName(var->type()) << " "<< var->name() << ";" << std::endl; } Scope::FunctionIterator funcIt(scope); while (funcIt.hasNext()) { funcIt.next()->node()->visit(this); _out << std::endl; } }
void PrettyPrinter::printScope(Scope *scope) { std::string indentation(m_indent, ' '); //why constructor doesn't get const pointer? Scope::VarIterator ivar(scope); //Java-style iterators in C++? while (ivar.hasNext()) { AstVar *var = ivar.next(); m_out << indentation << typeToName(var->type()) << " " << var->name() << ";" << std::endl; } Scope::FunctionIterator ifun(scope); while (ifun.hasNext()) ifun.next()->node()->visit(this); }
void AstPrinter::printScopeDeclarations(Scope* scope) { Scope::VarIterator varIterator(scope); while(varIterator.hasNext()) { AstVar *var = varIterator.next(); print(typeToName(var->type())); print(" "); print(var->name()); print(";\n"); } Scope::FunctionIterator funcIterator(scope); while(funcIterator.hasNext()) { AstFunction *func = funcIterator.next(); printFunction(func); } print("\n"); }
void printScope(Scope * scope) { Scope::VarIterator varIt(scope); while(varIt.hasNext()) { AstVar* var = varIt.next(); os << indent(); os << typeToName(var->type()) << " " << var->name() << ";" << endl; } Scope::FunctionIterator funcIt(scope); while(funcIt.hasNext()) { AstFunction* func = funcIt.next(); os << indent(); func->node()->visit(this); } }
void BytecodeVisitor::visitBlockNode(BlockNode *node) { LOG_Visitor("visitBlockNode"); Scope::VarIterator variableIterator(node->scope()); while (variableIterator.hasNext()) { AstVar *var = variableIterator.next(); context->introduceVariable(var->type(), var->name()); } Scope::FunctionIterator functionIterator(node->scope()); while (functionIterator.hasNext()) { context->introduceFunction(new BytecodeFunction(functionIterator.next())); } node->visitChildren(this); functionIterator = Scope::FunctionIterator(node->scope()); while (functionIterator.hasNext()) { visitFunctionNode(functionIterator.next()->node()); } }
void ByteCodeVisitor::initVars(Scope *scope) { if (!scope) return; Scope::VarIterator varIt(scope); while (varIt.hasNext()) { AstVar *var = varIt.next(); if (!var->info()) { var->setInfo(new AstVarInfo(currentBytecodeFunction->id(), currentBytecodeFunction->localsNumber())); currentBytecodeFunction->setLocalsNumber(currentBytecodeFunction->localsNumber() + 1); if (currentBytecodeFunction->localsNumber() >= UINT16_MAX) { error("vars overflow in function <%s,id>", currentBytecodeFunction->name().c_str(), currentBytecodeFunction->id()); } } else { AstVarInfo *varInfo = (AstVarInfo *) var->info(); TranslatedFunction *function = interpreterCode->functionById(varInfo->contextId); error("variable already declared var <%s,%d,%d> for function %s", var->name().c_str(), varInfo->contextId, varInfo->id, function->name().c_str()); } } }
void SourceByASTPrinter::visitBlockNodeWithoutBraces(BlockNode *node){ Scope::VarIterator vIt(node->scope()); while(vIt.hasNext()) { AstVar *var = vIt.next(); cout<<typeToName(var->type())<<" "<< var->name()<< ";"<<endl; } Scope::FunctionIterator fIt(node->scope()); while(fIt.hasNext()) { fIt.next()->node()->visit(this); } int nodesNumber = node->nodes(); for (int i = 0; i < nodesNumber; ++i) { AstNode *nodeAt = node->nodeAt(i); nodeAt->visit(this); if(!nodeAt->isForNode() && !nodeAt->isIfNode() && !nodeAt->isWhileNode()) cout<<";"<<endl; } }
// METHODS AstVarScope* genInpClk(AstVarScope* vscp) { if (vscp->user2p()) { return VN_CAST(vscp->user2p(), VarScope); } else { AstVar* varp = vscp->varp(); string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name(); // Create: VARREF(inpclk) // ... // ASSIGN(VARREF(inpclk), 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); AstAssign* asninitp = new AstAssign(vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, true), new AstVarRef(vscp->fileline(), vscp, false)); m_scopetopp->addFinalClkp(asninitp); // vscp->user2p(newvscp); return newvscp; } }
void EmitCSyms::emitSymImp() { UINFO(6,__FUNCTION__<<": "<<endl); string filename = v3Global.opt.makeDir()+"/"+symClassName()+".cpp"; AstCFile* cfilep = newCFile(filename, true/*slow*/, true/*source*/); cfilep->support(true); V3OutCFile cf (filename); m_ofp = &cf; ofp()->putsHeader(); puts("// DESCR" "IPTION: Verilator output: Symbol table implementation internals\n"); puts("\n"); // Includes puts("#include \""+symClassName()+".h\"\n"); for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) { puts("#include \""+modClassName(nodep)+".h\"\n"); } //puts("\n// GLOBALS\n"); puts("\n// FUNCTIONS\n"); puts(symClassName()+"::"+symClassName()+"("+topClassName()+"* topp, const char* namep)\n"); puts("\t// Setup locals\n"); puts("\t: __Vm_namep(namep)\n"); // No leak, as we get destroyed when the top is destroyed puts("\t, __Vm_activity(false)\n"); puts("\t, __Vm_didInit(false)\n"); puts("\t// Setup submodule names\n"); char comma=','; for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { AstScope* scopep = it->first; AstNodeModule* modp = it->second; if (modp->isTop()) { } else { ofp()->printf("\t%c %-30s ", comma, scopep->nameDotless().c_str()); puts("(Verilated::catName(topp->name(),"); // The "." is added by catName putsQuoted(scopep->prettyName()); puts("))\n"); comma=','; } } puts("{\n"); puts("// Pointer to top level\n"); puts("TOPp = topp;\n"); puts("// Setup each module's pointers to their submodules\n"); for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { AstScope* scopep = it->first; AstNodeModule* modp = it->second; if (!modp->isTop()) { string arrow = scopep->name(); string::size_type pos; while ((pos=arrow.find(".")) != string::npos) { arrow.replace(pos, 1, "->"); } if (arrow.substr(0,5) == "TOP->") arrow.replace(0,5,"TOPp->"); ofp()->printf("%-30s ", arrow.c_str()); puts(" = &"); puts(scopep->nameDotless()+";\n"); } } puts("// Setup each module's pointer back to symbol table (for public functions)\n"); puts("TOPp->__Vconfigure(this, true);\n"); for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { AstScope* scopep = it->first; AstNodeModule* modp = it->second; if (!modp->isTop()) { // first is used by AstCoverDecl's call to __vlCoverInsert bool first = !modp->user1(); modp->user1(true); puts(scopep->nameDotless()+".__Vconfigure(this, " +(first?"true":"false") +");\n"); } } puts("// Setup scope names\n"); for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { puts("__Vscope_"+it->second.m_symName+".configure(this,name(),"); putsQuoted(it->second.m_prettyName); puts(");\n"); } if (v3Global.dpi()) { puts("// Setup export functions\n"); puts("for (int __Vfinal=0; __Vfinal<2; __Vfinal++) {\n"); for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { AstScopeName* scopep = it->second.m_scopep; AstCFunc* funcp = it->second.m_funcp; AstNodeModule* modp = it->second.m_modp; if (funcp->dpiExport()) { puts("__Vscope_"+scopep->scopeSymName()+".exportInsert(__Vfinal,"); putsQuoted(funcp->cname()); puts(", (void*)(&"); puts(modClassName(modp)); puts("::"); puts(funcp->name()); puts("));\n"); } } // It would be less code if each module inserted its own variables. // Someday. For now public isn't common. for (ScopeVars::iterator it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) { AstNodeModule* modp = it->second.m_modp; AstScope* scopep = it->second.m_scopep; AstVar* varp = it->second.m_varp; // int pdim=0; int udim=0; string bounds; if (AstBasicDType* basicp = varp->basicp()) { // Range is always first, it's not in "C" order if (basicp->isRanged()) { bounds += " ,"; bounds += cvtToStr(basicp->msb()); bounds += ","; bounds += cvtToStr(basicp->lsb()); pdim++; } for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) { bounds += " ,"; bounds += cvtToStr(adtypep->msb()); bounds += ","; bounds += cvtToStr(adtypep->lsb()); if (dtypep->castPackArrayDType()) pdim++; else udim++; dtypep = adtypep->subDTypep(); } else break; // AstBasicDType - nothing below, 1 } } // if (pdim>1 || udim>1) { puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays } puts("__Vscope_"+it->second.m_scopeName+".varInsert(__Vfinal,"); putsQuoted(it->second.m_varBasePretty); puts(", &("); if (modp->isTop()) { puts(scopep->nameDotless()); puts("p->"); } else { puts(scopep->nameDotless()); puts("."); } puts(varp->name()); puts("), "); puts(varp->vlEnumType()); // VLVT_UINT32 etc puts(","); puts(varp->vlEnumDir()); // VLVD_IN etc if (varp->isSigUserRWPublic()) puts("|VLVF_PUB_RW"); else if (varp->isSigUserRdPublic()) puts("|VLVF_PUB_RD"); puts(","); puts(cvtToStr(pdim+udim)); puts(bounds); puts(");\n"); } puts("}\n"); } puts("}\n"); if (v3Global.opt.savable() ) { puts("\n"); for (int de=0; de<2; ++de) { string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; string funcname = de ? "__Vdeserialize" : "__Vserialize"; string op = de ? ">>" : "<<"; puts("void "+symClassName()+"::"+funcname+"("+classname+"& os) {\n"); puts( "// LOCAL STATE\n"); // __Vm_namep presumably already correct puts( "os"+op+"__Vm_activity;\n"); puts( "os"+op+"__Vm_didInit;\n"); puts( "// SUBCELL STATE\n"); for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { AstScope* scopep = it->first; AstNodeModule* modp = it->second; if (!modp->isTop()) { puts( scopep->nameDotless()+"."+funcname+"(os);\n"); } } puts("}\n"); } } }
void drivenWhole() { UINFO(9, "set d[*] "<<m_varp->name()<<endl); m_drivenWhole = true; }
void usedWhole() { UINFO(9, "set u[*] "<<m_varp->name()<<endl); m_usedWhole = true; }
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; }
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); }