Support scan %* format

This commit is contained in:
Wilson Snyder 2020-05-11 22:13:59 -04:00
parent 68ed683681
commit f005b7fd87
7 changed files with 101 additions and 67 deletions

View File

@ -993,12 +993,14 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
int floc = fbits - 1; int floc = fbits - 1;
IData got = 0; IData got = 0;
bool inPct = false; bool inPct = false;
bool inIgnore = false;
const char* pos = formatp; const char* pos = formatp;
for (; *pos && !_vl_vsss_eof(fp, floc); ++pos) { for (; *pos && !_vl_vsss_eof(fp, floc); ++pos) {
// VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp, floc, // VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp, floc,
// fromp, fstr)<<"'"<<endl); // fromp, fstr)<<"'"<<endl);
if (!inPct && pos[0] == '%') { if (!inPct && pos[0] == '%') {
inPct = true; inPct = true;
inIgnore = false;
} else if (!inPct && isspace(pos[0])) { // Format spaces } else if (!inPct && isspace(pos[0])) { // Format spaces
while (isspace(pos[1])) pos++; while (isspace(pos[1])) pos++;
_vl_vsss_skipspace(fp, floc, fromp, fstr); _vl_vsss_skipspace(fp, floc, fromp, fstr);
@ -1018,10 +1020,14 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_advance(fp, floc); _vl_vsss_advance(fp, floc);
break; break;
} }
case '*':
inPct = true;
inIgnore = true;
break;
default: { default: {
// Deal with all read-and-scan somethings // Deal with all read-and-scan somethings
// Note LSBs are preserved if there's an overflow // Note LSBs are preserved if there's an overflow
const int obits = va_arg(ap, int); const int obits = inIgnore ? 0 : va_arg(ap, int);
WData qowp[VL_WQ_WORDS_E]; WData qowp[VL_WQ_WORDS_E];
VL_SET_WQ(qowp, VL_ULL(0)); VL_SET_WQ(qowp, VL_ULL(0));
WDataOutP owp = qowp; WDataOutP owp = qowp;
@ -1108,9 +1114,10 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
break; break;
} // switch } // switch
got++; if (!inIgnore) ++got;
// Reload data if non-wide (if wide, we put it in the right place directly) // Reload data if non-wide (if wide, we put it in the right place directly)
if (obits <= VL_BYTESIZE) { if (obits == 0) { // Due to inIgnore
} else if (obits <= VL_BYTESIZE) {
CData* p = va_arg(ap, CData*); CData* p = va_arg(ap, CData*);
*p = owp[0]; *p = owp[0];
} else if (obits <= VL_SHORTSIZE) { } else if (obits <= VL_SHORTSIZE) {

View File

@ -73,7 +73,7 @@ public:
void displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat, void displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
AstNode* exprsp, bool isScan); AstNode* exprsp, bool isScan);
void displayEmit(AstNode* nodep, bool isScan); void displayEmit(AstNode* nodep, bool isScan);
void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, bool ignore,
char fmtLetter); char fmtLetter);
void emitVarDecl(const AstVar* nodep, const string& prefixIfImp); void emitVarDecl(const AstVar* nodep, const string& prefixIfImp);
@ -2042,27 +2042,30 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
ofp()->putsQuoted(emitDispState.m_format); ofp()->putsQuoted(emitDispState.m_format);
// Arguments // Arguments
for (unsigned i = 0; i < emitDispState.m_argsp.size(); i++) { for (unsigned i = 0; i < emitDispState.m_argsp.size(); i++) {
puts(",");
char fmt = emitDispState.m_argsChar[i]; char fmt = emitDispState.m_argsChar[i];
AstNode* argp = emitDispState.m_argsp[i]; AstNode* argp = emitDispState.m_argsp[i];
string func = emitDispState.m_argsFunc[i]; string func = emitDispState.m_argsFunc[i];
ofp()->indentInc(); if (func != "" || argp) {
ofp()->putbs(""); puts(",");
if (func != "") puts(func); ofp()->indentInc();
if (argp) { ofp()->putbs("");
if (isScan) { if (func != "") {
puts("&("); puts(func);
} else if (fmt == '@') { } else if (argp) {
puts("&("); if (isScan) {
} puts("&(");
iterate(argp); } else if (fmt == '@') {
if (isScan) { puts("&(");
puts(")"); }
} else if (fmt == '@') { iterate(argp);
puts(")"); if (isScan) {
puts(")");
} else if (fmt == '@') {
puts(")");
}
} }
ofp()->indentDec();
} }
ofp()->indentDec();
} }
// End // End
puts(")"); puts(")");
@ -2076,28 +2079,33 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
} }
void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt,
char fmtLetter) { bool ignore, char fmtLetter) {
// Print display argument, edits elistp // Print display argument, edits elistp
AstNode* argp = *elistp; AstNode* argp = NULL;
if (VL_UNCOVERABLE(!argp)) { if (!ignore) {
// expectDisplay() checks this first, so internal error if found here argp = *elistp;
dispp->v3error("Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE // Prep for next parameter
return; // LCOV_EXCL_LINE *elistp = (*elistp)->nextp();
if (VL_UNCOVERABLE(!argp)) {
// expectDisplay() checks this first, so internal error if found here
dispp->v3error(
"Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE
return; // LCOV_EXCL_LINE
}
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
dispp->v3error("Exceeded limit of " + cvtToStr(VL_VALUE_STRING_MAX_WIDTH)
+ " bits for any $display-like arguments");
}
if (argp->widthMin() > 8 && fmtLetter == 'c') {
// Technically legal, but surely not what the user intended.
argp->v3warn(WIDTH, dispp->verilogKwd() << "of %c format of > 8 bit value");
}
} }
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
dispp->v3error("Exceeded limit of " + cvtToStr(VL_VALUE_STRING_MAX_WIDTH)
+ " bits for any $display-like arguments");
}
if (argp->widthMin() > 8 && fmtLetter == 'c') {
// Technically legal, but surely not what the user intended.
argp->v3warn(WIDTH, dispp->verilogKwd() << "of %c format of > 8 bit value");
}
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter; // string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
string pfmt; string pfmt;
if ((fmtLetter == '#' || fmtLetter == 'd') && !isScan if ((fmtLetter == '#' || fmtLetter == 'd') && !isScan
&& vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros && vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros
const double mantissabits = argp->widthMin() - ((fmtLetter == 'd') ? 1 : 0); const double mantissabits = ignore ? 0 : (argp->widthMin() - ((fmtLetter == 'd') ? 1 : 0));
// This is log10(2**mantissabits) as log2(2**mantissabits)/log2(10), // This is log10(2**mantissabits) as log2(2**mantissabits)/log2(10),
// + 1.0 rounding bias. // + 1.0 rounding bias.
double dchars = mantissabits / 3.321928094887362 + 1.0; double dchars = mantissabits / 3.321928094887362 + 1.0;
@ -2108,11 +2116,12 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
pfmt = string("%") + vfmt + fmtLetter; pfmt = string("%") + vfmt + fmtLetter;
} }
emitDispState.pushFormat(pfmt); emitDispState.pushFormat(pfmt);
emitDispState.pushArg(' ', NULL, cvtToStr(argp->widthMin())); if (!ignore) {
emitDispState.pushArg(fmtLetter, argp, ""); emitDispState.pushArg(' ', NULL, cvtToStr(argp->widthMin()));
emitDispState.pushArg(fmtLetter, argp, "");
// Next parameter } else {
*elistp = (*elistp)->nextp(); emitDispState.pushArg(fmtLetter, NULL, "");
}
} }
void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat, void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
@ -2125,10 +2134,12 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, const str
string vfmt; string vfmt;
string::const_iterator pos = vformat.begin(); string::const_iterator pos = vformat.begin();
bool inPct = false; bool inPct = false;
bool ignore = false;
for (; pos != vformat.end(); ++pos) { for (; pos != vformat.end(); ++pos) {
// UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp) << endl); // UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp) << endl);
if (!inPct && pos[0] == '%') { if (!inPct && pos[0] == '%') {
inPct = true; inPct = true;
ignore = false;
vfmt = ""; vfmt = "";
} else if (!inPct) { // Normal text } else if (!inPct) { // Normal text
emitDispState.pushFormat(*pos); emitDispState.pushFormat(*pos);
@ -2145,25 +2156,34 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, const str
case '%': case '%':
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
break; break;
case '*':
vfmt += pos[0];
inPct = true; // Get more digits
ignore = true;
break;
// Special codes // Special codes
case '~': displayArg(nodep, &elistp, isScan, vfmt, 'd'); break; // Signed decimal case '~':
displayArg(nodep, &elistp, isScan, vfmt, ignore, 'd');
break; // Signed decimal
case '@': case '@':
displayArg(nodep, &elistp, isScan, vfmt, '@'); displayArg(nodep, &elistp, isScan, vfmt, ignore, '@');
break; // Packed string break; // Packed string
// Spec: h d o b c l // Spec: h d o b c l
case 'b': displayArg(nodep, &elistp, isScan, vfmt, 'b'); break; case 'b': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'b'); break;
case 'c': displayArg(nodep, &elistp, isScan, vfmt, 'c'); break; case 'c': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'c'); break;
case 't': displayArg(nodep, &elistp, isScan, vfmt, 't'); break; case 't': displayArg(nodep, &elistp, isScan, vfmt, ignore, 't'); break;
case 'd': displayArg(nodep, &elistp, isScan, vfmt, '#'); break; // Unsigned decimal case 'd':
case 'o': displayArg(nodep, &elistp, isScan, vfmt, 'o'); break; displayArg(nodep, &elistp, isScan, vfmt, ignore, '#');
break; // Unsigned decimal
case 'o': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'o'); break;
case 'h': // FALLTHRU case 'h': // FALLTHRU
case 'x': displayArg(nodep, &elistp, isScan, vfmt, 'x'); break; case 'x': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'x'); break;
case 's': displayArg(nodep, &elistp, isScan, vfmt, 's'); break; case 's': displayArg(nodep, &elistp, isScan, vfmt, ignore, 's'); break;
case 'e': displayArg(nodep, &elistp, isScan, vfmt, 'e'); break; case 'e': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'e'); break;
case 'f': displayArg(nodep, &elistp, isScan, vfmt, 'f'); break; case 'f': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'f'); break;
case 'g': displayArg(nodep, &elistp, isScan, vfmt, 'g'); break; case 'g': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'g'); break;
case '^': displayArg(nodep, &elistp, isScan, vfmt, '^'); break; // Realtime case '^': displayArg(nodep, &elistp, isScan, vfmt, ignore, '^'); break; // Realtime
case 'v': displayArg(nodep, &elistp, isScan, vfmt, 'v'); break; case 'v': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'v'); break;
case 'm': { case 'm': {
UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName"); UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName");
string suffix = scopenamep->scopePrettySymName(); string suffix = scopenamep->scopePrettySymName();
@ -2312,8 +2332,8 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
+ ");\n"); + ");\n");
} }
if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) { if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) {
puts("Verilated::timeprecision(" puts("Verilated::timeprecision(" + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen())
+ cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) + ");\n"); + ");\n");
} }
puts("}\n"); puts("}\n");
splitSizeInc(10); splitSizeInc(10);

