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.
|
*** 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]
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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); }
|
||||||
|
|
|
||||||
150
src/V3EmitC.cpp
150
src/V3EmitC.cpp
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(); }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue