diff --git a/include/verilated.cpp b/include/verilated.cpp index 00600f7ca..6d9d7b6e4 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1079,7 +1079,17 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf WData qowp[VL_WQ_WORDS_E]; VL_SET_WQ(qowp, 0ULL); WDataOutP owp = qowp; - if (obits > VL_QUADSIZE) owp = va_arg(ap, WDataOutP); + if (obits == -1) { // string + owp = nullptr; + if (VL_UNCOVERABLE(fmt != 's')) { + VL_FATAL_MT( + __FILE__, __LINE__, "", + "Internal: format other than %s is passed to string"); // LCOV_EXCL_LINE + } + } else if (obits > VL_QUADSIZE) { + owp = va_arg(ap, WDataOutP); + } + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; switch (fmt) { case 'c': { @@ -1093,11 +1103,13 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf _vl_vsss_skipspace(fp, floc, fromp, fstr); _vl_vsss_read_str(fp, floc, fromp, fstr, tmp, nullptr); if (!tmp[0]) goto done; - int lpos = (static_cast(strlen(tmp))) - 1; - int lsb = 0; - for (int i = 0; i < obits && lpos >= 0; --lpos) { - _vl_vsss_setbit(owp, obits, lsb, 8, tmp[lpos]); - lsb += 8; + if (owp) { + int lpos = (static_cast(strlen(tmp))) - 1; + int lsb = 0; + for (int i = 0; i < obits && lpos >= 0; --lpos) { + _vl_vsss_setbit(owp, obits, lsb, 8, tmp[lpos]); + lsb += 8; + } } break; } @@ -1194,6 +1206,9 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf if (!inIgnore) ++got; // Reload data if non-wide (if wide, we put it in the right place directly) if (obits == 0) { // Due to inIgnore + } else if (obits == -1) { // string + std::string* p = va_arg(ap, std::string*); + *p = tmp; } else if (obits <= VL_BYTESIZE) { CData* p = va_arg(ap, CData*); *p = owp[0]; @@ -1252,36 +1267,44 @@ void _VL_STRING_TO_VINT(int obits, void* destp, size_t srclen, const char* srcp) for (; i < bytes; ++i) { *op++ = 0; } } -IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { +static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE { + str.clear(); + // While threadsafe, each thread can only access different file handles FILE* fp = VL_CVT_I_FP(fpi); 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 - // character will finally live, so we need to copy. Yuk. - IData bytes = VL_BYTES_I(obits); - char buffer[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; + // We don't use fgets, as we must read \0s. + while (str.size() < maxLen) { + const int c = getc(fp); // getc() is threadsafe + if (c == EOF) break; + str.push_back(c); + if (c == '\n') break; + } + return str.size(); +} + +IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { + std::string str; + const IData bytes = VL_BYTES_I(obits); + IData got = getLine(str, fpi, bytes); + + if (VL_UNLIKELY(str.empty())) return 0; + // V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe - if (VL_UNCOVERABLE(bytes > VL_TO_STRING_MAX_WORDS * VL_EDATASIZE)) { + if (VL_UNCOVERABLE(bytes < str.size())) { VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE } - // We don't use fgets, as we must read \0s. - IData got = 0; - char* cp = buffer; - while (got < bytes) { - int c = getc(fp); // getc() is threadsafe - if (c == EOF) break; - *cp++ = c; - got++; - if (c == '\n') break; - } - - _VL_STRING_TO_VINT(obits, destp, got, buffer); + _VL_STRING_TO_VINT(obits, destp, got, str.data()); return got; } +// declared in verilated_heavy.h +IData VL_FGETS_NI(std::string& str, IData fpi) VL_MT_SAFE { + return getLine(str, fpi, std::numeric_limits::max()); +} + IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { // We ignore lhs/fpi - IEEE says "most recent error" so probably good enough IData ret = errno; diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 2d110567a..3d4795de0 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -546,6 +546,8 @@ inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool igno extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; +extern IData VL_FGETS_NI(std::string& destp, IData fpi); + //====================================================================== // Dumping diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e2e5d48dc..3630f88ed 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -7959,7 +7959,10 @@ public: V3ERROR_NA; } virtual string emitVerilog() override { return "%f$fgets(%l,%r)"; } - virtual string emitC() override { return "VL_FGETS_%nqX%rq(%lw, %P, &(%li), %ri)"; } + virtual string emitC() override { + return strgp()->dtypep()->basicp()->isString() ? "VL_FGETS_NI(%li, %ri)" + : "VL_FGETS_%nqX%rq(%lw, %P, &(%li), %ri)"; + } virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool cleanRhs() const override { return true; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index b723ec475..c07416528 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -2199,7 +2199,12 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const } emitDispState.pushFormat(pfmt); if (!ignore) { - emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin())); + if (argp->dtypep()->basicp()->keyword() == AstBasicDTypeKwd::STRING) { + // string in SystemVerilog is std::string in C++ which is not POD + emitDispState.pushArg(' ', nullptr, "-1"); + } else { + emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin())); + } emitDispState.pushArg(fmtLetter, argp, ""); } else { emitDispState.pushArg(fmtLetter, nullptr, ""); diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index e264f676a..d38c8580a 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -134,6 +134,12 @@ module t; if (chars != 10) $stop; if (letterw != "\0\0\0\0\0\0widestuff\n") $stop; + s = ""; + chars = $fgets(s, file); + if (`verbose) $write("c=%0d w=%s", chars, s); // Output includes newline + if (chars != 7) $stop; + if (s != "string\n") $stop; + // $sscanf if ($sscanf("x","")!=0) $stop; if ($sscanf("z","z")!=0) $stop; @@ -173,6 +179,12 @@ module t; `checkr(r, 0.1); if (letterq != 64'hfffffffffffc65a5) $stop; + chars = $sscanf("scan from string", + "scan %s string", s); + if (`verbose) $write("c=%0d s=%s\n", chars, s); + if (chars != 1) $stop; + if (s != "from") $stop; + // Cover quad and %e/%f chars = $sscanf("r=0.2", "r=%e", r); @@ -245,6 +257,11 @@ module t; if (chars != 1) $stop; if (letterl != "\n") $stop; + chars = $fscanf(file, "%c%s not_included\n", letterl, s); + if (`verbose) $write("c=%0d l=%s\n", chars, s); + if (chars != 2) $stop; + if (s != "BCD") $stop; + // msg1229 v_a = $fgetc(file); v_b = $fgetc(file); diff --git a/test_regress/t/t_sys_file_basic_input.dat b/test_regress/t/t_sys_file_basic_input.dat index 0e640b4ed..53d20630c 100644 --- a/test_regress/t/t_sys_file_basic_input.dat +++ b/test_regress/t/t_sys_file_basic_input.dat @@ -1,10 +1,12 @@ hi lquad widestuff +string *xa=1f xb=237904689_02348923 *ba=10 bb=11010010101001010101 note_the_two *oa=23 ob=12563 *d=-236123 *u=-236124 *fredfishblah +aBCD not_included 12346789