Logo Search packages:      
Sourcecode: verilator version File versions  Download package

V3EmitC.cpp

//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2010 by Wilson Snyder.  This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// 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 <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <cmath>
#include <map>
#include <vector>
#include <algorithm>

#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

//######################################################################
// Emit statements and math operators

class EmitCStmts : public EmitCBaseVisitor {
private:
    bool    m_suppressSemi;
    AstVarRef*    m_wideTempRefp;         // Variable that _WW macros should be setting
    vector<AstVar*>           m_ctorVarsVec;          // All variables in constructor order
    int           m_splitSize;      // # of cfunc nodes placed into output file
    int           m_splitFilenum;   // File number being created, 0 = primary

public:
    // METHODS
    static int debug() {
      static int level = -1;
      if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
      return level;
    }

    // ACCESSORS
    int     splitFilenum() { return m_splitFilenum; }
    int     splitFilenumInc() { m_splitSize = 0; return ++m_splitFilenum; }
    int splitSize() { return m_splitSize; }
    void splitSizeInc(AstNode* nodep) { m_splitSize += EmitCBaseCounterVisitor(nodep).count(); }
    bool splitNeeded() { return (splitSize() && v3Global.opt.outputSplit() > 1
                         && v3Global.opt.outputSplit() < splitSize()); }