View File

@ -289,11 +289,13 @@ private:
// Check display arguments, return new format string // Check display arguments, return new format string
string newFormat; string newFormat;
bool inPct = false; bool inPct = false;
bool inIgnore = false;
string fmt; string fmt;
for (string::const_iterator it = format.begin(); it != format.end(); ++it) { for (string::const_iterator it = format.begin(); it != format.end(); ++it) {
char ch = *it; char ch = *it;
if (!inPct && ch == '%') { if (!inPct && ch == '%') {
inPct = true; inPct = true;
inIgnore = false;
fmt = ch; fmt = ch;
} else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) { } else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
fmt += ch; fmt += ch;
@ -303,6 +305,10 @@ private:
switch (tolower(ch)) { switch (tolower(ch)) {
case '%': // %% - just output a % case '%': // %% - just output a %
break; break;
case '*':
inPct = true;
inIgnore = true;
break;
case 'm': // %m - auto insert "name" case 'm': // %m - auto insert "name"
if (isScan) { if (isScan) {
nodep->v3error("Unsupported: %m in $fscanf"); nodep->v3error("Unsupported: %m in $fscanf");
@ -317,9 +323,9 @@ private:
if (m_modp) fmt = VString::quotePercent(m_modp->prettyName()); if (m_modp) fmt = VString::quotePercent(m_modp->prettyName());
break; break;
default: // Most operators, just move to next argument default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch)) { if (!V3Number::displayedFmtLegal(ch, isScan)) {
nodep->v3error("Unknown $display-like format code: '%" << ch << "'"); nodep->v3error("Unknown $display-like format code: '%" << ch << "'");
} else { } else if (!inIgnore) {
if (!argp) { if (!argp) {
nodep->v3error("Missing arguments for $display-like format"); nodep->v3error("Missing arguments for $display-like format");
} else { } else {
@ -362,7 +368,7 @@ private:
case '.': inpercent = true; break; case '.': inpercent = true; break;
case '%': break; case '%': break;
default: default:
if (V3Number::displayedFmtLegal(c)) { skipCount++; } if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount;
} }
} }
} }

View File

@ -524,7 +524,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
return out.str(); return out.str();
} }
bool V3Number::displayedFmtLegal(char format) { bool V3Number::displayedFmtLegal(char format, bool isScan) {
// Is this a valid format letter? // Is this a valid format letter?
switch (tolower(format)) { switch (tolower(format)) {
case 'b': return true; case 'b': return true;
@ -544,6 +544,7 @@ bool V3Number::displayedFmtLegal(char format) {
case 'z': return true; // Packed 4-state case 'z': return true; // Packed 4-state
case '@': return true; // Packed string case '@': return true; // Packed string
case '~': return true; // Signed decimal case '~': return true; // Signed decimal
case '*': return isScan; // $scan ignore argument
default: return false; default: return false;
} }
} }

View File

@ -231,7 +231,7 @@ public:
// ACCESSORS // ACCESSORS
string ascii(bool prefixed = true, bool cleanVerilog = false) const; string ascii(bool prefixed = true, bool cleanVerilog = false) const;
string displayed(AstNode* nodep, const string& vformat) const; string displayed(AstNode* nodep, const string& vformat) const;
static bool displayedFmtLegal(char format); // Is this a valid format letter? static bool displayedFmtLegal(char format, bool isScan); // Is this a valid format letter?
int width() const { return m_width; } int width() const { return m_width; }
int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1) int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1)
bool sized() const { return m_sized; } bool sized() const { return m_sized; }

