From f005b7fd8791a3ef1b874c7b57cba04d26a837a3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 11 May 2020 22:13:59 -0400 Subject: [PATCH] Support scan %* format --- include/verilated.cpp | 13 ++- src/V3EmitC.cpp | 128 +++++++++++++++++------------- src/V3LinkResolve.cpp | 12 ++- src/V3Number.cpp | 3 +- src/V3Number.h | 2 +- src/V3Simulate.h | 2 +- test_regress/t/t_sys_file_basic.v | 8 +- 7 files changed, 101 insertions(+), 67 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index f8f034fb1..cc9c26e24 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -993,12 +993,14 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf int floc = fbits - 1; IData got = 0; bool inPct = false; + bool inIgnore = false; const char* pos = formatp; for (; *pos && !_vl_vsss_eof(fp, floc); ++pos) { // VL_DBG_MSGF("_vlscan fmt='"<putsQuoted(emitDispState.m_format); // Arguments for (unsigned i = 0; i < emitDispState.m_argsp.size(); i++) { - puts(","); char fmt = emitDispState.m_argsChar[i]; AstNode* argp = emitDispState.m_argsp[i]; string func = emitDispState.m_argsFunc[i]; - ofp()->indentInc(); - ofp()->putbs(""); - if (func != "") puts(func); - if (argp) { - if (isScan) { - puts("&("); - } else if (fmt == '@') { - puts("&("); - } - iterate(argp); - if (isScan) { - puts(")"); - } else if (fmt == '@') { - puts(")"); + if (func != "" || argp) { + puts(","); + ofp()->indentInc(); + ofp()->putbs(""); + if (func != "") { + puts(func); + } else if (argp) { + if (isScan) { + puts("&("); + } else if (fmt == '@') { + puts("&("); + } + iterate(argp); + if (isScan) { + puts(")"); + } else if (fmt == '@') { + puts(")"); + } } + ofp()->indentDec(); } - ofp()->indentDec(); } // End puts(")"); @@ -2076,28 +2079,33 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) { } void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, - char fmtLetter) { + bool ignore, char fmtLetter) { // Print display argument, edits elistp - AstNode* argp = *elistp; - 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 + AstNode* argp = NULL; + if (!ignore) { + argp = *elistp; + // Prep for next parameter + *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; if ((fmtLetter == '#' || fmtLetter == 'd') && !isScan && 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), // + 1.0 rounding bias. 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; } emitDispState.pushFormat(pfmt); - emitDispState.pushArg(' ', NULL, cvtToStr(argp->widthMin())); - emitDispState.pushArg(fmtLetter, argp, ""); - - // Next parameter - *elistp = (*elistp)->nextp(); + if (!ignore) { + emitDispState.pushArg(' ', NULL, cvtToStr(argp->widthMin())); + emitDispState.pushArg(fmtLetter, argp, ""); + } else { + emitDispState.pushArg(fmtLetter, NULL, ""); + } } 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::const_iterator pos = vformat.begin(); bool inPct = false; + bool ignore = false; for (; pos != vformat.end(); ++pos) { // UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp) << endl); if (!inPct && pos[0] == '%') { inPct = true; + ignore = false; vfmt = ""; } else if (!inPct) { // Normal text emitDispState.pushFormat(*pos); @@ -2145,25 +2156,34 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, const str case '%': emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % break; + case '*': + vfmt += pos[0]; + inPct = true; // Get more digits + ignore = true; + break; // Special codes - case '~': displayArg(nodep, &elistp, isScan, vfmt, 'd'); break; // Signed decimal + case '~': + displayArg(nodep, &elistp, isScan, vfmt, ignore, 'd'); + break; // Signed decimal case '@': - displayArg(nodep, &elistp, isScan, vfmt, '@'); + displayArg(nodep, &elistp, isScan, vfmt, ignore, '@'); break; // Packed string // Spec: h d o b c l - case 'b': displayArg(nodep, &elistp, isScan, vfmt, 'b'); break; - case 'c': displayArg(nodep, &elistp, isScan, vfmt, 'c'); break; - case 't': displayArg(nodep, &elistp, isScan, vfmt, 't'); break; - case 'd': displayArg(nodep, &elistp, isScan, vfmt, '#'); break; // Unsigned decimal - case 'o': displayArg(nodep, &elistp, isScan, vfmt, 'o'); break; + case 'b': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'b'); break; + case 'c': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'c'); break; + case 't': displayArg(nodep, &elistp, isScan, vfmt, ignore, 't'); break; + case 'd': + displayArg(nodep, &elistp, isScan, vfmt, ignore, '#'); + break; // Unsigned decimal + case 'o': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'o'); break; case 'h': // FALLTHRU - case 'x': displayArg(nodep, &elistp, isScan, vfmt, 'x'); break; - case 's': displayArg(nodep, &elistp, isScan, vfmt, 's'); break; - case 'e': displayArg(nodep, &elistp, isScan, vfmt, 'e'); break; - case 'f': displayArg(nodep, &elistp, isScan, vfmt, 'f'); break; - case 'g': displayArg(nodep, &elistp, isScan, vfmt, 'g'); break; - case '^': displayArg(nodep, &elistp, isScan, vfmt, '^'); break; // Realtime - case 'v': displayArg(nodep, &elistp, isScan, vfmt, 'v'); break; + case 'x': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'x'); break; + case 's': displayArg(nodep, &elistp, isScan, vfmt, ignore, 's'); break; + case 'e': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'e'); break; + case 'f': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'f'); break; + case 'g': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'g'); break; + case '^': displayArg(nodep, &elistp, isScan, vfmt, ignore, '^'); break; // Realtime + case 'v': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'v'); break; case 'm': { UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName"); string suffix = scopenamep->scopePrettySymName(); @@ -2312,8 +2332,8 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) { + ");\n"); } if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) { - puts("Verilated::timeprecision(" - + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) + ");\n"); + puts("Verilated::timeprecision(" + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) + + ");\n"); } puts("}\n"); splitSizeInc(10); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 90752c815..1a576e857 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -289,11 +289,13 @@ private: // Check display arguments, return new format string string newFormat; bool inPct = false; + bool inIgnore = false; string fmt; for (string::const_iterator it = format.begin(); it != format.end(); ++it) { char ch = *it; if (!inPct && ch == '%') { inPct = true; + inIgnore = false; fmt = ch; } else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) { fmt += ch; @@ -303,6 +305,10 @@ private: switch (tolower(ch)) { case '%': // %% - just output a % break; + case '*': + inPct = true; + inIgnore = true; + break; case 'm': // %m - auto insert "name" if (isScan) { nodep->v3error("Unsupported: %m in $fscanf"); @@ -317,9 +323,9 @@ private: if (m_modp) fmt = VString::quotePercent(m_modp->prettyName()); break; 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 << "'"); - } else { + } else if (!inIgnore) { if (!argp) { nodep->v3error("Missing arguments for $display-like format"); } else { @@ -362,7 +368,7 @@ private: case '.': inpercent = true; break; case '%': break; default: - if (V3Number::displayedFmtLegal(c)) { skipCount++; } + if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount; } } } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 3e139da99..d149a510f 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -524,7 +524,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const { return out.str(); } -bool V3Number::displayedFmtLegal(char format) { +bool V3Number::displayedFmtLegal(char format, bool isScan) { // Is this a valid format letter? switch (tolower(format)) { case 'b': return true; @@ -544,6 +544,7 @@ bool V3Number::displayedFmtLegal(char format) { case 'z': return true; // Packed 4-state case '@': return true; // Packed string case '~': return true; // Signed decimal + case '*': return isScan; // $scan ignore argument default: return false; } } diff --git a/src/V3Number.h b/src/V3Number.h index fb2fc6cd5..c25509a49 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -231,7 +231,7 @@ public: // ACCESSORS string ascii(bool prefixed = true, bool cleanVerilog = false) 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 widthMin() const; // Minimum width that can represent this number (~== log2(num)+1) bool sized() const { return m_sized; } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index c229d09f1..d8120b0a3 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -1019,7 +1019,7 @@ private: } else { // Format character inPct = false; - if (V3Number::displayedFmtLegal(tolower(pos[0]))) { + if (V3Number::displayedFmtLegal(tolower(pos[0]), false)) { AstNode* argp = nextArgp; nextArgp = nextArgp->nextp(); AstConst* constp = fetchConstNull(argp); diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index fe075a6be..272574d0d 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -139,8 +139,8 @@ module t; if (chars != 1) $stop; if (letterq != "ijklmnop") $stop; - chars = $sscanf("xa=1f xb=12898971238912389712783490823_abcdef689_02348923", - "xa=%x xb=%x", letterq, letterw); + chars = $sscanf("xa=1f ign=22 xb=12898971238912389712783490823_abcdef689_02348923", + "xa=%x ign=%*d xb=%x", letterq, letterw); if (`verbose) $write("c=%0d xa=%x xb=%x\n", chars, letterq, letterw); if (chars != 2) $stop; if (letterq != 64'h1f) $stop; @@ -154,8 +154,8 @@ module t; 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; - chars = $sscanf("oa=23 ob=125634123615234123681236", - "oa=%o ob=%o", letterq, letterw); + chars = $sscanf("oa=23 oi=11 ob=125634123615234123681236", + "oa=%o oi=%*o ob=%o", letterq, letterw); if (`verbose) $write("c=%0d oa=%x ob=%x\n", chars, letterq, letterw); if (chars != 2) $stop; if (letterq != 64'h13) $stop;