    // METHODS
    void displayNode(AstNode* nodep, AstScopeName* scopenamep,
                 const string& vformat, AstNode* exprsp, bool isScan);
    void displayEmit(AstNode* nodep, bool isScan);
    void displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
                string vfmt, 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) {
      // Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g
      puts (nodep->isWide()?"W":(nodep->isQuad()?"Q":"I"));
    }
    void emitScIQW(AstVar* nodep) {
      puts (nodep->isScBv()?"SW":(nodep->isScQuad()?"SQ":"SI"));
    }
    void emitOpName(AstNode* nodep, const string& format,
                AstNode* lhsp, AstNode* rhsp, AstNode* thsp);

    // 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()) {
                puts("O(");
            } else {
                puts("I(");
            }
            puts(cvtToStr(nodep->widthMin())+",");
            selp->lsbp()->iterateAndNext(*this); puts(", ");
            selp->fromp()->iterateAndNext(*this); puts(", ");
          } else {
            putbs("VL_ASSIGNSEL_");
            emitIQW (selp->fromp());
            puts("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
          emitScIQW(nodep->lhsp()->castVarRef()->varp());
          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);
          emitScIQW(nodep->rhsp()->castVarRef()->varp());
          puts("(");
          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(AstAlwaysPublic*, AstNUser*) {
    }
    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("&(vlSymsp->__Vcoverage[");
      puts(cvtToStr(nodep->dataDeclThisp()->binNum())); puts("])");
      // If this isn't the first instantiation of this module under this
      // design, don't really count the bucket, and rely on SystemPerl to
      // aggregate counts.  This is because Verilator combines all
      // hiearchies itself, and if SystemPerl also did it, you'd end up
      // with (number-of-instant) times too many counts in this bin.
      puts(", first");  // Enable, passed from __Vconfigure parameter
      puts(", "); putsQuoted(nodep->fileline()->filename());
      puts(", "); puts(cvtToStr(nodep->fileline()->lineno()));
      puts(", "); puts(cvtToStr(nodep->column()));
      puts(", "); putsQuoted((nodep->hier()!=""?".":"")+nodep->hier());
      puts(", "); putsQuoted(nodep->page());
      puts(", "); putsQuoted(nodep->comment());
      puts(");\n");
    }
    virtual void visit(AstCoverInc* nodep, AstNUser*) {
      puts("++(vlSymsp->__Vcoverage[");
      puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
      puts("]);\n");
    }
    virtual void visit(AstCReturn* nodep, AstNUser*) {
      puts("return (");
      nodep->lhsp()->iterateAndNext(*this);
      puts(");\n");
    }
    virtual void visit(AstDisplay* nodep, AstNUser*) {
      string text = nodep->fmtp()->text();
      if (nodep->addNewline()) text += "\n";
      displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false);
    }
    virtual void visit(AstScopeName* nodep, AstNUser*) {
      // For use under AstCCalls for dpiImports.  ScopeNames under displays are handled in AstDisplay
      if (!nodep->dpiExport()) {
          putbs("(&(vlSymsp->__Vscope_"+nodep->scopeSymName()+"))");
      }
    }
    virtual void visit(AstSFormat* nodep, AstNUser*) {
      displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp(), false);
    }
    virtual void visit(AstSFormatF* nodep, AstNUser*) {
      displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false);
    }
    virtual void visit(AstFScanF* nodep, AstNUser*) {
      displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true);
    }
    virtual void visit(AstSScanF* nodep, AstNUser*) {
      displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true);
    }
    virtual void visit(AstValuePlusArgs* nodep, AstNUser*) {
      string prefix;
      char format = '?';
      bool pct=false;
      int got=0;
      for (const char* cp = nodep->text().c_str(); *cp; cp++) {
          if (pct) {
            pct = false;
            switch (tolower(*cp)) {
            case '%':
                prefix += *cp;
                break;
            case 'd': // FALLTHRU
            case 'o': // FALLTHRU
            case 'h': // FALLTHRU
            case 'x': // FALLTHRU
            case 'b': // FALLTHRU
            case 's':
                got++; format = tolower(*cp);
                break;
            case 'e': // FALLTHRU
            case 'f': // FALLTHRU
            case 'g':
                got++; format = tolower(*cp);
                nodep->v3error("Unsupported $value$plusargs format qualifier: '"<<*cp<<"'"<<endl);
                break;
            default:
                got++;
                nodep->v3error("Illegal $value$plusargs format qualifier: '"<<*cp<<"'"<<endl);
                break;
            }
          }
          else if (*cp == '%') pct = true;
          else prefix += *cp;
      }
      if (got!=1) nodep->v3error("Missing or extra $value$plusargs format qualifier: '"<<nodep->text()<<"'"<<endl);
      puts("VL_VALUEPLUSARGS_I");
      emitIQW(nodep->exprsp());
      puts("(");
      puts(cvtToStr(nodep->exprsp()->widthMin()));  // Note argument width, not node width (which is always 32)
      putbs(",");
      putsQuoted(prefix);
      putbs(",");
      puts("'"); puts(cvtToStr(format)); puts("'");
      puts(",");
      nodep->exprsp()->iterateAndNext(*this);
      puts(")");
    }
    virtual void visit(AstTestPlusArgs* nodep, AstNUser*) {
      puts("VL_TESTPLUSARGS_I(");
      putsQuoted(nodep->text());
      puts(")");
    }
    virtual void visit(AstFGetS* nodep, AstNUser*) {
      checkMaxWords(nodep);
      emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
    }

    void checkMaxWords(AstNode* nodep) {
      if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) {
          nodep->v3error("String of "<<nodep->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h");
      }
    }
    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(", ");
      }
      checkMaxWords(nodep->filenamep());
      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 than 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) { nodep->v3error("Readmem loading non-variable"); }
          else if (AstArrayDType* adtypep = varrefp->varp()->dtypeSkipRefp()->castArrayDType()) {
            puts(cvtToStr(varrefp->varp()->arrayElements()));
            array_lsb = adtypep->lsb();
          }
          else {
            nodep->v3error("Readmem loading non-arrayed variable");
          }
      }
      putbs(", ");
      puts(cvtToStr(array_lsb));
      putbs(",");
      puts(cvtToStr(nodep->filenamep()->widthWords()));
      checkMaxWords(nodep->filenamep());
      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(AstFFlush* nodep, AstNUser*) {
      if (!nodep->filep()) {
          puts("fflush (stdout);\n");
      } else {
          puts("if (");
          nodep->filep()->iterateAndNext(*this);
          puts(") { fflush (VL_CVT_Q_FP(");
          nodep->filep()->iterateAndNext(*this);
          puts(")); }\n");
      }
    }
    virtual void visit(AstJumpGo* nodep, AstNUser*) {
      puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n");
    }
    virtual void visit(AstJumpLabel* nodep, AstNUser*) {
      puts("{\n");
      nodep->stmtsp()->iterateAndNext(*this);
      puts("}\n");
      puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n");
    }
    virtual void visit(AstWhile* nodep, AstNUser*) {
      nodep->precondsp()->iterateAndNext(*this);
      puts("while (");
      nodep->condp()->iterateAndNext(*this);
      puts(") {\n");
      nodep->bodysp()->iterateAndNext(*this);
      nodep->incsp()->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::BP_UNKNOWN) {
          puts(nodep->branchPred().ascii()); puts("(");
      }
      nodep->condp()->iterateAndNext(*this);
      if (nodep->branchPred() != AstBranchPred::BP_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(");
      putsQuoted(nodep->fileline()->filename());
      puts(",");
      puts(cvtToStr(nodep->fileline()->lineno()));
      puts(",\"\");\n");
    }
    virtual void visit(AstFinish* nodep, AstNUser*) {
      puts("vl_finish(");
      putsQuoted(nodep->fileline()->filename());
      puts(",");
      puts(cvtToStr(nodep->fileline()->lineno()));
      puts(",\"\");\n");
    }
    virtual void visit(AstText* nodep, AstNUser*) {
      if (nodep->tracking()) {
          puts(nodep->text());
      } else {
          ofp()->putsNoTracking(nodep->text());
      }
    }
    virtual void visit(AstCStmt* nodep, AstNUser*) {
      putbs("");
      nodep->bodysp()->iterateAndNext(*this);
    }
    virtual void visit(AstCMath* nodep, AstNUser*) {
      putbs("");
      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->emitC(), NULL, NULL, NULL);
    }
    virtual void visit(AstNodeUniop* nodep, AstNUser*) {
      if (emitSimpleOk(nodep)) {
          putbs("("); puts(nodep->emitSimpleOperator()); puts(" ");
          nodep->lhsp()->iterateAndNext(*this); puts(")");
      } else {
          emitOpName(nodep, nodep->emitC(), nodep->lhsp(), NULL, NULL);
      }
    }
    virtual void visit(AstNodeBiop* nodep, AstNUser*) {
      if (emitSimpleOk(nodep)) {
          putbs("("); nodep->lhsp()->iterateAndNext(*this);
          puts(" "); putbs(nodep->emitSimpleOperator()); puts(" ");
          nodep->rhsp()->iterateAndNext(*this); puts(")");
      } else {
          emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
      }
    }
    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("Unsupported: Signed multiply of "<<nodep->width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
      }
      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->emitC(), nodep->condp(), nodep->expr1p(), nodep->expr2p());
      } 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
      emitOpName(nodep, nodep->emitC(), nodep->fromp(), nodep->lsbp(), nodep->thsp());
    }
    virtual void visit(AstReplicate* nodep, AstNUser*) {
      if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) {
          if (((int)nodep->rhsp()->castConst()->toUInt()
                 * 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(",");
          nodep->lhsp()->iterateAndNext(*this); puts(", ");
          nodep->rhsp()->iterateAndNext(*this); puts(")");
      } else {
          emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
      }
    }
    // 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--) {
            // Only 32 bits - llx + long long here just to appease CPP format warning
            ofp()->printf(",0x%08" VL_PRI64 "x", (vluint64_t)(nodep->num().dataWord(word)));
          }
          ofp()->printf(",0x%08" VL_PRI64 "x)", (vluint64_t)(nodep->num().dataWord(0)));
      } else if (nodep->isQuad()) {
          vluint64_t num = nodep->toUQuad();
          if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "d)", num);
          else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", num);
      } else {
          uint32_t num = nodep->toUInt();
          // Only 32 bits - llx + long long here just to appease CPP format warning
          if (num<10) puts(cvtToStr(num));
          else ofp()->printf("0x%" VL_PRI64 "x", (vluint64_t)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, "");
      }
    }
    virtual void visit(AstConstString* nodep, AstNUser*) {
      putsQuoted(nodep->name());
    }

    // Just iterate
    virtual void visit(AstNetlist* nodep, AstNUser*) {
      nodep->iterateChildren(*this);
    }
    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
    virtual void visit(AstCFile*, AstNUser*) {}       // Handled outside the Visit class
    // Default
    virtual void visit(AstNode* nodep, AstNUser*) {
      puts((string)"\n???? // "+nodep->prettyTypeName()+"\n");
      nodep->iterateChildren(*this);
      nodep->v3fatalSrc("Unknown node type reached emitter: "<<nodep->prettyTypeName());
    }

