diff --git a/include/verilated.cpp b/include/verilated.cpp index 8e1956a38..c44e17253 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1411,6 +1411,12 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { outputr = std::string{::std::strerror(ret)}; return ret; } +IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE { + std::string output; + const IData ret = VL_FERROR_IN(fpi, output /*ref*/); + _vl_string_to_vint(obits, outwp, output.length(), output.c_str()); + return ret; +} IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) { return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str()); diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 013193354..665fce9c8 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2212,6 +2212,7 @@ extern std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE; extern std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE; extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; +extern IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE; extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ea7f04825..d306f0171 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4607,6 +4607,7 @@ public: bool cleanLhs() const override { return true; } bool sizeMattersLhs() const override { return false; } int instrCount() const override { return widthInstrs() * 16; } + bool isPredictOptimizable() const override { return false; } bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } }; @@ -4623,6 +4624,7 @@ public: bool cleanLhs() const override { return true; } bool sizeMattersLhs() const override { return false; } int instrCount() const override { return widthInstrs() * 64; } + bool isPredictOptimizable() const override { return false; } bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } }; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index c6d4bad54..c91112ef0 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -624,9 +624,14 @@ public: puts(")"); } void visit(AstFError* nodep) override { - puts("VL_FERROR_IN("); + puts("VL_FERROR_I"); + puts(nodep->strp()->isString() ? "N(" : "W("); iterateAndNextNull(nodep->filep()); putbs(", "); + if (nodep->strp()->isWide()) { + puts(cvtToStr(nodep->strp()->widthWords())); + putbs(", "); + } iterateAndNextNull(nodep->strp()); puts(")"); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 89f0721a7..9bb5ab610 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4777,8 +4777,8 @@ private: void visit(AstFError* nodep) override { if (m_vup->prelim()) { iterateCheckFileDesc(nodep, nodep->filep(), BOTH); - // We only support string types, not packed array - iterateCheckString(nodep, "$ferror string result", nodep->strp(), BOTH); + // Could be string or packed array + userIterateAndNext(nodep->strp(), WidthVP{SELF, BOTH}.p()); nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return } } diff --git a/src/verilog.y b/src/verilog.y index d54560c4a..f9ff19d42 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4037,10 +4037,12 @@ system_t_call: // IEEE: system_tf_call (as task) | yD_FMONITORB '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_MONITOR, $3, $5, 'b'}; } | yD_FMONITORH '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_MONITOR, $3, $5, 'h'}; } | yD_FMONITORO '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_MONITOR, $3, $5, 'o'}; } + | yD_FSTROBE '(' expr ')' { $$ = new AstDisplay{$1, VDisplayType::DT_STROBE, $3, nullptr}; } | yD_FSTROBE '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_STROBE, $3, $5}; } | yD_FSTROBEB '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_STROBE, $3, $5, 'b'}; } | yD_FSTROBEH '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_STROBE, $3, $5, 'h'}; } | yD_FSTROBEO '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_STROBE, $3, $5, 'o'}; } + | yD_FWRITE '(' expr ')' { $$ = new AstDisplay{$1, VDisplayType::DT_WRITE, $3, nullptr}; } | yD_FWRITE '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_WRITE, $3, $5}; } | yD_FWRITEB '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_WRITE, $3, $5, 'b'}; } | yD_FWRITEH '(' expr ',' exprDispList ')' { $$ = new AstDisplay{$1, VDisplayType::DT_WRITE, $3, $5, 'h'}; } @@ -4153,17 +4155,17 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_FELL '(' expr ',' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } | yD_FELL_GCLK '(' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_FEOF '(' expr ')' { $$ = new AstFEof{$1, $3}; } - | yD_FERROR '(' idClassSel ',' idClassSel ')' { $$ = new AstFError{$1, $3, $5}; } + | yD_FERROR '(' expr ',' idClassSel ')' { $$ = new AstFError{$1, $3, $5}; } | yD_FGETC '(' expr ')' { $$ = new AstFGetC{$1, $3}; } - | yD_FGETS '(' idClassSel ',' expr ')' { $$ = new AstFGetS{$1, $3, $5}; } - | yD_FREAD '(' idClassSel ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, nullptr}; } - | yD_FREAD '(' idClassSel ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, nullptr}; } - | yD_FREAD '(' idClassSel ',' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, $9}; } - | yD_FREWIND '(' idClassSel ')' { $$ = new AstFRewind{$1, $3}; } + | yD_FGETS '(' expr ',' expr ')' { $$ = new AstFGetS{$1, $3, $5}; } + | yD_FREAD '(' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, nullptr}; } + | yD_FREAD '(' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, nullptr}; } + | yD_FREAD '(' expr ',' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, $9}; } + | yD_FREWIND '(' expr ')' { $$ = new AstFRewind{$1, $3}; } | yD_FLOOR '(' expr ')' { $$ = new AstFloorD{$1, $3}; } | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF{$1, *$5, $3, $6}; } - | yD_FSEEK '(' idClassSel ',' expr ',' expr ')' { $$ = new AstFSeek{$1, $3, $5, $7}; } - | yD_FTELL '(' idClassSel ')' { $$ = new AstFTell{$1, $3}; } + | yD_FSEEK '(' expr ',' expr ',' expr ')' { $$ = new AstFSeek{$1, $3, $5, $7}; } + | yD_FTELL '(' expr ')' { $$ = new AstFTell{$1, $3}; } | yD_GLOBAL_CLOCK parenE { $$ = GRAMMARP->createGlobalClockParseRef($1); } | yD_HIGH '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_HIGH, $3, nullptr}; } | yD_HIGH '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_HIGH, $3, $5}; } @@ -4192,7 +4194,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_RANDOM parenE { $$ = new AstRand{$1, nullptr, false}; } | yD_REALTIME parenE { $$ = new AstTimeD{$1, VTimescale{VTimescale::NONE}}; } | yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits{$1, $3}; } - | yD_REWIND '(' idClassSel ')' { $$ = new AstFSeek{$1, $3, new AstConst{$1, 0}, new AstConst{$1, 0}}; } + | yD_REWIND '(' expr ')' { $$ = new AstFSeek{$1, $3, new AstConst{$1, 0}, new AstConst{$1, 0}}; } | yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, nullptr}; } | yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, $5}; } | yD_ROSE '(' expr ')' { $$ = new AstRose{$1, $3, nullptr}; } diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index fd64c07a8..35a0d54a8 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -24,6 +24,7 @@ module t; reg [16*8:1] letterz; real r; string s; + reg [16*8:1] si; integer i; reg [7:0] v_a,v_b,v_c,v_d; @@ -88,6 +89,9 @@ module t; i = $ferror(file, s); `checkh(i, 2); `checks(s, "No such file or directory"); + si = "xx"; + i = $ferror(file, si); + `checkh(i, 2); end begin diff --git a/test_regress/t/t_sys_file_basic_mcd.out b/test_regress/t/t_sys_file_basic_mcd.out index 636fdaca3..61d320dd4 100644 --- a/test_regress/t/t_sys_file_basic_mcd.out +++ b/test_regress/t/t_sys_file_basic_mcd.out @@ -1,3 +1,4 @@ Sean Connery was the best Bond. To file and to stdout *-* All Finished *-* + diff --git a/test_regress/t/t_sys_file_basic_mcd.v b/test_regress/t/t_sys_file_basic_mcd.v index 54d0aba21..e8329e20d 100644 --- a/test_regress/t/t_sys_file_basic_mcd.v +++ b/test_regress/t/t_sys_file_basic_mcd.v @@ -4,16 +4,17 @@ // any use, without warranty, 2020 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); module t; `define STR(__s) `"__s`" - task automatic fail(string s); begin + task automatic fail(string s); $display({"FAIL! Reason: ", s}); $stop; - end endtask + endtask - task automatic test1; begin + task automatic test1; int fd[30], fd_fail, fd_success, fd_close, tmp; for (int i = 0; i < 30; i++) begin // Attempt to allocate 30 MCD descriptors; returned descriptors @@ -45,9 +46,9 @@ module t; fd_close = fd[i]; $fclose(fd_close); end - end endtask + endtask - task automatic test2; begin + task automatic test2; // Validate basic MCD functionality. integer fd[3], fd_all, tmp; @@ -65,14 +66,29 @@ module t; $fwrite(fd_all, "All other countries are inferior.\n"); $fwrite(fd_all, "Woe betide those to stand against the mighty Scottish nation.\n"); $fclose(fd_all); - end endtask + endtask - task automatic test3; begin + task automatic test3; + int result; // Write some things to standard output. $fwrite(32'h8000_0001, "Sean Connery was the best Bond.\n"); - end endtask + $fwrite(32'h8000_0001); + $fstrobe(32'h8000_0001); - task automatic test4; begin + result = $fseek(32'hffffffff, 0, 0); + `checkd(result, -1); + + result = $ftell(32'hffffffff); + `checkd(result, -1); + + result = $rewind(32'hffffffff); + `checkd(result, -1); + + result = $feof(0); + `checkd(result, 1); + endtask + + task automatic test4; int fd; // Wide filename fd = $fopen({`STR(`TEST_OBJ_DIR), @@ -80,16 +96,16 @@ module t; "except_to_purposefully_break_my_beautiful_code.dat"}); if (fd == 0) fail("Long filename could not be opened."); $fclose(fd); - end endtask + endtask - task automatic test5; begin + task automatic test5; int fd_all; fd_all = $fopen({`STR(`TEST_OBJ_DIR), "/t_sys_file_basic_mcd_test5.dat"}); if (fd_all == 0) fail("could not be opened."); fd_all |= 1; $fdisplay(fd_all, "To file and to stdout"); $fclose(fd_all); - end endtask + endtask initial begin