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:
parent
20aa21d4b6
commit
1a8c8bec0d
2
Changes
2
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]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_BITBIT_I(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_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
|
||||
#define VL_SET_WQ(decl,data) { decl[0]=(data); decl[1]=((data)>>VL_WORDSIZE); }
|
||||
|
|
|
|||
200
src/V3EmitC.cpp
200
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<AstNode*> m_argsp; // Each argument to be printed
|
||||
vector<string> 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"<<inPct<<" List "<<(void*)(elistp)<<endl);
|
||||
if (!inPct && pos[0]=='%') {
|
||||
inPct = true;
|
||||
fmt = "";
|
||||
vfmt = "";
|
||||
} else if (!inPct) { // Normal text
|
||||
//char text[2]; text[0]=*pos; text[1]='\0';
|
||||
emitDispState.pushFormat(*pos);
|
||||
} else { // Format character
|
||||
if (isdigit(pos[0])) {
|
||||
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.
|
||||
fmt += pos[0];
|
||||
} else {
|
||||
inPct = false;
|
||||
switch (tolower(pos[0])) {
|
||||
// Special codes
|
||||
case '~': displayArg(nodep,&elistp,fmt,'d'); break; // Signed decimal
|
||||
// Spec: h d o b c l
|
||||
case 'b': displayArg(nodep,&elistp,fmt,'b'); break;
|
||||
case 'c': displayArg(nodep,&elistp,fmt,'c'); break;
|
||||
case 't':
|
||||
case 'd': displayArg(nodep,&elistp,fmt,'u'); break; // Unsigned decimal
|
||||
case 'o': displayArg(nodep,&elistp,fmt,'o'); break;
|
||||
case 'h':
|
||||
case 'x': displayArg(nodep,&elistp,fmt,'x'); break;
|
||||
case 's': displayArg(nodep,&elistp,fmt,'s'); break;
|
||||
case 'm': {
|
||||
emitDispState.pushFormat("%s");
|
||||
emitDispState.pushArg(NULL, "vlSymsp->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: %"<<pos[0]);
|
||||
break;
|
||||
default:
|
||||
nodep->v3error("Unknown $display format code: %"<<pos[0]);
|
||||
break;
|
||||
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,vfmt,'d'); break; // Signed decimal
|
||||
// Spec: h d o b c l
|
||||
case 'b': displayArg(nodep,&elistp,vfmt,'b'); break;
|
||||
case 'c': displayArg(nodep,&elistp,vfmt,'c'); break;
|
||||
case 't':
|
||||
case 'd': displayArg(nodep,&elistp,vfmt,'u'); break; // Unsigned decimal
|
||||
case 'o': displayArg(nodep,&elistp,vfmt,'o'); break;
|
||||
case 'h':
|
||||
case 'x': displayArg(nodep,&elistp,vfmt,'x'); break;
|
||||
case 's': displayArg(nodep,&elistp,vfmt,'s'); break;
|
||||
case 'm': {
|
||||
emitDispState.pushFormat("%S");
|
||||
emitDispState.pushArg(NULL, "vlSymsp->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: %"<<pos[0]);
|
||||
break;
|
||||
default:
|
||||
nodep->v3error("Unknown $display format code: %"<<pos[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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");
|
||||
displayEmit(nodep);
|
||||
|
|
|
|||
|
|
@ -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 "<<filename);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public:
|
|||
private:
|
||||
// MEMBERS
|
||||
FILE* m_fp;
|
||||
string m_filename;
|
||||
int m_lineno;
|
||||
int m_column;
|
||||
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 tokenEnd(const char* cp);
|
||||
void indentInc() { m_indentLevel += INDBLK; };
|
||||
void indentDec() { m_indentLevel -= INDBLK;
|
||||
UASSERT(m_indentLevel>=0,"Underflow of indentation\n");
|
||||
void indentDec() {
|
||||
m_indentLevel -= INDBLK;
|
||||
UASSERT(m_indentLevel>=0, ": "<<m_filename<<": Underflow of indentation\n");
|
||||
}
|
||||
void blockInc() { m_parenVec.push(m_indentLevel + INDBLK); }
|
||||
void blockDec() { if (!m_parenVec.empty()) m_parenVec.pop(); }
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue