diff --git a/Changes b/Changes index a5468b37e..91662df0b 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Support $random. +**** Internal changes to how $displays get compiled and executed. + * Verilator 3.665 2008/06/25 **** Ignore "// verilator" comments alone on endif lines. [Rodney Sinclair] diff --git a/include/verilated.cpp b/include/verilated.cpp index 663b56881..2be4f4e9c 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -138,94 +138,127 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) { //=========================================================================== // Formatting -const char* VL_VALUE_FORMATTED_Q(int obits, char fmt, bool drop0, QData ld) { - // Convert value into %b/%o/%x/%s/%u/%d formatted string - // Note uses a single buffer; presumes only one call per printf - static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH]; - char* strp = &str[0]; - int lsb=obits-1; - if (drop0) while (lsb && !VL_BITISSET_Q(ld,lsb)) lsb--; - switch (fmt) { - case 'd': - sprintf(str,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(obits,obits,ld))); - return str; - case 'u': - sprintf(str,"%llu",ld); - return str; - case 's': - for (; lsb>=0; lsb--) { - lsb = (lsb / 8) * 8; // Next digit - IData charval = (ld>>VL_BITBIT_Q(lsb)) & 0xff; - *strp++ = (charval==0)?' ':charval; - } - *strp++ = '\0'; - return str; - case 'b': - for (; lsb>=0; lsb--) { - *strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 1) + '0'; - } - *strp++ = '\0'; - return str; - case 'o': - for (; lsb>=0; lsb--) { - lsb = (lsb / 3) * 3; // Next digit - *strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 7) + '0'; - } - *strp++ = '\0'; - return str; - default: - for (; lsb>=0; lsb--) { - lsb = (lsb / 4) * 4; // Next digit - IData charval = (ld>>VL_BITBIT_Q(lsb)) & 0xf; - *strp++ = (charval + ((charval < 10) ? '0':('a'-10))); - } - *strp++ = '\0'; - return str; - } - *strp++ = '\0'; - return str; -} +// Do a va_arg returning a quad, assuming input argument is anything less than wide +#define _VL_VA_ARG_Q(ap, bits) (((bits) <= VL_WORDSIZE) ? va_arg(ap,IData) : va_arg(ap,QData)) -const char* VL_VALUE_FORMATTED_W(int obits, char fmt, bool drop0, WDataInP lwp) { - // Convert value into %b/%o/%x/%s/%u/%d formatted string - // Note uses a single buffer; presumes only one call per printf +void _vl_vsformat(string& output, const char* formatp, va_list ap) { + // Format a Verilog $write style format into the output list + // The format must be pre-processed (and lower cased) by Verilator + // Arguments are in "width, arg-value (or WDataIn* if wide)" form + // Note uses a single buffer internally; presumes only one usage per printf static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH]; - char* strp = &str[0]; - int lsb=obits-1; - if (drop0) while (lsb && !VL_BITISSET_W(lwp,lsb)) lsb--; - switch (fmt) { - case 's': - for (; lsb>=0; lsb--) { - lsb = (lsb / 8) * 8; // Next digit - IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff; - *strp++ = (charval==0)?' ':charval; + bool inPct = false; + bool widthSet = false; + int width = 0; + const char* pos = formatp; + for (; *pos; ++pos) { + if (!inPct && pos[0]=='%') { + inPct = true; + widthSet = false; + width = 0; + } else if (!inPct) { // Normal text + // Fast-forward to next escape and add to output + const char *ep = pos; + while (ep[0] && ep[0]!='%') ep++; + if (ep != pos) { + output += string(pos, ep-pos); + pos += ep-pos-1; + } + } else { // Format character + inPct = false; + char fmt = pos[0]; + switch (fmt) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + inPct = true; // Get more digits + widthSet = true; + width = width*10 + (fmt - '0'); + break; + case '%': + output += '%'; + break; + case 'S': { // "C" string + const char* cstrp = va_arg(ap, const char*); + output += cstrp; + break; + } + default: { + // Deal with all read-and-print somethings + int bits = va_arg(ap, int); + QData ld = 0; + WDataInP lwp; + if (bits <= VL_QUADSIZE) { + WData qlwp[2]; + ld = _VL_VA_ARG_Q(ap, bits); + VL_SET_WQ(qlwp,ld); + lwp = qlwp; + } else { + lwp = va_arg(ap,WDataInP); + ld = lwp[0]; + if (fmt == 'u' || fmt == 'd') fmt = 'x'; // Not supported, but show something + } + int lsb=bits-1; + if (widthSet && width==0) while (lsb && !VL_BITISSET_W(lwp,lsb)) lsb--; + switch (fmt) { + case 'b': + for (; lsb>=0; lsb--) { + output += ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0'; + } + break; + case 'c': { + IData charval = ld & 0xff; + output += charval; + break; + } + case 'd': { // Signed decimal + int digits=sprintf(str,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(bits,bits,ld))); + int needmore = width-digits; + if (needmore>0) output.append(needmore,' '); // Pre-pad spaces + output += str; + break; + } + case 'o': + for (; lsb>=0; lsb--) { + lsb = (lsb / 3) * 3; // Next digit + // Octal numbers may span more than one wide word, + // so we need to grab each bit separately and check for overrun + // Octal is rare, so we'll do it a slow simple way + output += ('0' + + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+0)) ? 1 : 0) + + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+1)) ? 2 : 0) + + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+2)) ? 4 : 0)); + } + break; + case 's': + for (; lsb>=0; lsb--) { + lsb = (lsb / 8) * 8; // Next digit + IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff; + output += (charval==0)?' ':charval; + } + break; + case 'u': { // Unsigned decimal + int digits=sprintf(str,"%llu",ld); + int needmore = width-digits; + if (needmore>0) output.append(needmore,' '); // Pre-pad spaces + output += str; + break; + } + case 'x': + for (; lsb>=0; lsb--) { + lsb = (lsb / 4) * 4; // Next digit + IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf; + output += "0123456789abcdef"[charval]; + } + break; + default: + string msg = string("%%Error: Unknown _vl_vsformat code: ")+pos[0]+"\n"; + vl_fatal(__FILE__,__LINE__,"",msg.c_str()); + break; + } // switch + } + } // switch } - *strp++ = '\0'; - return str; - case 'b': - for (; lsb>=0; lsb--) { - *strp++ = ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0'; - } - *strp++ = '\0'; - return str; - case 'o': - for (; lsb>=0; lsb--) { - lsb = (lsb / 3) * 3; // Next digit - *strp++ = ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 7) + '0'; - } - *strp++ = '\0'; - return str; - default: - for (; lsb>=0; lsb--) { - lsb = (lsb / 4) * 4; // Next digit - IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf; - *strp++ = (charval + ((charval < 10) ? '0':('a'-10))); - } - *strp++ = '\0'; - return str; } - *strp++ = '\0'; - return str; } //=========================================================================== @@ -259,7 +292,7 @@ void _VL_STRING_TO_VINT(int obits, void* destp, int srclen, const char* srcp) { IData VL_FGETS_IXQ(int obits, void* destp, QData fpq) { FILE* fp = VL_CVT_Q_FP(fpq); - if (!fp) return 0; + if (VL_UNLIKELY(!fp)) return 0; // The string needs to be padded with 0's in unused spaces in front of // any read data. This means we can't know in what location the first @@ -293,6 +326,31 @@ QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) { return VL_CVT_FP_Q(fopen(filenamez,modez)); } +void VL_WRITEF(const char* formatp, ...) { + + va_list ap; + va_start(ap,formatp); + string output; + _vl_vsformat(output, formatp, ap); + va_end(ap); + + // Users can redefine VL_PRINTF if they wish. + VL_PRINTF("%s", output.c_str()); +} + +void VL_FWRITEF(QData fpq, const char* formatp, ...) { + FILE* fp = VL_CVT_Q_FP(fpq); + if (VL_UNLIKELY(!fp)) return; + + va_list ap; + va_start(ap,formatp); + string output; + _vl_vsformat(output, formatp, ap); + va_end(ap); + + fputs(output.c_str(), fp); +} + void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int, QData ofilename, void* memp, IData start, IData end) { IData fnw[2]; VL_SET_WQ(fnw, ofilename); diff --git a/include/verilated.h b/include/verilated.h index b53c390d8..533dfa1f0 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -191,13 +191,6 @@ extern QData VL_RAND_RESET_Q(int obits); ///< Random reset a signal extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); ///< Random reset a signal extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); ///< Zero reset a signal -/// Return string with formated Verilog value -extern const char* VL_VALUE_FORMATTED_W(int obits, char fmt, bool drop0, WDataInP lwp); -extern const char* VL_VALUE_FORMATTED_Q(int obits, char fmt, bool drop0, QData ld); -inline const char* VL_VALUE_FORMATTED_I(int obits, char fmt, bool drop0, IData ld) { - return VL_VALUE_FORMATTED_Q(obits,fmt,drop0,ld); -} - /// File I/O extern IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq); @@ -213,6 +206,9 @@ inline void VL_READMEM_I(bool hex, int width, int depth, int array_lsb, int fnwo IData ofilename, void* memp, IData start, IData end) { VL_READMEM_Q(hex, width,depth,array_lsb,fnwords, ofilename,memp,start,end); } +extern void VL_WRITEF(const char* formatp, ...); +extern void VL_FWRITEF(QData fpq, const char* formatp, ...); + //========================================================================= // Base macros @@ -220,6 +216,7 @@ inline void VL_READMEM_I(bool hex, int width, int depth, int array_lsb, int fnwo #define VL_BITISSET_I(data,bit) (data & (VL_UL(1)<>VL_WORDSIZE); } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index ca1c20d83..f86ed50b3 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -79,9 +79,7 @@ public: // 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 displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter); void emitVarDecl(AstVar* nodep, const string& prefixIfImp); typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich; @@ -936,13 +934,11 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format, // 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(); @@ -958,13 +954,11 @@ 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("); + puts("VL_FWRITEF("); nodep->filep()->iterate(*this); - puts("),\""); + puts(",\""); } else { - puts("VL_PRINTF(\""); + puts("VL_WRITEF(\""); } ofp()->putsNoTracking(emitDispState.m_format); puts("\""); @@ -977,7 +971,6 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) { ofp()->putbs(""); if (func!="") puts(func); if (argp) argp->iterate(*this); - if (func!="") puts(")"); ofp()->indentDec(); } // End @@ -987,80 +980,43 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) { } } -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) { +void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter) { // Print display argument, edits elistp - if (!*elistp) { - dispp->v3error("Missing arguments for $display format"); + 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 ((*elistp)->widthMin() > VL_VALUE_STRING_MAX_WIDTH) { + if (argp->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); + if (argp && argp->isWide() + && (fmtLetter=='d'||fmtLetter=='u')) { + argp->v3error("Unsupported: $display 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("$display of char format of > 8 bit result"); + } + + //string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter; + string pfmt; + if ((fmtLetter=='u' || fmtLetter=='d') + && 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(); } @@ -1072,61 +1028,63 @@ void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) { // Convert Verilog display to C printf formats // "%0t" becomes "%d" emitDispState.clear(); - string fmt = ""; + string vfmt = ""; string::iterator pos = vformat.begin(); bool inPct = false; for (; pos != vformat.end(); ++pos) { - if (inPct && pos[0]=='%') { - emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % - inPct = false; - } else if (pos[0]=='%') { + //UINFO(1,"Parse '"<<*pos<<"' IP"<name("); - if (!nodep->scopeNamep()) nodep->v3fatalSrc("Display with %m but no AstScopeName"); - for (AstText* textp=nodep->scopeNamep()->scopeAttrp(); 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: %"<name()"); + if (!nodep->scopeNamep()) nodep->v3fatalSrc("Display with %m but no AstScopeName"); + for (AstText* textp=nodep->scopeNamep()->scopeAttrp(); 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"); + // expectFormat also checks this, and should have found it first, so internal + elistp->v3error("Internal: Extra arguments for $display format\n"); } if (nodep->addNewline()) emitDispState.pushFormat("\\n"); displayEmit(nodep); diff --git a/src/V3File.cpp b/src/V3File.cpp index f288c76c9..cca14a526 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -244,8 +244,9 @@ void V3File::createMakeDir() { // V3OutFile: A class for printing to a file, with automatic indentation of C++ code. V3OutFile::V3OutFile(const string& filename) - : m_lineno(1), m_column(0), m_nobreak(false), m_prependIndent(true), m_indentLevel(0) - , m_declSAlign(0), m_declNSAlign(0), m_declPadNum(0) { + : m_filename(filename), m_lineno(1), m_column(0) + , m_nobreak(false), m_prependIndent(true), m_indentLevel(0) + , m_declSAlign(0), m_declNSAlign(0), m_declPadNum(0) { if ((m_fp = V3File::new_fopen_w(filename.c_str())) == NULL) { v3fatal("Cannot write "<=0,"Underflow of indentation\n"); + void indentDec() { + m_indentLevel -= INDBLK; + UASSERT(m_indentLevel>=0, ": "<v3error("Unknown $display format code: %"<v3error("Missing arguments for $display format"); + } else { + argp = argp->nextp(); + } + } + break; + } // switch + } + } + if (argp) { + argp->v3error("Extra arguments for $display format\n"); + } + } + void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) { if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable"); if (filep && filep->varp()) filep->varp()->attrFileDescr(true); } + virtual void visit(AstFOpen* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); @@ -343,6 +379,7 @@ private: virtual void visit(AstDisplay* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); + expectFormat(nodep, nodep->name(), nodep->exprsp()); if (!m_assertp && (nodep->displayType() == AstDisplayType::INFO || nodep->displayType() == AstDisplayType::WARNING diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 9efd66630..21ef64400 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -345,6 +345,22 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const { return out.str(); } +bool V3Number::displayedFmtLegal(char format) { + // Is this a valid format letter? + switch (tolower(format)) { + case 'b': return true; + case 'c': return true; + case 'd': return true; // Unsigned decimal + case 'h': return true; + case 'o': return true; + case 's': return true; + case 't': return true; + case 'x': return true; + case '~': return true; // Signed decimal + default: return false; + } +} + string V3Number::displayed(const string& vformat) const { string::const_iterator pos = vformat.begin(); UASSERT(pos != vformat.end() && pos[0]=='%', "display with non format argument "<<*this); diff --git a/src/V3Number.h b/src/V3Number.h index d7d7b344d..6817f3377 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -119,6 +119,7 @@ public: // ACCESSORS string ascii(bool prefixed=true, bool cleanVerilog=false) const; string displayed(const string& format) const; + static bool displayedFmtLegal(char format); // Is this a valid format letter? int width() const { return m_width; } int minWidth() const; // Minimum width that can represent this number (~== log2(num)+1) bool sized() const { return m_sized; }