public:
    EmitCStmts() {
      m_suppressSemi = false;
      m_wideTempRefp = NULL;
      m_splitSize = 0;
      m_splitFilenum = 0;
    }
    virtual ~EmitCStmts() {}
};

//######################################################################
// Internal EmitC implementation

class EmitCImp : EmitCStmts {
    // MEMBERS
    AstNodeModule*      m_modp;
    vector<AstChangeDet*>     m_blkChangeDetVec;      // All encountered changes in block
    bool    m_slow;           // Creating __Slow file
    bool    m_fast;           // Creating non __Slow file (or both)

    //---------------------------------------
    // 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; word<changep->lhsp()->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(AstNodeModule* 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 (v3Global.opt.lintOnly()) {
          // Unfortunately we have some lint checks here, so we can't just skip processing.
          // We should move them to a different stage.
          string filename = "/dev/null";
          newCFile(filename, slow, source);
          ofp = new V3OutSpFile (filename);
      }
      else 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();
      if (modp->isTop() && !source) {
          ofp->puts("// DESCR" "IPTION: Verilator output: Primary design header\n");
          ofp->puts("//\n");
          ofp->puts("// This header should be included by all source files instantiating the design.\n");
          ofp->puts("// The class here is then constructed to instantiate the design.\n");
          ofp->puts("// See the Verilator manual for examples.\n");
      } else {
          if (source) {
            ofp->puts("// DESCR" "IPTION: Verilator output: Design implementation internals\n");
          } else {
            ofp->puts("// DESCR" "IPTION: Verilator output: Design internal header\n");
          }
          ofp->puts("// See "+v3Global.opt.prefix()+".h for the primary calling header\n");
      }
      ofp->puts("\n");

      return ofp;
    }

    //---------------------------------------
    // VISITORS
    virtual void visit(AstCFunc* nodep, AstNUser*) {
      // TRACE_* and DPI handled elsewhere
      if (nodep->funcType().isTrace()) return;
      if (nodep->dpiImport()) return;
      if (!(nodep->slow() ? m_slow : m_fast)) return;

      m_blkChangeDetVec.clear();

      splitSizeInc(nodep);

      puts("\n");
      puts(nodep->rtnTypeVoid()); puts(" ");
      puts(modClassName(m_modp)+"::"+nodep->name()
           +"("+cFuncArgs(nodep)+") {\n");

      puts("VL_DEBUG_IF(VL_PRINTF(\"  ");
      for (int i=0;i<m_modp->level();i++) { puts("  "); }
      puts(modClassName(m_modp)+"::"+nodep->name()
           +"\\n\"); );\n");

      if (nodep->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<AstChangeDet*>::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<AstChangeDet*>::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(")) VL_PRINTF(\"\tCHANGE: "+nodep->fileline()->ascii()
                   +varname+"\\n\"); );\n");
            }
          }
      }
    }

    virtual void visit(AstChangeDet* nodep, AstNUser*) {
      m_blkChangeDetVec.push_back(nodep);
    }

    //---------------------------------------
    // ACCESSORS

    // METHODS
    // Low level
    void emitVarResets(AstNodeModule* modp);
    void emitCellCtors(AstNodeModule* modp);
    void emitSensitives();
    // Medium level
    void emitCtorImp(AstNodeModule* modp);
    void emitConfigureImp(AstNodeModule* modp);
    void emitCoverageDecl(AstNodeModule* modp);
    void emitCoverageImp(AstNodeModule* modp);
    void emitDestructorImp(AstNodeModule* modp);
    void emitTextSection(AstType type);
    void emitIntFuncDecls(AstNodeModule* modp);
    // High level
    void emitImp(AstNodeModule* modp);
    void emitStaticDecl(AstNodeModule* modp);
    void emitWrapEval(AstNodeModule* modp);
    void emitInt(AstNodeModule* modp);
    void writeMakefile(string filename);

public:
    EmitCImp() {
      m_modp = NULL;
    }
    virtual ~EmitCImp() {}
    void main(AstNodeModule* modp, bool slow, bool fast);
    void mainDoFunc(AstCFunc* nodep) {
      nodep->accept(*this);
    }
};

//######################################################################
// Internal EmitCStmts

