// $Id$ //************************************************************************* // DESCRIPTION: Verilator: Emit C++ for tree // // Code available from: http://www.veripool.com/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2007 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // General Public License or the Perl Artistic License. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include #include #include "V3Global.h" #include "V3EmitC.h" #include "V3EmitCBase.h" #define VL_VALUE_STRING_MAX_WIDTH 1024 // We use a static char array in VL_VALUE_STRING //###################################################################### // Coverage ID tracking class EmitCoverIds { // TYPES typedef map CoverIdMap; // MEMBERS CoverIdMap m_coverIds; // Remapping of coverage IDs to per-module value int m_coverIdsSize; // Size of m_coverIds (to avoid O(n^2) insert) // METHODS public: void clear() { m_coverIds.clear(); m_coverIdsSize = 0; } int size() const { return m_coverIdsSize; } int remap(AstCoverDecl* declp) { // Make cover ID for this point, unique on this module CoverIdMap::iterator it = m_coverIds.find(declp); if (it == m_coverIds.end()) { m_coverIds.insert(make_pair(declp,m_coverIdsSize++)); // Remapping of coverage IDs to per-module value return m_coverIdsSize-1; } else { return it->second; } } EmitCoverIds() : m_coverIdsSize(0) {} ~EmitCoverIds() {} }; //###################################################################### // Emit statements and math operators class EmitCStmts : public EmitCBaseVisitor { private: bool m_suppressSemi; AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting vector m_ctorVarsVec; // All variables in constructor order public: EmitCoverIds m_coverIds; // Coverage ID remapping public: //int debug() { return 9; } // METHODS void displayEmit(AstDisplay* nodep); string displayFormat(AstNode* widthNode, string in, char fmtLetter, bool padZero, bool reallyString); void displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter); void emitVarDecl(AstVar* nodep, const string& prefixIfImp); typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich; void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp); void emitVarCtors(); bool emitSimpleOk(AstNodeMath* nodep); void emitIQW(AstNode* nodep) { puts (nodep->isWide()?"W":(nodep->isQuad()?"Q":"I")); } void emitOpName(AstNode* nodep, const string& name); string cFuncArgs(AstCFunc* nodep) { // Return argument list for given C function string args = nodep->argTypes(); // Might be a user function with argument list. for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO() && !portp->isFuncReturn()) { if (args != "") args+= ", "; if (portp->isWide()) { if (portp->isInOnly()) args += "const "; args += portp->cType(); args += " (& "+portp->name(); args += ")["+cvtToStr(portp->widthWords())+"]"; } else { args += portp->cType(); if (portp->isOutput()) args += "&"; args += " "+portp->name(); } } } } return args; } // VISITORS virtual void visit(AstNodeAssign* nodep, AstNUser*) { bool paren = true; bool decind = false; if (AstSel* selp=nodep->lhsp()->castSel()) { if (selp->widthMin()==1) { putbs("VL_ASSIGNBIT_"); emitIQW(selp->fromp()); if (nodep->rhsp()->isAllOnesV()) { putbs("O("); } else { putbs("I("); } puts(cvtToStr(nodep->widthMin())+","); selp->lsbp()->iterateAndNext(*this); puts(", "); selp->fromp()->iterateAndNext(*this); puts(", "); } else { putbs("VL_ASSIGNSEL_"); emitIQW (selp->fromp()); putbs("II"); emitIQW(nodep->rhsp()); puts("("); puts(cvtToStr(nodep->widthMin())+","); selp->lsbp()->iterateAndNext(*this); puts(", "); selp->fromp()->iterateAndNext(*this); puts(", "); } } else if (nodep->lhsp()->castVarRef() && nodep->lhsp()->castVarRef()->varp()->isSc()) { putbs("VL_ASSIGN_"); // Set a systemC variable if (nodep->lhsp()->castVarRef()->varp()->isScQuad()) puts("SQ"); else puts("S"); emitIQW(nodep); puts("("); puts(cvtToStr(nodep->widthMin())+","); nodep->lhsp()->iterateAndNext(*this); puts(", "); } else if (nodep->rhsp()->castVarRef() && nodep->rhsp()->castVarRef()->varp()->isSc()) { putbs("VL_ASSIGN_"); // Get a systemC variable emitIQW(nodep); if (nodep->rhsp()->castVarRef()->varp()->isScQuad()) puts("SQ("); else puts("S("); puts(cvtToStr(nodep->widthMin())+","); nodep->lhsp()->iterateAndNext(*this); puts(", "); } else if (nodep->isWide() && nodep->lhsp()->castVarRef() && !nodep->rhsp()->castVarRef() && !nodep->rhsp()->castArraySel()) { // Wide functions assign into the array directly, don't need separate assign statement m_wideTempRefp = nodep->lhsp()->castVarRef(); paren = false; } else if (nodep->isWide()) { putbs("VL_ASSIGN_W("); puts(cvtToStr(nodep->widthMin())+","); nodep->lhsp()->iterateAndNext(*this); puts(", "); } else { paren = false; nodep->lhsp()->iterateAndNext(*this); puts(" "); ofp()->blockInc(); decind = true; if (!nodep->rhsp()->castConst()) ofp()->putBreak(); puts("= "); } nodep->rhsp()->iterateAndNext(*this); if (paren) puts(")"); if (decind) ofp()->blockDec(); if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstCCall* nodep, AstNUser*) { puts(nodep->hiername()); puts(nodep->funcp()->name()); puts("("); puts(nodep->argTypes()); bool comma = (nodep->argTypes() != ""); for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { if (comma) puts(", "); subnodep->accept(*this); comma = true; } if (nodep->backp()->castNodeMath() || nodep->backp()->castCReturn()) { // We should have a separate CCall for math and statement usage, but... puts(")"); } else { puts(");\n"); } } virtual void visit(AstNodeCase* nodep, AstNUser*) { // In V3Case... nodep->v3fatalSrc("Case statements should have been reduced out\n"); } virtual void visit(AstComment* nodep, AstNUser*) { puts((string)"// "+nodep->name()+" at "+nodep->fileline()->ascii()+"\n"); nodep->iterateChildren(*this); } virtual void visit(AstCoverDecl* nodep, AstNUser*) { puts("__vlCoverInsert("); // As Declared in emitCoverageDecl puts("&__Vcoverage["); puts(cvtToStr(m_coverIds.remap(nodep))); puts("]"); puts(", \""); puts(nodep->fileline()->filebasename()); puts("\""); puts(", "); puts(cvtToStr(nodep->fileline()->lineno())); puts(", "); puts(cvtToStr(nodep->column())); puts(", \""); puts(nodep->hier()); puts("\""); puts(", \""); puts(nodep->typeText()); puts("\""); puts(", \""); puts(nodep->comment()); puts("\""); puts(");\n"); } virtual void visit(AstCoverInc* nodep, AstNUser*) { puts("++this->__Vcoverage["); puts(cvtToStr(m_coverIds.remap(nodep->declp()))); puts("];\n"); } virtual void visit(AstCReturn* nodep, AstNUser*) { puts("return ("); nodep->lhsp()->iterateAndNext(*this); puts(");\n"); } virtual void visit(AstDisplay* nodep, AstNUser*); // BELOW virtual void visit(AstFOpen* nodep, AstNUser*) { nodep->filep()->iterateAndNext(*this); puts(" = VL_FOPEN_"); emitIQW(nodep->filenamep()); emitIQW(nodep->modep()); if (nodep->modep()->width()>4*8) nodep->modep()->v3error("$fopen mode should be <= 4 characters"); puts("("); if (nodep->filenamep()->isWide()) { puts(cvtToStr(nodep->filenamep()->widthWords())); putbs(", "); } if (nodep->filenamep()->widthWords() > VL_TO_STRING_MAX_WORDS) { nodep->v3error("String of "<filenamep()->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); } nodep->filenamep()->iterateAndNext(*this); putbs(", "); nodep->modep()->iterateAndNext(*this); puts(");\n"); } virtual void visit(AstReadMem* nodep, AstNUser*) { puts("VL_READMEM_"); emitIQW(nodep->filenamep()); puts(" ("); // We take a void* rather then emitIQW(nodep->memp()); puts(nodep->isHex()?"true":"false"); putbs(","); puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width putbs(","); uint32_t array_lsb = 0; { AstVarRef* varrefp = nodep->memp()->castVarRef(); if (!varrefp || !varrefp->varp()->arrayp(0)) { nodep->v3error("Readmem loading non-arrayed variable"); } else { puts(cvtToStr(varrefp->varp()->arrayElements())); array_lsb = varrefp->varp()->arrayp(0)->lsbConst(); } } putbs(", "); puts(cvtToStr(array_lsb)); putbs(","); puts(cvtToStr(nodep->filenamep()->widthWords())); if (nodep->filenamep()->widthWords() > VL_TO_STRING_MAX_WORDS) { nodep->v3error("String of "<filenamep()->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); } putbs(", "); nodep->filenamep()->iterateAndNext(*this); putbs(", "); nodep->memp()->iterateAndNext(*this); putbs(","); if (nodep->lsbp()) { nodep->lsbp()->iterateAndNext(*this); } else puts(cvtToStr(array_lsb)); putbs(","); if (nodep->msbp()) { nodep->msbp()->iterateAndNext(*this); } else puts("~0"); puts(");\n"); } virtual void visit(AstFClose* nodep, AstNUser*) { puts("if ("); nodep->filep()->iterateAndNext(*this); puts(") { fclose (VL_CVT_Q_FP("); nodep->filep()->iterateAndNext(*this); puts(")); "); nodep->filep()->iterateAndNext(*this); // For saftey, so user doesn't later WRITE with it. puts("=0; }\n"); } virtual void visit(AstWhile* nodep, AstNUser*) { nodep->precondsp()->iterateAndNext(*this); puts("while ("); nodep->condp()->iterateAndNext(*this); puts(") {\n"); nodep->bodysp()->iterateAndNext(*this); nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop puts("}\n"); } virtual void visit(AstNodeIf* nodep, AstNUser*) { puts("if ("); if (nodep->branchPred() != AstBranchPred::UNKNOWN) { puts(nodep->branchPred().ascii()); puts("("); } nodep->condp()->iterateAndNext(*this); if (nodep->branchPred() != AstBranchPred::UNKNOWN) puts(")"); puts(") {\n"); nodep->ifsp()->iterateAndNext(*this); if (nodep->elsesp()) { puts("} else {\n"); nodep->elsesp()->iterateAndNext(*this); } puts("}\n"); } virtual void visit(AstStop* nodep, AstNUser*) { puts("vl_stop(\""); puts(nodep->fileline()->filename()); puts("\","); puts(cvtToStr(nodep->fileline()->lineno())); puts(",\"\");\n"); } virtual void visit(AstFinish* nodep, AstNUser*) { puts("vl_finish(\""); puts(nodep->fileline()->filename()); puts("\","); puts(cvtToStr(nodep->fileline()->lineno())); puts(",\"\");\n"); } virtual void visit(AstText* nodep, AstNUser*) { ofp()->putsNoTracking(nodep->text()); } virtual void visit(AstCStmt* nodep, AstNUser*) { nodep->bodysp()->iterateAndNext(*this); } virtual void visit(AstCMath* nodep, AstNUser*) { nodep->bodysp()->iterateAndNext(*this); } virtual void visit(AstUCStmt* nodep, AstNUser*) { puts("// $c statement at "+nodep->fileline()->ascii()+"\n"); nodep->bodysp()->iterateAndNext(*this); puts("\n"); } virtual void visit(AstUCFunc* nodep, AstNUser*) { puts("\n"); puts("// $c function at "+nodep->fileline()->ascii()+"\n"); nodep->bodysp()->iterateAndNext(*this); puts("\n"); } // Operators virtual void visit(AstNodeTermop* nodep, AstNUser*) { emitOpName(nodep,nodep->emitOperator()); puts(")"); } virtual void visit(AstNodeUniop* nodep, AstNUser*) { if (emitSimpleOk(nodep)) { putbs("("); puts(nodep->emitSimpleOperator()); puts(" "); } else { emitOpName(nodep,nodep->emitOperator()); } nodep->lhsp()->iterateAndNext(*this); puts(")"); } virtual void visit(AstNodeBiop* nodep, AstNUser*) { if (emitSimpleOk(nodep)) { putbs("("); nodep->lhsp()->iterateAndNext(*this); puts(" "); putbs(nodep->emitSimpleOperator()); puts(" "); } else { emitOpName(nodep,nodep->emitOperator()); nodep->lhsp()->iterateAndNext(*this); puts(", "); } nodep->rhsp()->iterateAndNext(*this); puts(")"); } virtual void visit(AstRedXor* nodep, AstNUser* vup) { if (nodep->lhsp()->isWide()) { visit(nodep->castNodeUniop(), vup); } else { putbs("VL_REDXOR_"); puts(cvtToStr(nodep->lhsp()->widthPow2())); puts("("); nodep->lhsp()->iterateAndNext(*this); puts(")"); } } virtual void visit(AstMulS* nodep, AstNUser* vup) { if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Signed multiply of "<width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h\n"); } visit(nodep->castNodeBiop(), vup); } virtual void visit(AstCast* nodep, AstNUser*) { // Extending a value of the same word width is just a NOP. if (nodep->size()>VL_WORDSIZE) { puts("(QData)("); } else { puts("(IData)("); } nodep->lhsp()->iterateAndNext(*this); puts(")"); } virtual void visit(AstNodeCond* nodep, AstNUser*) { // Widths match up already, so we'll just use C++'s operator w/o any temps. if (nodep->expr1p()->isWide()) { emitOpName(nodep,nodep->emitOperator()); nodep->condp()->iterateAndNext(*this); puts(", "); nodep->expr1p()->iterateAndNext(*this); puts(", "); nodep->expr2p()->iterateAndNext(*this); puts(")"); } else { putbs("("); nodep->condp()->iterateAndNext(*this); putbs(" ? "); nodep->expr1p()->iterateAndNext(*this); putbs(" : "); nodep->expr2p()->iterateAndNext(*this); puts(")"); } } virtual void visit(AstSel* nodep, AstNUser*) { // Note ASSIGN checks for this on a LHS if (nodep->widthp()->isOne()) { emitOpName(nodep,"VL_BITSEL"); nodep->fromp()->iterateAndNext(*this); puts(", "); nodep->lsbp()->iterateAndNext(*this); puts(")"); } else { emitOpName(nodep,"VL_SEL"); nodep->fromp()->iterateAndNext(*this); puts(", "); nodep->lsbp()->iterateAndNext(*this); puts(", "); nodep->widthp()->iterateAndNext(*this); puts(")"); } } virtual void visit(AstReplicate* nodep, AstNUser*) { if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) { if (((int)nodep->rhsp()->castConst()->asInt() * nodep->lhsp()->widthMin()) != nodep->widthMin()) nodep->v3fatalSrc("Replicate non-constant or width miscomputed"); puts("VL_REPLICATE_"); emitIQW(nodep); puts("OI("); puts(cvtToStr(nodep->widthMin())); if (nodep->lhsp()) { puts(","+cvtToStr(nodep->lhsp()->widthMin())); } if (nodep->rhsp()) { puts(","+cvtToStr(nodep->rhsp()->widthMin())); } puts(","); } else { emitOpName(nodep,nodep->emitOperator()); } nodep->lhsp()->iterateAndNext(*this); puts(", "); nodep->rhsp()->iterateAndNext(*this); puts(")"); } virtual void visit(AstArraySel* nodep, AstNUser*) { nodep->fromp()->iterateAndNext(*this); putbs("["); nodep->bitp()->iterateAndNext(*this); puts("]"); } virtual void visit(AstWordSel* nodep, AstNUser*) { nodep->fromp()->iterateAndNext(*this); puts("["); // Not putbs, as usually it's a small constant next nodep->bitp()->iterateAndNext(*this); puts("]"); } // Terminals virtual void visit(AstVarRef* nodep, AstNUser*) { puts(nodep->hiername()); puts(nodep->varp()->name()); } void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) { // Put out constant set to the specified variable, or given variable in a string if (nodep->num().isFourState()) { nodep->v3error("Unsupported: 4-state numbers in this context"); } else if (nodep->isWide()) { putbs("VL_CONST_W_"); puts(cvtToStr(VL_WORDS_I(nodep->num().minWidth()))); puts("X("); puts(cvtToStr(nodep->widthMin())); puts(","); if (!assigntop) { puts(assignString); } else if (assigntop->castVarRef()) { puts(assigntop->hiername()); puts(assigntop->varp()->name()); } else { assigntop->iterateAndNext(*this); } for (int word=VL_WORDS_I(nodep->num().minWidth())-1; word>0; word--) { ofp()->printf(",0x%08x", nodep->num().dataWord(word)); } ofp()->printf(",0x%08x)", nodep->num().dataWord(0)); } else if (nodep->isQuad()) { vluint64_t num = nodep->asQuad(); if (num<10) ofp()->printf("VL_ULL(%lld)", (long long)num); else ofp()->printf("VL_ULL(0x%llx)", (long long)num); } else { uint32_t num = nodep->asInt(); if (num<10) puts(cvtToStr(num)); else ofp()->printf("0x%x", num); //Unneeded-Causes %lx format warnings: // if (!nodep->num().isSigned() && (num & (1UL<<31))) puts("U"); } } void emitSetVarConstant(const string& assignString, AstConst* constp) { if (!constp->isWide()) { puts(assignString); puts(" = "); } emitConstant(constp, NULL, assignString); puts(";\n"); } virtual void visit(AstConst* nodep, AstNUser*) { if (nodep->isWide()) { if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp"); emitConstant(nodep, m_wideTempRefp, ""); m_wideTempRefp = NULL; // We used it, barf if set it a second time } else { emitConstant(nodep, NULL, ""); } } // Just iterate virtual void visit(AstTopScope* nodep, AstNUser*) { nodep->iterateChildren(*this); } virtual void visit(AstScope* nodep, AstNUser*) { nodep->iterateChildren(*this); } // NOPs virtual void visit(AstPragma*, AstNUser*) {} virtual void visit(AstCell*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstVar*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstNodeText*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstTraceDecl*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstTraceInc*, AstNUser*) {} // Handled outside the Visit class // Default virtual void visit(AstNode* nodep, AstNUser*) { puts((string)"\n???? // "+nodep->typeName()+"\n"); nodep->iterateChildren(*this); nodep->v3fatalSrc("Unknown node type reached emitter: "<typeName()); } public: EmitCStmts() { m_suppressSemi = false; m_wideTempRefp = NULL; } virtual ~EmitCStmts() {} }; //###################################################################### // Internal EmitC implementation class EmitCImp : EmitCStmts { // MEMBERS AstModule* m_modp; vector m_blkChangeDetVec; // All encountered changes in block bool m_slow; // Creating __Slow file bool m_fast; // Creating non __Slow file (or both) int m_splitSize; // # of cfunc nodes placed into output file int m_splitFilenum; // File number being created, 0 = primary //--------------------------------------- // METHODS void doubleOrDetect(AstChangeDet* changep, bool& gotOne) { static int addDoubleOr = 10; // Determined experimentally as best if (!changep->rhsp()) { if (!gotOne) gotOne = true; else puts(" | "); changep->lhsp()->iterateAndNext(*this); } else { AstVarRef* lhsp = changep->lhsp()->castVarRef(); AstVarRef* rhsp = changep->rhsp()->castVarRef(); if (!lhsp) changep->v3fatalSrc("Not ref?"); if (!rhsp) changep->v3fatalSrc("Not ref?"); for (int word=0; wordlhsp()->widthWords(); word++) { if (!gotOne) { gotOne = true; addDoubleOr = 10; // Determined experimentally as best puts("("); } else if (--addDoubleOr == 0) { puts("|| ("); addDoubleOr = 10; } else { puts(" | ("); } changep->lhsp()->iterateAndNext(*this); if (changep->isWide()) puts("["+cvtToStr(word)+"]"); puts(" ^ "); changep->rhsp()->iterateAndNext(*this); if (changep->isWide()) puts("["+cvtToStr(word)+"]"); puts(")"); } } } V3OutCFile* newOutCFile(AstModule* modp, bool slow, bool source, int filenum=0) { string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp); if (filenum) filenameNoExt += "__"+cvtToStr(filenum); filenameNoExt += (slow ? "__Slow":""); V3OutCFile* ofp = NULL; if (optSystemPerl()) { string filename = filenameNoExt+".sp"; newCFile(filename, slow, source); ofp = new V3OutSpFile (filename); } else if (optSystemC()) { string filename = filenameNoExt+(source?".cpp":".h"); newCFile(filename, slow, source); ofp = new V3OutScFile (filename); } else { string filename = filenameNoExt+(source?".cpp":".h"); newCFile(filename, slow, source); ofp = new V3OutCFile (filename); } ofp->putsHeader(); return ofp; } //--------------------------------------- // VISITORS virtual void visit(AstCFunc* nodep, AstNUser*) { if (nodep->funcType() == AstCFuncType::TRACE_INIT || nodep->funcType() == AstCFuncType::TRACE_FULL || nodep->funcType() == AstCFuncType::TRACE_CHANGE) { return; // Handled specially } if (!(nodep->slow() ? m_slow : m_fast)) return; m_blkChangeDetVec.clear(); m_splitSize += EmitCBaseCounterVisitor(nodep).count(); puts("\n"); puts(nodep->rtnTypeVoid()); puts(" "); puts(modClassName(m_modp)+"::"+nodep->name() +"("+cFuncArgs(nodep)+") {\n"); puts("VL_DEBUG_IF(cout<<\" "); for (int i=0;ilevel();i++) { puts(" "); } puts(modClassName(m_modp)+"::"+nodep->name() +"\"<symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); if (nodep->initsp()) puts("// Variables\n"); ofp()->putAlign(V3OutFile::AL_AUTO, 4); for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { if (AstVar* varp=subnodep->castVar()) { if (varp->isFuncReturn()) emitVarDecl(varp, ""); } } emitVarList(nodep->initsp(), EVL_ALL, ""); ofp()->putAlign(V3OutFile::AL_AUTO, 4); emitVarList(nodep->stmtsp(), EVL_ALL, ""); ofp()->putAlign(V3OutFile::AL_AUTO, 4); nodep->initsp()->iterateAndNext(*this); if (nodep->stmtsp()) puts("// Body\n"); nodep->stmtsp()->iterateAndNext(*this); #ifndef NEW_ORDERING if (!m_blkChangeDetVec.empty()) emitChangeDet(); #endif if (nodep->finalsp()) puts("// Final\n"); nodep->finalsp()->iterateAndNext(*this); // if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); // puts("__Vm_activity = true;\n"); puts("}\n"); } void emitChangeDet() { puts("// Change detection\n"); puts("IData __req = false; // Logically a bool\n"); // But not because it results in faster code bool gotOne = false; for (vector::iterator it = m_blkChangeDetVec.begin(); it != m_blkChangeDetVec.end(); ++it) { AstChangeDet* changep = *it; if (changep->lhsp()) { if (!gotOne) { // Not a clocked block puts("__req |= ("); } else puts("\n"); doubleOrDetect(changep, gotOne); } } if (gotOne) { puts(");\n"); //puts("VL_DEBUG_IF( if (__req) cout<<\"\tCLOCKREQ );"); for (vector::iterator it = m_blkChangeDetVec.begin(); it != m_blkChangeDetVec.end(); ++it) { AstChangeDet* nodep = *it; if (nodep->lhsp()) { puts("VL_DEBUG_IF( if(__req && ("); bool gotOneIgnore = false; doubleOrDetect(nodep, gotOneIgnore); string varname; if (nodep->lhsp()->castVarRef()) { varname = ": "+nodep->lhsp()->castVarRef()->varp()->prettyName(); } puts(")) cout<<\"\tCHANGE: "+nodep->fileline()->ascii() +varname+"\"<accept(*this); } int splitSize() { return m_splitSize; } }; //###################################################################### // Internal EmitCStmts void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) { if (nodep->isIO()) { if (nodep->isSc()) { m_ctorVarsVec.push_back(nodep); ofp()->putAlign(nodep->isStatic(), 4); // sc stuff is a structure, so bigger alignment if (nodep->attrScClocked() && nodep->isInput()) { puts("sc_in_clk\t"); } else { if (nodep->isInout()) puts("sc_inout<"); else if (nodep->isInput()) puts("sc_in<"); else if (nodep->isOutput()) puts("sc_out<"); else nodep->v3fatalSrc("Unknown type"); puts(nodep->scType()); puts(">\t"); } puts(nodep->name()); puts(";\n"); } else { // C++ signals ofp()->putAlign(nodep->isStatic(), nodep->widthAlignBytes()); if (nodep->isInout()) puts("VL_INOUT"); else if (nodep->isInput()) puts("VL_IN"); else if (nodep->isOutput()) puts("VL_OUT"); else nodep->v3fatalSrc("Unknown type"); if (nodep->isQuad()) puts("64"); else if (nodep->widthMin() <= 8) puts("8"); else if (nodep->widthMin() <= 16) puts("16"); if (!nodep->isWide()) puts("("+nodep->name() +","+cvtToStr(nodep->msb()) +","+cvtToStr(nodep->lsb())); else puts("W("+nodep->name() +","+cvtToStr(nodep->msb()) +","+cvtToStr(nodep->lsb()) +","+cvtToStr(nodep->widthWords())); puts(");\n"); } } else { // Arrays need a small alignment, but may need different padding after. // For example three VL_SIG8's needs alignment 1 but size 3. ofp()->putAlign(nodep->isStatic(), nodep->widthAlignBytes(), nodep->arrayElements()*nodep->widthAlignBytes()); if (nodep->isStatic() && prefixIfImp=="") puts("static "); if (nodep->isStatic()) puts("VL_ST_"); else puts("VL_"); if (nodep->widthMin() <= 8) { puts("SIG8("); } else if (nodep->widthMin() <= 16) { puts("SIG16("); } else if (nodep->isQuad()) { puts("SIG64("); } else if (!nodep->isWide()) { puts("SIG("); } else { puts("SIGW("); } if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); } puts(nodep->name()); for (AstRange* arrayp=nodep->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) { puts("["+cvtToStr(arrayp->elementsConst())+"]"); } puts(","+cvtToStr(nodep->msb())+","+cvtToStr(nodep->lsb())); if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); puts(");\n"); } } void EmitCStmts::emitVarCtors() { ofp()->indentInc(); bool first = true; for (vector::iterator it = m_ctorVarsVec.begin(); it != m_ctorVarsVec.end(); ++it) { if (first) { first=false; puts("\n"); puts("#if (SYSTEMC_VERSION>20011000)\n"); // SystemC 2.0.1 and newer puts(" : "); } else puts(", "); if (ofp()->exceededWidth()) puts("\n "); puts((*it)->name()); puts("(\""); puts((*it)->name()); puts("\")"); } if (!first) puts ("\n#endif\n"); ofp()->indentDec(); } bool EmitCStmts::emitSimpleOk(AstNodeMath* nodep) { // Can we put out a simple (A + B) instead of VL_ADD_III(A,B)? if (nodep->emitSimpleOperator() == "") return false; if (nodep->isWide()) return false; if (nodep->op1p()) { if (nodep->op1p()->isWide()) return false; } if (nodep->op2p()) { if (nodep->op2p()->isWide()) return false; } if (nodep->op3p()) { if (nodep->op3p()->isWide()) return false; } return true; } void EmitCStmts::emitOpName(AstNode* nodep, const string& opname) { putbs(opname+"_"); if (nodep->emitWordForm()) { emitIQW(nodep->op1p()); puts("("); if (nodep->op1p()->isWide()) { puts(cvtToStr(nodep->op1p()->widthWords())); puts(", "); } } else { emitIQW(nodep); if (nodep->op1p()) { emitIQW(nodep->op1p()); } if (nodep->op2p()) { emitIQW(nodep->op2p()); } if (nodep->op3p()) { emitIQW(nodep->op3p()); } puts("("); puts(cvtToStr(nodep->widthMin())); if (nodep->op1p()) { puts(","+cvtToStr(nodep->op1p()->widthMin())); } if (nodep->op2p()) { puts(","+cvtToStr(nodep->op2p()->widthMin())); } if (nodep->op3p()) { puts(","+cvtToStr(nodep->op3p()->widthMin())); } if (nodep->op1p() || nodep->isWide()) puts(", "); } if (nodep->isWide()) { if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?"); puts(m_wideTempRefp->hiername()); puts(m_wideTempRefp->varp()->name()); m_wideTempRefp = NULL; if (nodep->op1p()) puts(", "); } } //---------------------------------------------------------------------- // Mid level - VISITS // We only do one display at once, so can just use static state struct EmitDispState { bool m_wide; // Put out a wide func that needs string buffer string m_format; // "%s" and text from user vector m_argsp; // Each argument to be printed vector m_argsFunc; // Function before each argument to be printed EmitDispState() { clear(); } void clear() { m_wide = false; m_format = ""; m_argsp.clear(); m_argsFunc.clear(); } void pushFormat(const string& fmt) { m_format += fmt; } void pushFormat(char fmt) { m_format += fmt; } void pushArg(AstNode* nodep, const string& func) { m_argsp.push_back(nodep); m_argsFunc.push_back(func); } } emitDispState; void EmitCStmts::displayEmit(AstDisplay* nodep) { if (emitDispState.m_format != "") { // Format if (nodep->filep()) { puts("if ("); nodep->filep()->iterate(*this); // Check if closed, to avoid core dump puts(") fprintf(VL_CVT_Q_FP("); nodep->filep()->iterate(*this); puts("),\""); } else { puts("VL_PRINTF(\""); } ofp()->putsNoTracking(emitDispState.m_format); puts("\""); // Arguments for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) { puts(","); AstNode* argp = emitDispState.m_argsp[i]; string func = emitDispState.m_argsFunc[i]; ofp()->indentInc(); ofp()->putbs(""); if (func!="") puts(func); if (argp) argp->iterate(*this); if (func!="") puts(")"); ofp()->indentDec(); } // End puts(");\n"); // Prep for next emitDispState.clear(); } } string EmitCStmts::displayFormat(AstNode* widthNodep, string in, char fmtLetter, bool padZero, bool reallyString) { if (fmtLetter=='s') padZero = false; if (widthNodep && widthNodep->isWide() && (fmtLetter=='d'||fmtLetter=='u')) { widthNodep->v3error("Unsupported: $display of dec format of > 64 bit results (use hex format instead)"); } if (widthNodep && widthNodep->widthMin()>8 && fmtLetter=='c') { widthNodep->v3error("$display of char format of > 8 bit result"); } string fmt; if (in == "") { // Size naturally if (widthNodep == NULL) fmt=""; // Out of args, will get error if (fmtLetter=='u' || fmtLetter=='d') { // Decimal. Spec says leading spaces, not zeros double mantissabits = widthNodep->widthMin() - ((fmtLetter=='d')?1:0); double maxval = pow(2.0, mantissabits); double dchars = log10(maxval)+1.0; if (fmtLetter=='d') dchars++; // space for sign int nchars = int(dchars); fmt=cvtToStr(nchars); } else if (fmtLetter!='s' && fmtLetter!='c') { // Strings/chars don't get padding int bitsPerChar = (fmtLetter=='b'?1 : fmtLetter=='o'?3 : 4); int nchars = (widthNodep->widthMin() + bitsPerChar-1)/bitsPerChar; if (padZero) fmt=(string)("0")+cvtToStr(nchars); else fmt=cvtToStr(nchars); } } else if (in == "0") { fmt=""; // No width } else { fmt=in; } return fmt; } void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter) { // Print display argument, edits elistp if (!*elistp) { dispp->v3error("Missing arguments for $display format"); return; } if ((*elistp)->widthMin() > VL_VALUE_STRING_MAX_WIDTH) { dispp->v3error("Exceeded limit of 1024 bits for any display arguments"); } if ((*elistp)->isWide() // Have to use our own function for wide, || fmtLetter=='s' // ... Verilog strings || fmtLetter=='b' // ... binary, no printf %b in C || fmtLetter=='d') { // ... Signed decimal // We use a single static string, so must only have one call per VL_PRINT if (emitDispState.m_wide) { displayEmit(dispp); } emitDispState.m_wide = true; string nfmt = displayFormat(*elistp, fmt, fmtLetter, false, true); string pfmt = "%"+nfmt+"s"; string func = "VL_VALUE_FORMATTED_"; func += ((*elistp)->isWide()) ? "W(" : ((*elistp)->isQuad()) ? "Q(" : "I("; func += (cvtToStr((*elistp)->widthMin()) +",'"+fmtLetter+"'" +","+(fmt=="0"?"true":"false")+","); emitDispState.pushFormat(pfmt); emitDispState.pushArg(*elistp,func); } else { string func; string nfmt = displayFormat(*elistp, fmt, fmtLetter, true, false); // We don't need to check for fmtLetter=='d', as it is above. if ((*elistp)->isQuad() && (fmtLetter=='u'||fmtLetter=='o'||fmtLetter=='x')) { nfmt+="ll"; func="(unsigned long long)("; // Must match %ull to avoid warnings } string pfmt = "%"+nfmt+fmtLetter; emitDispState.pushFormat(pfmt); emitDispState.pushArg(*elistp,func); } // Next parameter *elistp = (*elistp)->nextp(); } void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) { string vformat = nodep->text(); bool addNewline = (nodep->newline() == '\n'); AstNode* elistp = nodep->exprsp(); // Convert Verilog display to C printf formats // "%0t" becomes "%d" emitDispState.clear(); string fmt = ""; string::iterator pos = vformat.begin(); if (*pos == '"') pos++; bool inPct = false; for (; pos != vformat.end(); ++pos) { if (pos[0]=='"' && (pos+1)==vformat.end()) break; if (inPct && pos[0]=='%') { emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % inPct = false; } else if (pos[0]=='%') { inPct = true; fmt = ""; } else if (!inPct) { // Normal text //char text[2]; text[0]=*pos; text[1]='\0'; emitDispState.pushFormat(*pos); } else { // Format character if (isdigit(pos[0])) { // Digits, like %5d, etc. fmt += pos[0]; } else { inPct = false; switch (tolower(pos[0])) { // Special codes case '~': displayArg(nodep,&elistp,fmt,'d'); break; // Signed decimal // Spec: h d o b c l case 'b': displayArg(nodep,&elistp,fmt,'b'); break; case 'c': displayArg(nodep,&elistp,fmt,'c'); break; case 'd': displayArg(nodep,&elistp,fmt,'u'); break; // Unsigned decimal case 'o': displayArg(nodep,&elistp,fmt,'o'); break; case 'h': case 'x': displayArg(nodep,&elistp,fmt,'x'); break; case 's': displayArg(nodep,&elistp,fmt,'s'); break; case 't': displayArg(nodep,&elistp,fmt,'u'); break; case 'm': { emitDispState.pushFormat("%s"); emitDispState.pushArg(NULL, "vlSymsp->name("); for (AstText* textp=nodep->scopeTextp(); textp; textp=textp->nextp()->castText()) { emitDispState.pushFormat(textp->text()); } break; } case 'u': case 'z': case 'l': case 'v': nodep->v3error("Unsupported: $display format code: %"<v3error("Unknown $display format code: %"<v3error("Extra arguments for $display format\n"); } if (addNewline) emitDispState.pushFormat("\\n"); displayEmit(nodep); } //###################################################################### // Internal EmitC void EmitCImp::emitVarResets(AstModule* modp) { puts("// Reset internal values\n"); if (modp->isTop()) { if (v3Global.opt.inhibitSim()) puts("__Vm_inhibitSim = false;\n"); puts("\n"); } puts("// Reset structure values\n"); for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstVar* varp = nodep->castVar()) { if (varp->isIO() && modp->isTop() && optSystemC()) { // System C top I/O doesn't need loading, as the lower level subinst code does it. } else if (varp->isParam()) { if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?"); //puts("// parameter "+varp->name()+" = "+varp->initp()->name()+"\n"); } else if (AstInitArray* initarp = varp->initp()->castInitArray()) { AstConst* constsp = initarp->initsp()->castConst(); if (!varp->arraysp()) varp->v3fatalSrc("InitArray under non-arrayed var"); for (int i=0; iarraysp()->elementsConst(); i++) { if (!constsp) initarp->v3fatalSrc("Not enough values in array initalizement"); emitSetVarConstant(varp->name()+"["+cvtToStr(i)+"]", constsp); constsp = constsp->nextp()->castConst(); } } else { int vects = 0; for (AstRange* arrayp=varp->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) { int vecnum = vects++; if (arrayp->msbConst() < arrayp->lsbConst()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); string ivar = string("__Vi")+cvtToStr(vecnum); // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); puts("; ++"+ivar+") {\n"); } bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen || (varp->name().c_str()[0]=='_' && v3Global.opt.underlineZero())); if (varp->isWide()) { // DOCUMENT: We randomize everything. If the user wants a _var to be zero, // there should be a initial statement. (Different from verilator2.) if (zeroit) puts("VL_ZERO_RESET_W("); else puts("VL_RAND_RESET_W("); puts(cvtToStr(varp->widthMin())); puts(","); puts(varp->name()); for (int v=0; vname()); for (int v=0; vwidthMin())); puts(");\n"); } } for (int v=0; vstmtsp(); nodep; nodep = nodep->nextp()) { if (AstCoverDecl* declp = nodep->castCoverDecl()) { m_coverIds.remap(declp); } } if (m_coverIds.size()) { ofp()->putsPrivate(true); puts("// Coverage\n"); puts("SpZeroed\t__Vcoverage["); puts(cvtToStr(m_coverIds.size())); puts("];\n"); ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(uint32_t)*m_coverIds.size()); puts("void __vlCoverInsert(SpZeroed* countp, const char* filename, int lineno, int column,\n"); puts( "const char* hier, const char* type, const char* comment);\n"); } } void EmitCImp::emitCtorImp(AstModule* modp) { puts("\n"); if (optSystemPerl() && modp->isTop()) { puts("SP_CTOR_IMP("+modClassName(modp)+")"); } else if (optSystemC() && modp->isTop()) { puts("VL_SC_CTOR_IMP("+modClassName(modp)+")"); } else { puts("VL_CTOR_IMP("+modClassName(modp)+")"); } emitVarCtors(); puts(" {\n"); emitCellCtors(modp); emitSensitives(); emitVarResets(modp); emitTextSection(AstType::SCCTOR); if (optSystemPerl()) puts("SP_AUTO_CTOR;\n"); puts("}\n"); } void EmitCImp::emitConfigureImp(AstModule* modp) { puts("\nvoid "+modClassName(modp)+"::__Vconfigure("+symClassName()+"* symsp) {\n"); puts( "__VlSymsp = symsp;\n"); // First, as later stuff needs it. bool first=true; for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (nodep->castCoverDecl()) { if (first) { first = false; puts("// Coverage Declarations\n"); } nodep->accept(*this); } } puts("}\n"); } void EmitCImp::emitCoverageImp(AstModule* modp) { if (m_coverIds.size()) { puts("\n// Coverage\n"); // Rather then putting out SP_COVER_INSERT calls directly, we do it via this function // This gets around gcc slowness constructing all of the template arguments puts("void "+modClassName(m_modp)+"::__vlCoverInsert(SpZeroed* countp, const char* filename, int lineno, int column,\n"); puts( "const char* hier, const char* type, const char* comment) {\n"); puts( "SP_COVER_INSERT(countp,"); puts( " \"filename\",filename,"); puts( " \"lineno\",lineno,"); puts( " \"column\",column,\n"); //puts( "\"hier\",string(__VlSymsp->name())+hier,"); // Need to move hier into scopes and back out if do this puts( "\"hier\",string(name())+hier,"); puts( " \"type\",type,"); puts( " \"comment\",comment);\n"); puts("}\n"); } } void EmitCImp::emitDestructorImp(AstModule* modp) { puts("\n"); puts(modClassName(modp)+"::~"+modClassName(modp)+"() {\n"); emitTextSection(AstType::SCDTOR); if (modp->isTop()) puts("delete __VlSymsp; __VlSymsp=NULL;\n"); puts("}\n"); } void EmitCImp::emitStaticDecl(AstModule* modp) { // Need implementation here. Be careful of alignment code; needs to be uniquified // with module name to avoid multiple symbols. //emitVarList(modp->stmtsp(), EVL_ALL, modp->name()); } void EmitCImp::emitTextSection(AstType type) { int last_line = -999; for (AstNode* nodep = m_modp->stmtsp(); nodep != NULL; nodep = nodep->nextp()) { if (AstNodeText* textp = nodep->castNodeText()) { if (nodep->type() == type) { if (last_line != nodep->fileline()->lineno()) { if (last_line < 0) { puts("\n//*** Below code from `systemc in Verilog file\n"); } ofp()->putsNoTracking("//#line "+cvtToStr(nodep->fileline()->lineno()) +" \""+nodep->fileline()->filename()+"\"\n"); last_line = nodep->fileline()->lineno(); } ofp()->putsNoTracking(textp->text()); last_line++; } } } if (last_line > 0) { puts("//*** Above code from `systemc in Verilog file\n\n"); } } void EmitCImp::emitCellCtors(AstModule* modp) { if (modp->isTop()) { // Must be before other constructors, as __vlCoverInsert calls it puts(EmitCBaseVisitor::symClassVar()+" = __VlSymsp = new "+symClassName()+"(this, name());\n"); puts(EmitCBaseVisitor::symTopAssign()+"\n"); } for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCell* cellp=nodep->castCell()) { puts("VL_CELL ("+cellp->name()+", "+modClassName(cellp->modp())+");\n"); } } } void EmitCImp::emitSensitives() { // Create sensitivity list for when to evaluate the model. // If C++ code, the user must call this routine themself. if (m_modp->isTop() && optSystemC()) { puts("// Sensitivities on all clocks and combo inputs\n"); puts("SC_METHOD(eval);\n"); for (AstNode* nodep=m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstVar* varp = nodep->castVar()) { if (varp->isInput() && (varp->isScSensitive() || varp->isUsedClock())) { puts("sensitive("+varp->name()+");\n"); } } } puts("\n"); } } void EmitCImp::emitWrapEval(AstModule* modp) { puts("\nvoid "+modClassName(modp)+"::eval() {\n"); puts(EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp; // Setup global symbol table\n"); puts(EmitCBaseVisitor::symTopAssign()+"\n"); puts("// Initialize\n"); puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);\n"); if (v3Global.opt.inhibitSim()) { puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n"); } puts("// Evaluate till stable\n"); puts("VL_DEBUG_IF(cout<<\"\\n----TOP Evaluate "+modClassName(modp)+"::eval\"<__Vm_activity = true;\n"); puts( "_eval(vlSymsp);\n"); #ifndef NEW_ORDERING puts( "__Vchange = _change_request(vlSymsp);\n"); puts( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n"); puts("}\n"); #endif puts("}\n"); // puts("\nvoid "+modClassName(modp)+"::_eval_initial_loop("+EmitCBaseVisitor::symClassVar()+") {\n"); puts("vlSymsp->__Vm_didInit = true;\n"); puts("_eval_initial(vlSymsp);\n"); #ifndef NEW_ORDERING puts( "vlSymsp->__Vm_activity = true;\n"); puts( "int __VclockLoop = 0;\n"); puts( "IData __Vchange=1;\n"); puts( "while (VL_LIKELY(__Vchange)) {\n"); #endif puts( "_eval_settle(vlSymsp);\n"); puts( "_eval(vlSymsp);\n"); #ifndef NEW_ORDERING puts( "__Vchange = _change_request(vlSymsp);\n"); puts( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't DC converge\");\n"); puts( "}\n"); #endif puts("}\n"); } //---------------------------------------------------------------------- // Top interface/ implementation void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp) { // Put out a list of signal declarations // in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays // This aids cache packing and locality // Largest->smallest reduces the number of pad variables. // But for now, Smallest->largest makes it more likely a small offset will allow access to the signal. for (int isstatic=1; isstatic>=0; isstatic--) { if (prefixIfImp!="" && !isstatic) continue; for (int size=0; size<8; size++) { if (size==3) continue; for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) { if (AstVar* varp = nodep->castVar()) { bool doit = true; switch (which) { case EVL_ALL: doit = true; break; case EVL_IO: doit = varp->isIO(); break; case EVL_SIG: doit = (varp->isSignal() && !varp->isIO()); break; case EVL_TEMP: doit = varp->isTemp(); break; default: v3fatalSrc("Bad Case"); } if (varp->isStatic() ? !isstatic : isstatic) doit=false; if (doit) { int sigbytes = varp->widthAlignBytes(); if (varp->isUsedClock() && varp->widthMin()==1) sigbytes = 0; else if (varp->arraysp()) sigbytes=7; else if (varp->isScWide()) sigbytes=6; else if (sigbytes==8) sigbytes=5; else if (sigbytes==4) sigbytes=4; else if (sigbytes==2) sigbytes=2; else if (sigbytes==1) sigbytes=1; if (size==sigbytes) { emitVarDecl(varp, prefixIfImp); } } } } } ofp()->putAlign(isstatic, 4, 0, prefixIfImp.c_str()); } } struct CmpName { inline bool operator () (const AstNode* lhsp, const AstNode* rhsp) const { return lhsp->name() < rhsp->name(); } }; void EmitCImp::emitIntFuncDecls(AstModule* modp) { vector funcsp; for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCFunc* funcp = nodep->castCFunc()) { if (!funcp->skipDecl()) { funcsp.push_back(funcp); } } } sort(funcsp.begin(), funcsp.end(), CmpName()); for (vector::iterator it = funcsp.begin(); it != funcsp.end(); ++it) { AstCFunc* funcp = *it; ofp()->putsPrivate(funcp->declPrivate()); if (funcp->isStatic()) puts("static "); puts(funcp->rtnTypeVoid()); puts("\t"); puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n"); } } void EmitCImp::emitInt(AstModule* modp) { // Always have this first; gcc has short circuiting if #ifdef is first in a file if (!optSystemPerl()) { // else done for us automatically puts("#ifndef _"+modClassName(modp)+"_H_\n"); puts("#define _"+modClassName(modp)+"_H_\n"); puts("\n"); } ofp()->putsIntTopInclude(); puts("#include \"verilated.h\"\n"); if (v3Global.opt.coverage()) { puts("#include \"SpCoverage.h\"\n"); } // Declare foreign instances up front to make C++ happy puts("class "+symClassName()+";\n"); for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCell* cellp=nodep->castCell()) { puts("class "+modClassName(cellp->modp())+";\n"); } } puts("\n//----------\n\n"); emitTextSection(AstType::SCHDR); if (optSystemC() && modp->isTop()) { puts("SC_MODULE("+modClassName(modp)+") {\n"); } else { puts("VL_MODULE("+modClassName(modp)+") {\n"); } if (optSystemPerl()) puts("/*AUTOATTR(verilated)*/\n\n"); ofp()->resetPrivate(); ofp()->putsPrivate(false); // public: // Instantiated modules if (optSystemPerl()) { puts("/*AUTOSUBCELLS*/\n\n"); } else { puts("// CELLS\n"); for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCell* cellp=nodep->castCell()) { ofp()->putsCellDecl(modClassName(cellp->modp()), cellp->name()); } } } puts("\n// PORTS\n"); emitVarList(modp->stmtsp(), EVL_IO, ""); puts("\n// LOCAL SIGNALS\n"); emitVarList(modp->stmtsp(), EVL_SIG, ""); puts("\n// LOCAL VARIABLES\n"); emitVarList(modp->stmtsp(), EVL_TEMP, ""); puts("\n// INTERNAL VARIABLES\n"); ofp()->putsPrivate(!modp->isTop()); // private: unless top ofp()->putAlign(V3OutFile::AL_AUTO, 8); puts(symClassName()+"*\t__VlSymsp;\t\t// Symbol table\n"); ofp()->putsPrivate(false); // public: if (modp->isTop()) { if (v3Global.opt.inhibitSim()) { ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(bool)); puts("bool\t__Vm_inhibitSim;\t///< Set true to disable evaluation of module\n"); } } ofp()->putAlign(V3OutFile::AL_AUTO, 8); emitCoverageDecl(modp); // may flip public/private ofp()->putAlign(V3OutFile::AL_AUTO, 8); puts("\n// PARAMETERS\n"); ofp()->putsPrivate(false); // public: for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstVar* varp = nodep->castVar()) { if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { if (!varp->initp()) nodep->v3fatalSrc("No init for a param?"); // These should be static const values, however microsloth VC++ doesn't // support them. They also cause problems with GDB under GCC2.95. if (varp->isScWide()) { // Unsupported for output puts("// enum WData "+varp->name()+" //wide"); } else if (!varp->initp()->castConst()) { // Unsupported for output puts("// enum IData "+varp->name()+" //not simple value"); } else { puts("enum "); puts(varp->isQuad()?"_QData":"_IData"); puts(""+varp->name()+" { "+varp->name()+" = "); varp->initp()->iterateAndNext(*this); puts("};"); } puts("\n"); } } } puts("\n// METHODS\n"); ofp()->resetPrivate(); // We don't need a private copy constructor, as VerilatedModule has one for us. ofp()->putsPrivate(true); puts(modClassName(modp)+"& operator= (const "+modClassName(modp)+"&);\t///< Copying not allowed\n"); puts(modClassName(modp)+"(const "+modClassName(modp)+"&);\t///< Copying not allowed\n"); ofp()->putsPrivate(false); // public: if (optSystemC() && modp->isTop()) { puts("SC_CTOR("+modClassName(modp)+");\n"); puts("virtual ~"+modClassName(modp)+"();\n"); } else if (optSystemC()) { puts("VL_CTOR("+modClassName(modp)+");\n"); puts("~"+modClassName(modp)+"();\n"); } else { puts(modClassName(modp)+"(const char* name=\"TOP\");\n"); puts("~"+modClassName(modp)+"();\n"); if (v3Global.opt.trace()) { puts("void\ttrace (SpTraceVcdCFile* tfp, int levels, int options=0);\n"); } } puts("void\t__Vconfigure("+symClassName()+"* symsp);\n"); if (optSystemPerl()) puts("/*AUTOMETHODS*/\n"); emitTextSection(AstType::SCINT); puts("\n// Sensitivity blocks\n"); if (modp->isTop()) { puts("void\tfinal();\t///< Function to call when simulation completed\n"); if (optSystemC()) ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls. puts("void\teval();\t///< Main function to call from calling app when inputs change\n"); if (v3Global.opt.inhibitSim()) { puts("void\tinhibitSim(bool flag) { __Vm_inhibitSim=flag; }\t///< Set true to disable evaluation of module\n"); } ofp()->putsPrivate(true); // private: puts("static void _eval_initial_loop("+EmitCBaseVisitor::symClassVar()+");\n"); } emitIntFuncDecls(modp); if (!optSystemPerl() && v3Global.opt.trace()) { ofp()->putsPrivate(false); // public: puts("static void traceInit (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); puts("static void traceFull (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); puts("static void traceChg (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); } puts("} VL_ATTR_ALIGNED(64);\n"); puts("\n"); // finish up h-file if (!optSystemPerl()) { puts("#endif /*guard*/\n"); } } //---------------------------------------------------------------------- void EmitCImp::emitImp(AstModule* modp) { if (optSystemPerl()) { puts("//############################################################\n"); puts("#sp implementation\n"); } ofp()->printf("#include \"%-20s // For This\n", (modClassName(modp)+".h\"").c_str()); // Us puts("#include \""+ symClassName() +".h\"\n"); if (optSystemPerl() && (m_splitFilenum || !m_fast)) { puts("\n"); puts("SP_MODULE_CONTINUED("+modClassName(modp)+");\n"); } emitTextSection(AstType::SCIMPHDR); if (m_slow && m_splitFilenum==0) { puts("\n//--------------------\n"); puts("// STATIC VARIABLES\n\n"); emitVarList(modp->stmtsp(), EVL_ALL, modClassName(modp)); } if (m_fast && m_splitFilenum==0) { emitTextSection(AstType::SCIMP); emitStaticDecl(modp); } if (m_slow && m_splitFilenum==0) { puts("\n//--------------------\n"); emitCtorImp(modp); emitConfigureImp(modp); emitDestructorImp(modp); emitCoverageImp(modp); } if (m_fast && m_splitFilenum==0) { if (modp->isTop()) { emitStaticDecl(modp); puts("\n//--------------------\n"); puts("\n"); emitWrapEval(modp); } } if (m_fast && m_splitFilenum==0) { if (v3Global.opt.trace() && optSystemC() && m_modp->isTop()) { puts("\n"); puts("\n/*AUTOTRACE(__MODULE__,recurse,activity,exists)*/\n\n"); } } // Blocks puts("\n//--------------------\n"); puts("// Internal Methods\n"); } void EmitCImp::emitImpBottom(AstModule* modp) { } //###################################################################### void EmitCImp::main(AstModule* modp, bool slow, bool fast) { // Output a module m_modp = modp; m_slow = slow; m_fast = fast; string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp)+(m_fast ? "" : "__Slow"); if (debug()>=5) { for (int i=0;ilevel();i++) { cout<<" "; } UINFONL(0," Emitting "<stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCFunc* funcp = nodep->castCFunc()) { if (v3Global.opt.outputSplit() > 1 && splitSize() && v3Global.opt.outputSplit() < splitSize()) { // Close old file emitImpBottom (modp); delete m_ofp; m_ofp=NULL; // Open a new file m_splitSize = 0; m_ofp = newOutCFile (modp, !m_fast, true/*source*/, ++m_splitFilenum); emitImp (modp); } mainDoFunc(funcp); } } emitImpBottom (modp); delete m_ofp; m_ofp=NULL; } //###################################################################### // Tracing routines class EmitCTrace : EmitCStmts { AstCFunc* m_funcp; // Function we're in now bool m_slow; // Making slow file // METHODS void emitTraceHeader() { // Includes if (optSystemC()) { puts("#include \"SpTraceVcd.h\"\n"); } puts("#include \"SpTraceVcdC.h\"\n"); puts("#include \""+ symClassName() +".h\"\n"); puts("\n"); } void emitTraceSlow() { puts("\n//======================\n\n"); puts("void "+topClassName()+"::trace ("); if (optSystemC()) { puts("SpTraceFile* tfp, int, int) {\n"); } else { puts("SpTraceVcdCFile* tfp, int, int) {\n"); } puts( "tfp->spTrace()->addCallback (" "&"+topClassName()+"::traceInit" +", &"+topClassName()+"::traceFull" +", &"+topClassName()+"::traceChg, this);\n"); puts("}\n"); puts("void "+topClassName()+"::traceInit(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); puts("// Callback from vcd->open()\n"); puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); puts("if (!Verilated::calcUnusedSigs()) vl_fatal(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n"); puts("t->traceInitThis (vlSymsp, vcdp, code);\n"); puts("}\n"); puts("void "+topClassName()+"::traceFull(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); puts("// Callback from vcd->dump()\n"); puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); puts("t->traceFullThis (vlSymsp, vcdp, code);\n"); puts("}\n"); puts("\n//======================\n\n"); } void emitTraceFast() { puts("\n//======================\n\n"); puts("void "+topClassName()+"::traceChg(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); puts("// Callback from vcd->dump()\n"); puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); puts("if (vlSymsp->getClearActivity()) {\n"); puts("t->traceChgThis (vlSymsp, vcdp, code);\n"); puts("}\n"); puts("}\n"); puts("\n//======================\n\n"); } bool emitTraceIsScWide(AstTraceInc* nodep) { AstVarRef* varrefp = nodep->valuep()->castVarRef(); if (!varrefp) return false; AstVar* varp = varrefp->varp(); return varp->isSc() && varp->isScWide(); } void emitTraceInitOne(AstTraceDecl* nodep) { if (nodep->isWide()) { puts("vcdp->declArray"); } else if (nodep->isQuad()) { puts("vcdp->declQuad "); } else if (nodep->msb() || nodep->lsb()) { puts("vcdp->declBus "); } else { puts("vcdp->declBit "); } puts("(c+"+cvtToStr(nodep->code())); if (nodep->arrayWidth()) puts("+i*"+cvtToStr(nodep->widthWords())); puts(",\""+nodep->showname()+"\""); if (nodep->arrayWidth()) { puts(",(i+"+cvtToStr(nodep->arrayLsb())+")"); } else { puts(",-1"); } if (nodep->msb() || nodep->lsb()) { puts(","+cvtToStr(nodep->msb())+","+cvtToStr(nodep->lsb())); } puts(");"); } void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { nodep->precondsp()->iterateAndNext(*this); string full = (m_funcp->funcType() == AstCFuncType::TRACE_FULL) ? "full":"chg"; if (nodep->isWide() || emitTraceIsScWide(nodep)) { puts("vcdp->"+full+"Array"); } else if (nodep->isQuad()) { puts("vcdp->"+full+"Quad "); } else if (nodep->declp()->msb() || nodep->declp()->lsb()) { puts("vcdp->"+full+"Bus "); } else { puts("vcdp->"+full+"Bit "); } puts("(c+"+cvtToStr(nodep->declp()->code() + ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords())))); puts(","); emitTraceValue(nodep, arrayindex); if (nodep->declp()->msb() || nodep->declp()->lsb()) { puts(","+cvtToStr(nodep->declp()->widthMin())); } puts(");\n"); } void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (nodep->valuep()->castVarRef()) { AstVarRef* varrefp = nodep->valuep()->castVarRef(); AstVar* varp = varrefp->varp(); if (emitTraceIsScWide(nodep)) puts("(uint32_t*)"); puts("("); varrefp->iterate(*this); // Put var name out if (varp->arraysp()) { if (arrayindex==-2) puts("[i]"); else if (arrayindex==-1) puts("[0]"); else puts("["+cvtToStr(arrayindex)+"]"); } if (varp->isSc()) puts(".read()"); if (emitTraceIsScWide(nodep)) puts(".get_datap()"); puts(")"); } else { puts("("); nodep->valuep()->iterate(*this); puts(")"); } } // VISITORS virtual void visit(AstNetlist* nodep, AstNUser*) { // Top module only nodep->topModulep()->accept(*this); } virtual void visit(AstModule* nodep, AstNUser*) { nodep->iterateChildren(*this); } virtual void visit(AstCFunc* nodep, AstNUser*) { if (nodep->slow() != m_slow) return; if (nodep->funcType() == AstCFuncType::TRACE_INIT || nodep->funcType() == AstCFuncType::TRACE_FULL || nodep->funcType() == AstCFuncType::TRACE_CHANGE) { m_funcp = nodep; puts("\n"); puts(nodep->rtnTypeVoid()); puts(" "); puts(topClassName()+"::"+nodep->name() +"("+cFuncArgs(nodep)+") {\n"); if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); puts("int c=code;\n"); puts("if (0 && vcdp && c) {} // Prevent unused\n"); if (nodep->funcType() == AstCFuncType::TRACE_INIT) { puts("vcdp->module(vlSymsp->name()); // Setup signal names\n"); } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { } else nodep->v3fatalSrc("Bad Case"); if (nodep->initsp()) puts("// Variables\n"); emitVarList(nodep->initsp(), EVL_ALL, ""); nodep->initsp()->iterateAndNext(*this); ofp()->putAlign(V3OutFile::AL_AUTO, 4); puts("// Body\n"); puts("{\n"); // Do the statements Note not from this node, but the TRACE_INIT's. // That saved us from having 3 copies of all of the TRACEs nodep->stmtsp()->iterateAndNext(*this); puts("}\n"); if (nodep->finalsp()) puts("// Final\n"); nodep->finalsp()->iterateAndNext(*this); puts("}\n"); } m_funcp = NULL; } virtual void visit(AstTraceDecl* nodep, AstNUser*) { if (nodep->arrayWidth()) { puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayWidth())+"; i++) {\n"); emitTraceInitOne(nodep); puts("}}\n"); } else { emitTraceInitOne(nodep); puts("\n"); } } virtual void visit(AstTraceInc* nodep, AstNUser*) { if (nodep->declp()->arrayWidth()) { // It traces faster if we unroll the loop for (unsigned i=0; ideclp()->arrayWidth(); i++) { emitTraceChangeOne(nodep, i); } } else { emitTraceChangeOne(nodep, -1); } } virtual void visit(AstCoverDecl* nodep, AstNUser*) { } virtual void visit(AstCoverInc* nodep, AstNUser*) { } public: EmitCTrace(bool slow) { m_funcp = NULL; m_slow = slow; } virtual ~EmitCTrace() {} void main() { // Put out the file string filename = (v3Global.opt.makeDir()+"/"+ topClassName() + (m_slow?"__Trace__Slow.cpp":"__Trace.cpp")); AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/); cfilep->support(true); V3OutCFile of (filename); of.putsHeader(); m_ofp = &of; emitTraceHeader(); if (m_slow) emitTraceSlow(); else emitTraceFast(); v3Global.rootp()->accept(*this); m_ofp = NULL; } }; //###################################################################### // EmitC class functions void V3EmitC::emitc() { UINFO(2,__FUNCTION__<<": "<modulesp(); nodep; nodep=nodep->nextp()->castModule()) { if (v3Global.opt.outputSplit()) { { EmitCImp imp; imp.main(nodep, false, true); } { EmitCImp imp; imp.main(nodep, true, false); } } else { { EmitCImp imp; imp.main(nodep, true, true); } } } } void V3EmitC::emitcTrace() { UINFO(2,__FUNCTION__<<": "<