From a494ad5ec7a0c728a2d87b982771d724831855e8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 5 Apr 2020 11:22:05 -0400 Subject: [PATCH] Support $ferror, #1638. --- Changes | 2 +- bin/verilator | 3 ++- include/verilated.cpp | 7 +++++++ include/verilated_heavy.h | 1 + src/V3AstNodes.h | 24 ++++++++++++++++++++++++ src/V3EmitC.cpp | 7 +++++++ src/V3LinkLValue.cpp | 10 ++++++++++ src/V3LinkResolve.cpp | 4 ++++ src/V3Width.cpp | 8 ++++++++ src/verilog.l | 1 + src/verilog.y | 2 ++ test_regress/t/t_sys_file_basic.v | 7 +++++++ 12 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 0c5e94bfc..9ab756798 100644 --- a/Changes +++ b/Changes @@ -7,7 +7,7 @@ The contributors that suggested a given feature are shown in []. Thanks! ** Add simplistic class support with many restrictions, see manual, #377. -**** Support $fflush without arguments, #1638. +**** Support $ferror, and $fflush without arguments, #1638. * Verilator 4.032 2020-04-04 diff --git a/bin/verilator b/bin/verilator index 5cbf582ae..e1545e98b 100755 --- a/bin/verilator +++ b/bin/verilator @@ -3867,7 +3867,8 @@ $dumplimit/$dumpportlimit are currently ignored. The rarely used optional parameter to $finish and $stop is ignored. -=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fgetc, $fgets, $fscanf, $fwrite +=item $fopen, $fclose, $fdisplay, $ferror, $feof, $fflush, $fgetc, $fgets, +$fscanf, $fwrite File descriptors passed to the file PLI calls must be file descriptors, not MCDs, which includes the mode parameter to $fopen being mandatory. diff --git a/include/verilated.cpp b/include/verilated.cpp index adc6abacb..7620d76a6 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1148,6 +1148,13 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { return got; } +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; + outputr = std::string(::strerror(ret)); + return ret; +} + IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE { // While threadsafe, each thread can only access different file handles char modez[5]; diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 596d21292..8f8d9d383 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -424,6 +424,7 @@ inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } extern std::string VL_TOLOWER_NN(const std::string& ld); extern std::string VL_TOUPPER_NN(const std::string& ld); +extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE; extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, const std::string& filename, void* memp, QData start, diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 2b44aa7a9..9741eba32 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -4912,6 +4912,30 @@ public: AstNode* filep() const { return lhsp(); } }; +class AstFError : public AstNodeMath { +public: + AstFError(FileLine* fl, AstNode* filep, AstNode* strp) + : ASTGEN_SUPER(fl) { + setOp1p(filep); + setOp2p(strp); + } + ASTNODE_NODE_FUNCS(FError) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; } + virtual string emitVerilog() { return "%f$ferror(%l, %r)"; } + virtual string emitC() { V3ERROR_NA_RETURN(""); } + virtual bool cleanOut() const { return true; } + virtual bool cleanLhs() const { return true; } + virtual bool sizeMattersLhs() const { return false; } + virtual int instrCount() const { return widthInstrs() * 64; } + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + void filep(AstNode* nodep) { setOp1p(nodep); } + AstNode* filep() const { return op1p(); } + void strp(AstNode* nodep) { setOp2p(nodep); } + AstNode* strp() const { return op2p(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } +}; + class AstFGetC : public AstNodeUniop { public: AstFGetC(FileLine* fl, AstNode* lhsp) diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 6502f8bb4..9c8b0ba97 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -482,6 +482,13 @@ public: putsQuoted(nodep->text()); puts(")"); } + virtual void visit(AstFError* nodep) VL_OVERRIDE { + puts("VL_FERROR_IN("); + iterateAndNextNull(nodep->filep()); + putbs(", "); + iterateAndNextNull(nodep->strp()); + puts(")"); + } virtual void visit(AstFGetS* nodep) VL_OVERRIDE { checkMaxWords(nodep); emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index c4318b8cb..6788e1d9b 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -102,6 +102,16 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstFError* nodep) VL_OVERRIDE { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + iterateAndNextNull(nodep->filep()); + iterateAndNextNull(nodep->strp()); + m_setRefLvalue = false; + } + m_setRefLvalue = last_setRefLvalue; + } virtual void visit(AstFFlush* nodep) VL_OVERRIDE { bool last_setRefLvalue = m_setRefLvalue; { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 9a24283bc..3f5765881 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -378,6 +378,10 @@ private: iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } + virtual void visit(AstFError* nodep) VL_OVERRIDE { + iterateChildren(nodep); + expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); + } virtual void visit(AstFEof* nodep) VL_OVERRIDE { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f962f46ab..c71102b12 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3088,6 +3088,14 @@ private: assertAtStatement(nodep); iterateCheckFileDesc(nodep, nodep->filep(), BOTH); } + virtual void visit(AstFError* nodep) VL_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); + nodep->dtypeSetLogicUnsized(32, 1, AstNumeric::SIGNED); // Spec says integer return + } + } virtual void visit(AstFEof* nodep) VL_OVERRIDE { if (m_vup->prelim()) { iterateCheckFileDesc(nodep, nodep->filep(), BOTH); diff --git a/src/verilog.l b/src/verilog.l index 2674ff260..13a3229ef 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -217,6 +217,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$fdisplayh" { FL; return yD_FDISPLAYH; } "$fdisplayo" { FL; return yD_FDISPLAYO; } "$feof" { FL; return yD_FEOF; } + "$ferror" { FL; return yD_FERROR; } "$fflush" { FL; return yD_FFLUSH; } "$fgetc" { FL; return yD_FGETC; } "$fgets" { FL; return yD_FGETS; } diff --git a/src/verilog.y b/src/verilog.y index 6013f2248..a830caba0 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -570,6 +570,7 @@ class AstSenTree; %token yD_FDISPLAYH "$fdisplayh" %token yD_FDISPLAYO "$fdisplayo" %token yD_FEOF "$feof" +%token yD_FERROR "$ferror" %token yD_FFLUSH "$fflush" %token yD_FGETC "$fgetc" %token yD_FGETS "$fgets" @@ -3331,6 +3332,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); } | yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); } | yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); } + | yD_FERROR '(' idClassSel ',' 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,NULL,NULL); } diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index 43cbf3c31..fe075a6be 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -9,6 +9,8 @@ `define STRINGIFY(x) `"x`" `define ratio_error(a,b) (((a)>(b) ? ((a)-(b)) : ((b)-(a))) /(a)) `define checkr(gotv,expv) do if (`ratio_error((gotv),(expv))>0.0001) begin $write("%%Error: %s:%0d: got=%g exp=%g\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); +`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); module t; integer file; @@ -75,6 +77,11 @@ module t; // The "r" is required so we get a FD not a MFD file = $fopen("DOES_NOT_EXIST","r"); if (|file) $stop; // Should not exist, IE must return 0 + // Check error function + s = ""; + i = $ferror(file, s); + `checkh(i, 2); + `checks(s, "No such file or directory"); end begin