void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
    AstBasicDType* basicp = nodep->basicp();  if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type");
    if (nodep->isIO()) {
      bool isArray = !nodep->dtypeSkipRefp()->castBasicDType();
      if (nodep->isSc()) {
          if (isArray) {
            nodep->v3error("Unsupported: SystemC inputs and outputs must be simple data types; no arrays");
          }
          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->dtypeSkipRefp()->widthAlignBytes(),
                      nodep->dtypeSkipRefp()->widthTotalBytes());
          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 (isArray) {
            if (nodep->isWide()) puts("W");
            puts("("+nodep->name());
            for (AstArrayDType* arrayp=nodep->dtypeSkipRefp()->castArrayDType(); arrayp; arrayp = arrayp->dtypeSkipRefp()->castArrayDType()) {
                puts("["+cvtToStr(arrayp->elementsConst())+"]");
            }
            puts(","+cvtToStr(basicp->msb())+","+cvtToStr(basicp->lsb()));
            if (basicp->isWide()) puts(","+cvtToStr(basicp->widthWords()));
          } else {
            if (!basicp->isWide())
                puts("("+nodep->name()
                   +","+cvtToStr(basicp->msb())
                   +","+cvtToStr(basicp->lsb()));
            else puts("W("+nodep->name()
                    +","+cvtToStr(basicp->msb())
                    +","+cvtToStr(basicp->lsb())
                    +","+cvtToStr(basicp->widthWords()));
          }
          puts(");\n");
      }
    } else if (basicp && basicp->isOpaque()) {
      // strings and other fundamental c types
      puts(nodep->vlArgType(true,false));
      // This isn't very robust and may need cleanup for other data types
      for (AstArrayDType* arrayp=nodep->dtypeSkipRefp()->castArrayDType(); arrayp; arrayp = arrayp->dtypeSkipRefp()->castArrayDType()) {
          puts("["+cvtToStr(arrayp->elementsConst())+"]");
      }
      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->dtypeSkipRefp()->widthAlignBytes(),
                  nodep->dtypeSkipRefp()->widthTotalBytes());
      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());
      // This isn't very robust and may need cleanup for other data types
      for (AstArrayDType* arrayp=nodep->dtypeSkipRefp()->castArrayDType(); arrayp; arrayp = arrayp->dtypeSkipRefp()->castArrayDType()) {
          puts("["+cvtToStr(arrayp->elementsConst())+"]");
      }
      puts(","+cvtToStr(basicp->msb())+","+cvtToStr(basicp->lsb()));
      if (basicp->isWide()) puts(","+cvtToStr(basicp->widthWords()));
      puts(");\n");
    }
}

void EmitCStmts::emitVarCtors() {
    ofp()->indentInc();
    bool first = true;
    for (vector<AstVar*>::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("("); putsQuoted((*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& format,
                      AstNode* lhsp, AstNode* rhsp, AstNode* thsp) {
    // Look at emitOperator() format for term/uni/dual/triops,
    // and write out appropriate text.
    //  %n* node
    //   %nq        emitIQW on the [node]
    //   %nw        width in bits
    //   %nW        width in words
    //   %ni        iterate
    //      %l*   lhsp - if appropriate, then second char as above
    //      %r*   rhsp - if appropriate, then second char as above
    //      %t*   thsp - if appropriate, then second char as above
    //      %k    Potential line break
    //  %P  Wide temporary name
    //      ,     Commas suppressed if the previous field is suppressed
    string nextComma;
    bool needComma = false;
#define COMMA { if (nextComma!="") { puts(nextComma); nextComma=""; } }

    putbs("");
    for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
      if (pos[0]==',') {
          // Remember we need to add one, but don't do yet to avoid ",)"
          if (needComma) {
            if (pos[1]==' ') { nextComma=", "; }
            else nextComma = ",";
            needComma = false;
          }
          if (pos[1]==' ') { ++pos; } // Must do even if no nextComma
      }
      else if (pos[0]=='%') {
          ++pos;
          bool detail = false;
          AstNode* detailp = NULL;
          switch (pos[0]) {
          case '%': puts("%");  break;
          case 'k': putbs("");  break;
          case 'n': detail = true; detailp = nodep; break;
          case 'l': detail = true; detailp = lhsp; break;
          case 'r': detail = true; detailp = rhsp; break;
          case 't': detail = true; detailp = thsp; break;
          case 'P':
            if (nodep->isWide()) {
                if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?");
                COMMA;
                puts(m_wideTempRefp->hiername());
                puts(m_wideTempRefp->varp()->name());
                m_wideTempRefp = NULL;
                needComma = true;
            }
            break;
          default:
            nodep->v3fatalSrc("Unknown emitOperator format code: %"<<pos[0]);
            break;
          }
          if (detail) {
            // Get next letter of %[nlrt]
            ++pos;
            switch (pos[0]) {
            case 'q': emitIQW(detailp); break;
            case 'w': 
                COMMA;
                puts(cvtToStr(detailp->widthMin()));
                needComma = true;
                break;
            case 'W':
                if (lhsp->isWide()) {
                  COMMA;
                  puts(cvtToStr(lhsp->widthWords()));
                  needComma = true;
                }
                break;
            case 'i':
                COMMA;
                if (!detailp) { nodep->v3fatalSrc("emitOperator() references undef node"); }
                else detailp->iterateAndNext(*this);
                needComma = true;
                break;
            default:
                nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]"<<pos[0]);
                break;
            }
          }
      } else {
          // Normal text
          if (pos[0] == ')') nextComma="";
          COMMA;
          string s; s+=pos[0]; puts(s);
      }
    }
}

//----------------------------------------------------------------------
// Mid level - VISITS

// We only do one display at once, so can just use static state

struct EmitDispState {
    string        m_format;   // "%s" and text from user
    vector<AstNode*>    m_argsp;    // Each argument to be printed
    vector<string>      m_argsFunc; // Function before each argument to be printed
    EmitDispState() { clear(); }
    void clear() {
      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(AstNode* nodep, bool isScan) {
    if (emitDispState.m_format == ""
      && nodep->castDisplay()) { // not fscanf etc, as they need to return value
      // NOP
    } else {
      // Format
      bool isStmt = false;
      if (AstFScanF* dispp = nodep->castFScanF()) {
          isStmt = false;
          puts("VL_FSCANF_IX(");
          dispp->filep()->iterate(*this);
          puts(",");
      } else if (AstSScanF* dispp = nodep->castSScanF()) {
          isStmt = false;
          checkMaxWords(dispp->fromp());
          puts("VL_SSCANF_I"); emitIQW(dispp->fromp()); puts("X(");
          puts(cvtToStr(dispp->fromp()->widthMin()));
          puts(",");
          dispp->fromp()->iterate(*this);
          puts(",");
      } else if (AstDisplay* dispp = nodep->castDisplay()) {
          isStmt = true;
          if (dispp->filep()) {
            puts("VL_FWRITEF(");
            dispp->filep()->iterate(*this);
            puts(",");
          } else {
            puts("VL_WRITEF(");
          }
      } else if (AstSFormat* dispp = nodep->castSFormat()) {
          isStmt = true;
          puts("VL_SFORMAT_X(");
          puts(cvtToStr(dispp->lhsp()->widthMin()));
          putbs(",");
          dispp->lhsp()->iterate(*this);
          putbs(",");
      } else if (AstSFormatF* dispp = nodep->castSFormatF()) {
          isStmt = false;
          if (dispp) {}
          puts("VL_SFORMATF_NX(");
      } else {
          isStmt = true;
          nodep->v3fatalSrc("Unknown displayEmit node type");
      }
      ofp()->putsQuoted(emitDispState.m_format);
      // 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) {
            if (isScan) puts("&(");
            argp->iterate(*this);
            if (isScan) puts(")");
          }
          ofp()->indentDec();
      }
        // End
      puts(")");
      if (isStmt) puts(";\n");
      else puts(" ");
      // Prep for next
      emitDispState.clear();
    }
}

void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
                      string vfmt, char fmtLetter) {
    // Print display argument, edits elistp
    AstNode* argp = *elistp;
    if (!argp) {
      // expectDisplay() checks this first, so internal error if found here
      dispp->v3error("Internal: Missing arguments for $display format");
      return;
    }
    if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
      dispp->v3error("Exceeded limit of 1024 bits for any display arguments");
    }
    if (argp && argp->isWide()
      && (fmtLetter=='d'||fmtLetter=='u')) {
      argp->v3error("Unsupported: "<<dispp->verilogKwd()<<" of dec format of > 64 bit results (use hex format instead)");
    }
    if (argp && argp->widthMin()>8 && fmtLetter=='c') {
      // Technically legal, but surely not what the user intended.
      argp->v3error(dispp->verilogKwd()<<" of char format of > 8 bit result");
    }

    //string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
    string pfmt;
    if ((fmtLetter=='u' || fmtLetter=='d' || fmtLetter=='t')
      && !isScan
      && vfmt == "") { // Size decimal output.  Spec says leading spaces, not zeros
      double mantissabits = argp->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);
      pfmt = string("%") + cvtToStr(nchars) + fmtLetter;
    } else {
      pfmt = string("%") + vfmt + fmtLetter;
    }
    emitDispState.pushFormat(pfmt);
    emitDispState.pushArg(NULL,cvtToStr(argp->widthMin()));
    emitDispState.pushArg(argp,"");

    // Next parameter
    *elistp = (*elistp)->nextp();
}

