Rework $display/$write to go via new VL_WRITE function instead of

converting to pure C printf call.  This makes the resulting code smaller,
and allows sharing code with future $sprintf support.
This commit is contained in:
Wilson Snyder 2008-06-30 14:31:58 -04:00
parent 20aa21d4b6
commit 1a8c8bec0d
9 changed files with 289 additions and 217 deletions

View File

@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks!
*** Support $random. *** Support $random.
**** Internal changes to how $displays get compiled and executed.
* Verilator 3.665 2008/06/25 * Verilator 3.665 2008/06/25
**** Ignore "// verilator" comments alone on endif lines. [Rodney Sinclair] **** Ignore "// verilator" comments alone on endif lines. [Rodney Sinclair]

View File

@ -138,94 +138,127 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) {
//=========================================================================== //===========================================================================
// Formatting // Formatting
const char* VL_VALUE_FORMATTED_Q(int obits, char fmt, bool drop0, QData ld) { // Do a va_arg returning a quad, assuming input argument is anything less than wide
// Convert value into %b/%o/%x/%s/%u/%d formatted string #define _VL_VA_ARG_Q(ap, bits) (((bits) <= VL_WORDSIZE) ? va_arg(ap,IData) : va_arg(ap,QData))
// 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]; static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH];
char* strp = &str[0]; bool inPct = false;
int lsb=obits-1; bool widthSet = false;
if (drop0) while (lsb && !VL_BITISSET_Q(ld,lsb)) lsb--; int width = 0;
switch (fmt) { const char* pos = formatp;
case 'd': for (; *pos; ++pos) {
sprintf(str,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(obits,obits,ld))); if (!inPct && pos[0]=='%') {
return str; inPct = true;
case 'u': widthSet = false;
sprintf(str,"%llu",ld); width = 0;
return str; } else if (!inPct) { // Normal text
case 's': // Fast-forward to next escape and add to output
for (; lsb>=0; lsb--) { const char *ep = pos;
lsb = (lsb / 8) * 8; // Next digit while (ep[0] && ep[0]!='%') ep++;
IData charval = (ld>>VL_BITBIT_Q(lsb)) & 0xff; if (ep != pos) {
*strp++ = (charval==0)?' ':charval; output += string(pos, ep-pos);
pos += ep-pos-1;
} }
*strp++ = '\0'; } else { // Format character
return str; 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': case 'b':
for (; lsb>=0; lsb--) { for (; lsb>=0; lsb--) {
*strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 1) + '0'; 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;
} }
*strp++ = '\0';
return str;
case 'o': case 'o':
for (; lsb>=0; lsb--) { for (; lsb>=0; lsb--) {
lsb = (lsb / 3) * 3; // Next digit lsb = (lsb / 3) * 3; // Next digit
*strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 7) + '0'; // 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));
} }
*strp++ = '\0'; break;
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;
}
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
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': case 's':
for (; lsb>=0; lsb--) { for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit lsb = (lsb / 8) * 8; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff; IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
*strp++ = (charval==0)?' ':charval; output += (charval==0)?' ':charval;
} }
*strp++ = '\0'; break;
return str; case 'u': { // Unsigned decimal
case 'b': int digits=sprintf(str,"%llu",ld);
for (; lsb>=0; lsb--) { int needmore = width-digits;
*strp++ = ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0'; if (needmore>0) output.append(needmore,' '); // Pre-pad spaces
output += str;
break;
} }
*strp++ = '\0'; case 'x':
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--) { for (; lsb>=0; lsb--) {
lsb = (lsb / 4) * 4; // Next digit lsb = (lsb / 4) * 4; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf; IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf;
*strp++ = (charval + ((charval < 10) ? '0':('a'-10))); 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;
} }
*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) { IData VL_FGETS_IXQ(int obits, void* destp, QData fpq) {
FILE* fp = VL_CVT_Q_FP(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 // 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 // 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)); 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, void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int,
QData ofilename, void* memp, IData start, IData end) { QData ofilename, void* memp, IData start, IData end) {
IData fnw[2]; VL_SET_WQ(fnw, ofilename); IData fnw[2]; VL_SET_WQ(fnw, ofilename);

View File

@ -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_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 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 /// File I/O
extern IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq); 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) { IData ofilename, void* memp, IData start, IData end) {
VL_READMEM_Q(hex, width,depth,array_lsb,fnwords, ofilename,memp,start,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 // 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_BITBIT_I(bit))) #define VL_BITISSET_I(data,bit) (data & (VL_UL(1)<<VL_BITBIT_I(bit)))
#define VL_BITISSET_Q(data,bit) (data & (VL_ULL(1)<<VL_BITBIT_Q(bit))) #define VL_BITISSET_Q(data,bit) (data & (VL_ULL(1)<<VL_BITBIT_Q(bit)))
#define VL_BITISSET_W(data,bit) (data[VL_BITWORD_I(bit)] & (VL_UL(1)<<VL_BITBIT_I(bit))) #define VL_BITISSET_W(data,bit) (data[VL_BITWORD_I(bit)] & (VL_UL(1)<<VL_BITBIT_I(bit)))
#define VL_BITISSETLIMIT_W(data,width,bit) (((bit)<(width)) && data[VL_BITWORD_I(bit)] & (VL_UL(1)<<VL_BITBIT_I(bit)))
/// Create two 32-bit words from quadword /// Create two 32-bit words from quadword
#define VL_SET_WQ(decl,data) { decl[0]=(data); decl[1]=((data)>>VL_WORDSIZE); } #define VL_SET_WQ(decl,data) { decl[0]=(data); decl[1]=((data)>>VL_WORDSIZE); }

View File

@ -79,9 +79,7 @@ public:
// METHODS // METHODS
void displayEmit(AstDisplay* nodep); void displayEmit(AstDisplay* nodep);
string displayFormat(AstNode* widthNode, string in, void displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter);
char fmtLetter, bool padZero, bool reallyString);
void displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter);
void emitVarDecl(AstVar* nodep, const string& prefixIfImp); void emitVarDecl(AstVar* nodep, const string& prefixIfImp);
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich; 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 // We only do one display at once, so can just use static state
struct EmitDispState { struct EmitDispState {
bool m_wide; // Put out a wide func that needs string buffer
string m_format; // "%s" and text from user string m_format; // "%s" and text from user
vector<AstNode*> m_argsp; // Each argument to be printed vector<AstNode*> m_argsp; // Each argument to be printed
vector<string> m_argsFunc; // Function before each argument to be printed vector<string> m_argsFunc; // Function before each argument to be printed
EmitDispState() { clear(); } EmitDispState() { clear(); }
void clear() { void clear() {
m_wide = false;
m_format = ""; m_format = "";
m_argsp.clear(); m_argsp.clear();
m_argsFunc.clear(); m_argsFunc.clear();
@ -958,13 +954,11 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) {
if (emitDispState.m_format != "") { if (emitDispState.m_format != "") {
// Format // Format
if (nodep->filep()) { if (nodep->filep()) {
puts("if ("); puts("VL_FWRITEF(");
nodep->filep()->iterate(*this); // Check if closed, to avoid core dump
puts(") fprintf(VL_CVT_Q_FP(");
nodep->filep()->iterate(*this); nodep->filep()->iterate(*this);
puts("),\""); puts(",\"");
} else { } else {
puts("VL_PRINTF(\""); puts("VL_WRITEF(\"");
} }
ofp()->putsNoTracking(emitDispState.m_format); ofp()->putsNoTracking(emitDispState.m_format);
puts("\""); puts("\"");
@ -977,7 +971,6 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) {
ofp()->putbs(""); ofp()->putbs("");
if (func!="") puts(func); if (func!="") puts(func);
if (argp) argp->iterate(*this); if (argp) argp->iterate(*this);
if (func!="") puts(")");
ofp()->indentDec(); ofp()->indentDec();
} }
// End // End
@ -987,80 +980,43 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) {
} }
} }
string EmitCStmts::displayFormat(AstNode* widthNodep, string in, void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter) {
char fmtLetter, bool padZero, bool reallyString) { // Print display argument, edits elistp
if (fmtLetter=='s') padZero = false; AstNode* argp = *elistp;
if (widthNodep && widthNodep->isWide() 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')) { && (fmtLetter=='d'||fmtLetter=='u')) {
widthNodep->v3error("Unsupported: $display of dec format of > 64 bit results (use hex format instead)"); argp->v3error("Unsupported: $display of dec format of > 64 bit results (use hex format instead)");
} }
if (widthNodep && widthNodep->widthMin()>8 && fmtLetter=='c') { if (argp && argp->widthMin()>8 && fmtLetter=='c') {
widthNodep->v3error("$display of char format of > 8 bit result"); // Technically legal, but surely not what the user intended.
argp->v3error("$display of char format of > 8 bit result");
} }
string fmt;
if (in == "") { //string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
// Size naturally string pfmt;
if (widthNodep == NULL) fmt=""; // Out of args, will get error if ((fmtLetter=='u' || fmtLetter=='d')
if (fmtLetter=='u' || fmtLetter=='d') { // Decimal. Spec says leading spaces, not zeros && vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros
double mantissabits = widthNodep->widthMin() - ((fmtLetter=='d')?1:0); double mantissabits = argp->widthMin() - ((fmtLetter=='d')?1:0);
double maxval = pow(2.0, mantissabits); double maxval = pow(2.0, mantissabits);
double dchars = log10(maxval)+1.0; double dchars = log10(maxval)+1.0;
if (fmtLetter=='d') dchars++; // space for sign if (fmtLetter=='d') dchars++; // space for sign
int nchars = int(dchars); int nchars = int(dchars);
fmt=cvtToStr(nchars); pfmt = string("%") + cvtToStr(nchars) + fmtLetter;
} 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 { } else {
fmt=in; pfmt = string("%") + vfmt + fmtLetter;
} }
return fmt;
}
void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter) {
// Print display argument, edits elistp
if (!*elistp) {
dispp->v3error("Missing arguments for $display format");
return;
}
if ((*elistp)->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
dispp->v3error("Exceeded limit of 1024 bits for any display arguments");
}
if ((*elistp)->isWide() // Have to use our own function for wide,
|| fmtLetter=='s' // ... Verilog strings
|| fmtLetter=='b' // ... binary, no printf %b in C
|| fmtLetter=='d') { // ... Signed decimal
// We use a single static string, so must only have one call per VL_PRINT
if (emitDispState.m_wide) { displayEmit(dispp); }
emitDispState.m_wide = true;
string nfmt = displayFormat(*elistp, fmt, fmtLetter, false, true);
string pfmt = "%"+nfmt+"s";
string func = "VL_VALUE_FORMATTED_";
func += ((*elistp)->isWide()) ? "W(" : ((*elistp)->isQuad()) ? "Q(" : "I(";
func += (cvtToStr((*elistp)->widthMin())
+",'"+fmtLetter+"'"
+","+(fmt=="0"?"true":"false")+",");
emitDispState.pushFormat(pfmt); emitDispState.pushFormat(pfmt);
emitDispState.pushArg(*elistp,func); emitDispState.pushArg(NULL,cvtToStr(argp->widthMin()));
} else { emitDispState.pushArg(argp,"");
string func;
string nfmt = displayFormat(*elistp, fmt, fmtLetter, true, false);
// We don't need to check for fmtLetter=='d', as it is above.
if ((*elistp)->isQuad() && (fmtLetter=='u'||fmtLetter=='o'||fmtLetter=='x')) {
nfmt+="ll";
func="(unsigned long long)("; // Must match %ull to avoid warnings
}
string pfmt = "%"+nfmt+fmtLetter;
emitDispState.pushFormat(pfmt);
emitDispState.pushArg(*elistp,func);
}
// Next parameter // Next parameter
*elistp = (*elistp)->nextp(); *elistp = (*elistp)->nextp();
} }
@ -1072,40 +1028,42 @@ void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) {
// Convert Verilog display to C printf formats // Convert Verilog display to C printf formats
// "%0t" becomes "%d" // "%0t" becomes "%d"
emitDispState.clear(); emitDispState.clear();
string fmt = ""; string vfmt = "";
string::iterator pos = vformat.begin(); string::iterator pos = vformat.begin();
bool inPct = false; bool inPct = false;
for (; pos != vformat.end(); ++pos) { for (; pos != vformat.end(); ++pos) {
if (inPct && pos[0]=='%') { //UINFO(1,"Parse '"<<*pos<<"' IP"<<inPct<<" List "<<(void*)(elistp)<<endl);
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % if (!inPct && pos[0]=='%') {
inPct = false;
} else if (pos[0]=='%') {
inPct = true; inPct = true;
fmt = ""; vfmt = "";
} else if (!inPct) { // Normal text } else if (!inPct) { // Normal text
//char text[2]; text[0]=*pos; text[1]='\0';
emitDispState.pushFormat(*pos); emitDispState.pushFormat(*pos);
} else { // Format character } else { // Format character
if (isdigit(pos[0])) {
// Digits, like %5d, etc.
fmt += pos[0];
} else {
inPct = false; inPct = false;
switch (tolower(pos[0])) { 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 // Special codes
case '~': displayArg(nodep,&elistp,fmt,'d'); break; // Signed decimal case '~': displayArg(nodep,&elistp,vfmt,'d'); break; // Signed decimal
// Spec: h d o b c l // Spec: h d o b c l
case 'b': displayArg(nodep,&elistp,fmt,'b'); break; case 'b': displayArg(nodep,&elistp,vfmt,'b'); break;
case 'c': displayArg(nodep,&elistp,fmt,'c'); break; case 'c': displayArg(nodep,&elistp,vfmt,'c'); break;
case 't': case 't':
case 'd': displayArg(nodep,&elistp,fmt,'u'); break; // Unsigned decimal case 'd': displayArg(nodep,&elistp,vfmt,'u'); break; // Unsigned decimal
case 'o': displayArg(nodep,&elistp,fmt,'o'); break; case 'o': displayArg(nodep,&elistp,vfmt,'o'); break;
case 'h': case 'h':
case 'x': displayArg(nodep,&elistp,fmt,'x'); break; case 'x': displayArg(nodep,&elistp,vfmt,'x'); break;
case 's': displayArg(nodep,&elistp,fmt,'s'); break; case 's': displayArg(nodep,&elistp,vfmt,'s'); break;
case 'm': { case 'm': {
emitDispState.pushFormat("%s"); emitDispState.pushFormat("%S");
emitDispState.pushArg(NULL, "vlSymsp->name("); emitDispState.pushArg(NULL, "vlSymsp->name()");
if (!nodep->scopeNamep()) nodep->v3fatalSrc("Display with %m but no AstScopeName"); if (!nodep->scopeNamep()) nodep->v3fatalSrc("Display with %m but no AstScopeName");
for (AstText* textp=nodep->scopeNamep()->scopeAttrp(); textp; textp=textp->nextp()->castText()) { for (AstText* textp=nodep->scopeNamep()->scopeAttrp(); textp; textp=textp->nextp()->castText()) {
emitDispState.pushFormat(textp->text()); emitDispState.pushFormat(textp->text());
@ -1124,9 +1082,9 @@ void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) {
} }
} }
} }
}
if (elistp != NULL) { if (elistp != NULL) {
nodep->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"); if (nodep->addNewline()) emitDispState.pushFormat("\\n");
displayEmit(nodep); displayEmit(nodep);

View File

@ -244,7 +244,8 @@ void V3File::createMakeDir() {
// V3OutFile: A class for printing to a file, with automatic indentation of C++ code. // V3OutFile: A class for printing to a file, with automatic indentation of C++ code.
V3OutFile::V3OutFile(const string& filename) V3OutFile::V3OutFile(const string& filename)
: m_lineno(1), m_column(0), m_nobreak(false), m_prependIndent(true), m_indentLevel(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) { , m_declSAlign(0), m_declNSAlign(0), m_declPadNum(0) {
if ((m_fp = V3File::new_fopen_w(filename.c_str())) == NULL) { if ((m_fp = V3File::new_fopen_w(filename.c_str())) == NULL) {
v3fatal("Cannot write "<<filename); v3fatal("Cannot write "<<filename);

View File

@ -87,6 +87,7 @@ public:
private: private:
// MEMBERS // MEMBERS
FILE* m_fp; FILE* m_fp;
string m_filename;
int m_lineno; int m_lineno;
int m_column; int m_column;
int m_nobreak; // Basic operator or begin paren, don't break next int m_nobreak; // Basic operator or begin paren, don't break next
@ -120,8 +121,9 @@ public:
bool tokenStart(const char* cp, const char* cmp); bool tokenStart(const char* cp, const char* cmp);
bool tokenEnd(const char* cp); bool tokenEnd(const char* cp);
void indentInc() { m_indentLevel += INDBLK; }; void indentInc() { m_indentLevel += INDBLK; };
void indentDec() { m_indentLevel -= INDBLK; void indentDec() {
UASSERT(m_indentLevel>=0,"Underflow of indentation\n"); m_indentLevel -= INDBLK;
UASSERT(m_indentLevel>=0, ": "<<m_filename<<": Underflow of indentation\n");
} }
void blockInc() { m_parenVec.push(m_indentLevel + INDBLK); } void blockInc() { m_parenVec.push(m_indentLevel + INDBLK); }
void blockDec() { if (!m_parenVec.empty()) m_parenVec.pop(); } void blockDec() { if (!m_parenVec.empty()) m_parenVec.pop(); }

View File

@ -312,10 +312,46 @@ private:
} }
} }
void expectFormat(AstNode* nodep, const string& format, AstNode* argp) {
// Check display arguments
bool inPct = false;
for (const char* inp = format.c_str(); *inp; inp++) {
char ch = *inp; // Breaks with iterators...
if (!inPct && ch=='%') {
inPct = true;
} else if (inPct) {
inPct = false;
switch (tolower(ch)) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
inPct = true;
break;
case '%': break; // %% - just output a %
case 'm': break; // %m - auto insert "name"
default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch)) {
nodep->v3error("Unknown $display format code: %"<<ch);
} else {
if (!argp) {
nodep->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) { void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) {
if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable"); if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable");
if (filep && filep->varp()) filep->varp()->attrFileDescr(true); if (filep && filep->varp()) filep->varp()->attrFileDescr(true);
} }
virtual void visit(AstFOpen* nodep, AstNUser*) { virtual void visit(AstFOpen* nodep, AstNUser*) {
nodep->iterateChildren(*this); nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
@ -343,6 +379,7 @@ private:
virtual void visit(AstDisplay* nodep, AstNUser*) { virtual void visit(AstDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this); nodep->iterateChildren(*this);
if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
expectFormat(nodep, nodep->name(), nodep->exprsp());
if (!m_assertp if (!m_assertp
&& (nodep->displayType() == AstDisplayType::INFO && (nodep->displayType() == AstDisplayType::INFO
|| nodep->displayType() == AstDisplayType::WARNING || nodep->displayType() == AstDisplayType::WARNING

View File

@ -345,6 +345,22 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
return out.str(); 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 V3Number::displayed(const string& vformat) const {
string::const_iterator pos = vformat.begin(); string::const_iterator pos = vformat.begin();
UASSERT(pos != vformat.end() && pos[0]=='%', "display with non format argument "<<*this); UASSERT(pos != vformat.end() && pos[0]=='%', "display with non format argument "<<*this);

View File

@ -119,6 +119,7 @@ public:
// ACCESSORS // ACCESSORS
string ascii(bool prefixed=true, bool cleanVerilog=false) const; string ascii(bool prefixed=true, bool cleanVerilog=false) const;
string displayed(const string& format) 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 width() const { return m_width; }
int minWidth() const; // Minimum width that can represent this number (~== log2(num)+1) int minWidth() const; // Minimum width that can represent this number (~== log2(num)+1)
bool sized() const { return m_sized; } bool sized() const { return m_sized; }