View File

@ -1019,7 +1019,7 @@ private:
} else { // Format character } else { // Format character
inPct = false; inPct = false;
if (V3Number::displayedFmtLegal(tolower(pos[0]))) { if (V3Number::displayedFmtLegal(tolower(pos[0]), false)) {
AstNode* argp = nextArgp; AstNode* argp = nextArgp;
nextArgp = nextArgp->nextp(); nextArgp = nextArgp->nextp();
AstConst* constp = fetchConstNull(argp); AstConst* constp = fetchConstNull(argp);

View File

@ -139,8 +139,8 @@ module t;
if (chars != 1) $stop; if (chars != 1) $stop;
if (letterq != "ijklmnop") $stop; if (letterq != "ijklmnop") $stop;
chars = $sscanf("xa=1f xb=12898971238912389712783490823_abcdef689_02348923", chars = $sscanf("xa=1f ign=22 xb=12898971238912389712783490823_abcdef689_02348923",
"xa=%x xb=%x", letterq, letterw); "xa=%x ign=%*d xb=%x", letterq, letterw);
if (`verbose) $write("c=%0d xa=%x xb=%x\n", chars, letterq, letterw); if (`verbose) $write("c=%0d xa=%x xb=%x\n", chars, letterq, letterw);
if (chars != 2) $stop; if (chars != 2) $stop;
if (letterq != 64'h1f) $stop; if (letterq != 64'h1f) $stop;
@ -154,8 +154,8 @@ module t;
if (letterw != 128'hd2a55) $stop; if (letterw != 128'hd2a55) $stop;
if (letterz != {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0","2"}) $stop; if (letterz != {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0","2"}) $stop;
chars = $sscanf("oa=23 ob=125634123615234123681236", chars = $sscanf("oa=23 oi=11 ob=125634123615234123681236",
"oa=%o ob=%o", letterq, letterw); "oa=%o oi=%*o ob=%o", letterq, letterw);
if (`verbose) $write("c=%0d oa=%x ob=%x\n", chars, letterq, letterw); if (`verbose) $write("c=%0d oa=%x ob=%x\n", chars, letterq, letterw);
if (chars != 2) $stop; if (chars != 2) $stop;
if (letterq != 64'h13) $stop; if (letterq != 64'h13) $stop;