void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
                       const string& vformat, AstNode* exprsp,
                       bool isScan) {
    AstNode* elistp = exprsp;

    // Convert Verilog display to C printf formats
    //            "%0t" becomes "%d"
    emitDispState.clear();
    string vfmt = "";
    string::const_iterator pos = vformat.begin();
    bool inPct = false;
    for (; pos != vformat.end(); ++pos) {
      //UINFO(1,"Parse '"<<*pos<<"'  IP"<<inPct<<" List "<<(void*)(elistp)<<endl);
      if (!inPct && pos[0]=='%') {
          inPct = true;
          vfmt = "";
      } else if (!inPct) {   // Normal text
          emitDispState.pushFormat(*pos);
      } else { // Format character
          inPct = false;
          switch (tolower(pos[0])) {
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
            // Digits, like %5d, etc.
            vfmt += pos[0];
            inPct = true;  // Get more digits
            break;
          case '%':
            emitDispState.pushFormat("%%");  // We're printf'ing it, so need to quote the %
            break;
          // Special codes
          case '~': displayArg(nodep,&elistp,isScan, vfmt,'d'); break;  // Signed decimal
          // Spec: h d o b c l
          case 'b': displayArg(nodep,&elistp,isScan, vfmt,'b'); break;
          case 'c': displayArg(nodep,&elistp,isScan, vfmt,'c'); break;
          case 't': displayArg(nodep,&elistp,isScan, vfmt,'t'); break;
          case 'd': displayArg(nodep,&elistp,isScan, vfmt,'u'); break;  // Unsigned decimal
          case 'o': displayArg(nodep,&elistp,isScan, vfmt,'o'); break;
          case 'h':
          case 'x': displayArg(nodep,&elistp,isScan, vfmt,'x'); break;
          case 's': displayArg(nodep,&elistp,isScan, vfmt,'s'); break;
          case 'm': {
            if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName");
            string suffix = scopenamep->scopePrettyName();
            if (suffix=="") emitDispState.pushFormat("%S");
            else emitDispState.pushFormat("%N");  // Add a . when needed
            emitDispState.pushArg(NULL, "vlSymsp->name()");
            emitDispState.pushFormat(suffix);
            break;
          }
          case 'u':
          case 'z':
          case 'l':
          case 'v':
            nodep->v3error("Unsupported: $display format code: %"<<pos[0]);
            break;
          default:
            nodep->v3error("Unknown $display format code: %"<<pos[0]);
            break;
          }
      }
    }
    if (elistp != NULL) {
      // expectFormat also checks this, and should have found it first, so internal
      elistp->v3error("Internal: Extra arguments for $display format");
    }
    displayEmit(nodep, isScan);
}

//######################################################################
// Internal EmitC

void EmitCImp::emitVarResets(AstNodeModule* 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->basicp() && varp->basicp()->keyword() == AstBasicDTypeKwd::STRING) {
            // Constructor deals with it
          }
          else if (varp->isParam()) {
            if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?");
            //puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n");
          }
          else if (AstInitArray* initarp = varp->valuep()->castInitArray()) {
            AstConst* constsp = initarp->initsp()->castConst();
            if (AstArrayDType* arrayp = varp->dtypeSkipRefp()->castArrayDType()) {
                for (int i=0; i<arrayp->elementsConst(); i++) {
                  if (!constsp) initarp->v3fatalSrc("Not enough values in array initalizement");
                  emitSetVarConstant(varp->name()+"["+cvtToStr(i)+"]", constsp);
                  constsp = constsp->nextp()->castConst();
                }
            } else {
                varp->v3fatalSrc("InitArray under non-arrayed var");
            }
          }
          else {
            int vects = 0;
            // This isn't very robust and may need cleanup for other data types
            for (AstArrayDType* arrayp=varp->dtypeSkipRefp()->castArrayDType(); arrayp;
                 arrayp = arrayp->dtypeSkipRefp()->castArrayDType()) {
                int vecnum = vects++;
                if (arrayp->msb() < arrayp->lsb()) 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->basicp() && varp->basicp()->isZeroInit())
                         || (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; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
                puts(");\n");
            } else {
                puts(varp->name());
                for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
                if (zeroit) {
                  puts("= 0;\n");
                } else {
                  puts(" = VL_RAND_RESET_");
                  emitIQW(varp);
                  puts("(");
                  puts(cvtToStr(varp->widthMin()));
                  puts(");\n");
                }
            }
            for (int v=0; v<vects; ++v) puts( "}}\n");
          }
      }
    }
}

void EmitCImp::emitCoverageDecl(AstNodeModule* modp) {
    if (v3Global.opt.coverage()) {
      ofp()->putsPrivate(true);
      puts("// Coverage\n");
      puts("void __vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n");
      puts(       "const char* hierp, const char* pagep, const char* commentp);\n");
    }
}

void EmitCImp::emitCtorImp(AstNodeModule* 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::atSCCTOR);
    if (optSystemPerl()) puts("SP_AUTO_CTOR;\n");
    puts("}\n");
}

void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
    puts("\nvoid "+modClassName(modp)+"::__Vconfigure("+symClassName()+"* vlSymsp, bool first) {\n");
    puts(   "if (0 && first) {}  // Prevent unused\n");
    puts(   "this->__VlSymsp = vlSymsp;\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(AstNodeModule* modp) {
    if (v3Global.opt.coverage() ) {
      puts("\n// Coverage\n");
      // Rather than putting out SP_COVER_INSERT calls directly, we do it via this function
      // This gets around gcc slowness constructing all of the template arguments
      // SystemPerl 1.301 is much faster, but it's nice to remain back
      // compatible, and have a common wrapper.
      puts("void "+modClassName(m_modp)+"::__vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n");
      puts(       "const char* hierp, const char* pagep, const char* commentp) {\n");
      puts(   "static uint32_t fake_zero_count = 0;\n");
      puts(   "if (!enable) countp = &fake_zero_count;\n");  // Used for second++ instantiation of identical bin
      puts(   "*countp = 0;\n");
      puts(   "SP_COVER_INSERT(countp,");
      puts( "  \"filename\",filenamep,");
      puts( "  \"lineno\",lineno,");
      puts( "  \"column\",column,\n");
      //puts(     "\"hier\",string(__VlSymsp->name())+hierp,");  // Need to move hier into scopes and back out if do this
      puts( "\"hier\",string(name())+hierp,");
      puts( "  \"page\",pagep,");
      puts( "  \"comment\",commentp);\n");
      puts("}\n");
    }
}

void EmitCImp::emitDestructorImp(AstNodeModule* modp) {
    puts("\n");
    puts(modClassName(modp)+"::~"+modClassName(modp)+"() {\n");
    emitTextSection(AstType::atSCDTOR);
    if (modp->isTop()) puts("delete __VlSymsp; __VlSymsp=NULL;\n");
    puts("}\n");
}

void EmitCImp::emitStaticDecl(AstNodeModule* 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())
                                +" ");
                ofp()->putsQuoted(nodep->fileline()->filename());
                ofp()->putsNoTracking("\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(AstNodeModule* 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(AstNodeModule* 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(VL_PRINTF(\"\\n----TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
#ifndef NEW_ORDERING
    puts("int __VclockLoop = 0;\n");
    puts("IData __Vchange=1;\n");
    puts("while (VL_LIKELY(__Vchange)) {\n");
    puts(    "VL_DEBUG_IF(VL_PRINTF(\" Clock loop\\n\"););\n");
#endif
    puts(    "vlSymsp->__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;
      const int sortmax = 9;
      for (int sort=0; sort<sortmax; sort++) {
          if (sort==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() && !varp->isIO()); break;
                default: v3fatalSrc("Bad Case");
                }
                if (varp->isStatic() ? !isstatic : isstatic) doit=false;
                if (doit) {
                  int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
                  int sortbytes = sortmax-1;
                  if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
                  else if (varp->dtypeSkipRefp()->castArrayDType()) sortbytes=8;
                  else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
                  else if (varp->isScBv()) sortbytes=6;
                  else if (sigbytes==8) sortbytes=5;
                  else if (sigbytes==4) sortbytes=4;
                  else if (sigbytes==2) sortbytes=2;
                  else if (sigbytes==1) sortbytes=1;
                  if (sort==sortbytes) {
                      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(AstNodeModule* modp) {
    vector<AstCFunc*> 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<AstCFunc*>::iterator it = funcsp.begin(); it != funcsp.end(); ++it) {
      AstCFunc* funcp = *it;
      if (!funcp->dpiImport()) {  // DPI is prototyped in __Dpi.h
          ofp()->putsPrivate(funcp->declPrivate());
          if (funcp->isStatic()) puts("static ");
          puts(funcp->rtnTypeVoid()); puts("\t");
          puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n");
      }
    }
}

void EmitCImp::emitInt(AstNodeModule* 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();
    if (v3Global.needHeavy()) {
      puts("#include \"verilated_heavy.h\"\n");
    } else {
      puts("#include \"verilated.h\"\n");
    }
    if (v3Global.opt.coverage()) {
      puts("#include \"SpCoverage.h\"\n");
    }
    if (v3Global.needHInlines()) {   // Set by V3EmitCInlines; should have been called before us
      puts("#include \""+topClassName()+"__Inlines.h\"\n");
    }
    // No __Dpi.h needed, we've shimmed it all into the interface

    // 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");
      }
    }
    if (v3Global.opt.trace()) {
      puts("class "+v3Global.opt.traceClassBase()+";\n");
    }

    puts("\n//----------\n\n");
    emitTextSection(AstType::atSCHDR);

    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");
      if (modp->isTop()) puts("// Public to allow access to /*verilator_public*/ items;\n");
      if (modp->isTop()) puts("// otherwise the application code can consider these internals.\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");
    if (modp->isTop()) puts("// The application code writes and reads these signals to\n");
    if (modp->isTop()) puts("// propagate new values into/out from the Verilated model.\n");
    emitVarList(modp->stmtsp(), EVL_IO, "");

    puts("\n// LOCAL SIGNALS\n");
    if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
    emitVarList(modp->stmtsp(), EVL_SIG, "");

    puts("\n// LOCAL VARIABLES\n");
    if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
    emitVarList(modp->stmtsp(), EVL_TEMP, "");

    puts("\n// INTERNAL VARIABLES\n");
    if (modp->isTop()) puts("// Internals; generally not touched by application code\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");
    if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\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->valuep()) 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->isWide()) {   // Unsupported for output
                puts("// enum WData "+varp->name()+"  //wide");
            } else if (!varp->valuep()->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->valuep()->iterateAndNext(*this);
                puts("};");
            }
            puts("\n");
          }
      }
    }

    puts("\n// CONSTRUCTORS\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 {
      if (modp->isTop()) {
          puts("/// Construct the model; called by application code\n");
          puts("/// The special name "" may be used to make a wrapper with a\n");
          puts("/// single model invisible WRT DPI scope names.\n");
      }
      puts(modClassName(modp)+"(const char* name=\"TOP\");\n");
      if (modp->isTop()) puts("/// Destroy the model; called (often implicitly) by application code\n");
      puts("~"+modClassName(modp)+"();\n");
    }
    if (v3Global.opt.trace() && !optSystemPerl()) {
      if (modp->isTop()) puts("/// Trace signals in the model; called by application code\n");
      puts("void trace (VerilatedVcdC* tfp, int levels, int options=0);\n");
    }

    puts("\n// USER METHODS\n");
    if (optSystemPerl()) puts("/*AUTOMETHODS*/\n");
    emitTextSection(AstType::atSCINT);

    puts("\n// API METHODS\n");
    if (modp->isTop()) {
      if (optSystemC()) ofp()->putsPrivate(true);     ///< eval() is invoked by our sensitive() calls.
      else puts("/// Evaluate the model.  Application must call when inputs change.\n");
      puts("void eval();\n");
      ofp()->putsPrivate(false);  // public:
      if (!optSystemC()) puts("/// Simulation complete, run final blocks.  Application must call on completion.\n");
      puts("void final();\n");
      if (v3Global.opt.inhibitSim()) {
          puts("void inhibitSim(bool flag) { __Vm_inhibitSim=flag; }\t///< Set true to disable evaluation of module\n");
      }
    }

    puts("\n// INTERNAL METHODS\n");
    if (modp->isTop()) {
      ofp()->putsPrivate(true);  // private:
      puts("static void _eval_initial_loop("+EmitCBaseVisitor::symClassVar()+");\n");
    }

    ofp()->putsPrivate(false);  // public:
    puts("void __Vconfigure("+symClassName()+"* symsp, bool first);\n");

    emitIntFuncDecls(modp);

    if (!optSystemPerl() && v3Global.opt.trace()) {
      ofp()->putsPrivate(false);  // public:
      puts("static void traceInit ("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n");
      puts("static void traceFull ("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n");
      puts("static void traceChg  ("+v3Global.opt.traceClassBase()+"* 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(AstNodeModule* 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 (v3Global.dpi()) {
      puts("#include \""+ topClassName() +"__Dpi.h\"\n");
    }

    if (optSystemPerl() && (splitFilenum() || !m_fast)) {
      puts("\n");
      puts("SP_MODULE_CONTINUED("+modClassName(modp)+");\n");
    }

    emitTextSection(AstType::atSCIMPHDR);

    if (m_slow && splitFilenum()==0) {
      puts("\n//--------------------\n");
      puts("// STATIC VARIABLES\n\n");
      emitVarList(modp->stmtsp(), EVL_ALL, modClassName(modp));
    }

    if (m_fast && splitFilenum()==0) {
      emitTextSection(AstType::atSCIMP);
      emitStaticDecl(modp);
    }

    if (m_slow && splitFilenum()==0) {
      puts("\n//--------------------\n");
      emitCtorImp(modp);
      emitConfigureImp(modp);
      emitDestructorImp(modp);
      emitCoverageImp(modp);
    }

    if (m_fast && splitFilenum()==0) {
      if (modp->isTop()) {
          emitStaticDecl(modp);
          puts("\n//--------------------\n");
          puts("\n");
          emitWrapEval(modp);
      }
    }

    if (m_fast && splitFilenum()==0) {
      if (v3Global.opt.trace() && optSystemPerl() && m_modp->isTop()) {
          puts("\n");
          puts("\n/*AUTOTRACE(__MODULE__,recurse,activity,exists)*/\n\n");
      }
    }

    // Blocks
    puts("\n//--------------------\n");
    puts("// Internal Methods\n");
}

//######################################################################

void EmitCImp::main(AstNodeModule* 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) {
      UINFO(0,"  Emitting "<<modClassName(modp)<<endl);
    }

    if (optSystemPerl()) {
      m_ofp = newOutCFile(modp, !m_fast, true);

      if (m_fast) {
          puts("#sp interface\n");
          emitInt (modp);
      }
    }
    else if (optSystemC()) {
      if (m_fast) {
          m_ofp = newOutCFile (modp, !m_fast, false/*source*/);
          emitInt (modp);
          delete m_ofp; m_ofp=NULL;
      }

      m_ofp = newOutCFile (modp, !m_fast, true/*source*/);
    }
    else {
      if (m_fast) {
          m_ofp = newOutCFile (modp, !m_fast, false/*source*/);
          emitInt (modp);
          delete m_ofp; m_ofp=NULL;
      }

      m_ofp = newOutCFile (modp, !m_fast, true/*source*/);
    }

    emitImp (modp);

    for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
      if (AstCFunc* funcp = nodep->castCFunc()) {
          if (splitNeeded()) {
            // Close old file
            delete m_ofp; m_ofp=NULL;
            // Open a new file
            m_ofp = newOutCFile (modp, !m_fast, true/*source*/, splitFilenumInc());
            emitImp (modp);
          }
          mainDoFunc(funcp);
      }
    }

    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 newOutCFile(int filenum) {
      string filename = (v3Global.opt.makeDir()+"/"+ topClassName()
                     + (m_slow?"__Trace__Slow":"__Trace"));
      if (filenum) filename += "__"+cvtToStr(filenum);
      filename += ".cpp";

      AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/);
      cfilep->support(true);

      if (m_ofp) v3fatalSrc("Previous file not closed");
      m_ofp = new V3OutCFile (filename);
      m_ofp->putsHeader();
      m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n");

      emitTraceHeader();
    }

    void emitTraceHeader() {
      // Includes
      if (optSystemPerl()) {
          puts("#include \"SpTraceVcd.h\"\n");
      } else {
          puts("#include \"verilated_vcd_c.h\"\n");
      }
      puts("#include \""+ symClassName() +".h\"\n");
      puts("\n");
    }

    void emitTraceSlow() {
      puts("\n//======================\n\n");

      puts("void "+topClassName()+"::trace (");
      if (optSystemPerl()) {
          puts("SpTraceFile* tfp, int, int) {\n");
      } else {
          puts("VerilatedVcdC* tfp, int, int) {\n");
      }
      puts(  "tfp->spTrace()->addCallback ("
             "&"+topClassName()+"::traceInit"
             +", &"+topClassName()+"::traceFull"
             +", &"+topClassName()+"::traceChg, this);\n");
      puts("}\n");

      puts("void "+topClassName()+"::traceInit("
           +v3Global.opt.traceClassBase()+"* 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("vcdp->scopeEscape(' ');\n");
      puts("t->traceInitThis (vlSymsp, vcdp, code);\n");
      puts("vcdp->scopeEscape('.');\n");  // Restore so SystemPerl traced files won't break
      puts("}\n");

      puts("void "+topClassName()+"::traceFull("
           +v3Global.opt.traceClassBase()+"* 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("
           +v3Global.opt.traceClassBase()+"* 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 emitTraceIsScBv(AstTraceInc* nodep) {
      AstVarRef* varrefp = nodep->valuep()->castVarRef();
      if (!varrefp) return false;
      AstVar* varp = varrefp->varp();
      return varp->isSc() && varp->isScBv();
    }
    void emitTraceInitOne(AstTraceDecl* nodep) {
      if (nodep->isWide()) {
          puts("vcdp->declArray");
      } else if (nodep->isQuad()) {
          puts("vcdp->declQuad ");
      } else if (nodep->msbEndianed() || nodep->lsbEndianed()) {
          puts("vcdp->declBus  ");
      } else {
          puts("vcdp->declBit  ");
      }
      puts("(c+"+cvtToStr(nodep->code()));
      if (nodep->arrayWidth()) puts("+i*"+cvtToStr(nodep->widthWords()));
      puts(",");
      putsQuoted(nodep->showname());
      if (nodep->arrayWidth()) {
          puts(",(i+"+cvtToStr(nodep->arrayLsb())+")");
      } else {
          puts(",-1");
      }
      if (nodep->msbEndianed() || nodep->lsbEndianed()) {
          puts(","+cvtToStr(nodep->msbEndianed())+","+cvtToStr(nodep->lsbEndianed()));
      }
      puts(");");
    }

    void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
      nodep->precondsp()->iterateAndNext(*this);
      string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL
                  || m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB)
                   ? "full":"chg");
      if (nodep->isWide() || emitTraceIsScBv(nodep)) {
          puts("vcdp->"+full+"Array");
      } else if (nodep->isQuad()) {
          puts("vcdp->"+full+"Quad ");
      } else if (nodep->declp()->msbEndianed() || nodep->declp()->lsbEndianed()) {
          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()->msbEndianed() || nodep->declp()->lsbEndianed() || emitTraceIsScBv(nodep)) {
          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();
          puts("(");
          if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP(");
          varrefp->iterate(*this);  // Put var name out
          // Tracing only supports 1D arrays
          if (varp->dtypeSkipRefp()->castArrayDType()) {
            if (arrayindex==-2) puts("[i]");
            else if (arrayindex==-1) puts("[0]");
            else puts("["+cvtToStr(arrayindex)+"]");
          }
          if (varp->isSc()) puts(".read()");
          if (emitTraceIsScBv(nodep)) puts(")");
          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(AstNodeModule* nodep, AstNUser*) {
      nodep->iterateChildren(*this);
    }
    virtual void visit(AstCFunc* nodep, AstNUser*) {
      if (nodep->slow() != m_slow) return;
      if (nodep->funcType().isTrace()) {   // TRACE_*
          m_funcp = nodep;

          if (splitNeeded()) {
            // Close old file
            delete m_ofp; m_ofp=NULL;
            // Open a new file
            newOutCFile (splitFilenumInc());
          }

          splitSizeInc(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_INIT_SUB) {
          } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) {
          } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) {
          } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) {
          } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
          } 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");
          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; i<nodep->declp()->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
      newOutCFile(0);

      if (m_slow) emitTraceSlow();
      else emitTraceFast();

      v3Global.rootp()->accept(*this);

      delete m_ofp; m_ofp=NULL;
    }
};

//######################################################################
// EmitC class functions

void V3EmitC::emitc() {
    UINFO(2,__FUNCTION__<<": "<<endl);
    // Process each module in turn
    for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
      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__<<": "<<endl);
    if (v3Global.opt.trace()) {
      { EmitCTrace imp (true);  imp.main(); }
      { EmitCTrace imp (false); imp.main(); }
    }
}

Generated by  Doxygen 1.6.0   Back to index