diff --git a/Changes b/Changes index 1628387e2..e2b3389f0 100644 --- a/Changes +++ b/Changes @@ -4,7 +4,7 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.023 devel -**** Support $rewind. +**** Support $rewind and $ungetc. **** Add -Wpedantic for compliance testing. diff --git a/bin/verilator b/bin/verilator index 2cd9fc102..f5b805a80 100755 --- a/bin/verilator +++ b/bin/verilator @@ -3562,8 +3562,7 @@ MCDs, which includes the mode parameter to $fopen being mandatory. =item $fscanf, $sscanf -Only integer formats are supported; %e, %f, %m, %r, %v, and %z are not -supported. +The formats %r, %v, and %z are not supported. =item $fullskew, $hold, $nochange, $period, $recovery, $recrem, $removal, $setup, $setuphold, $skew, $timeskew, $width diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 019e79cd8..47dc28e30 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -4335,6 +4335,28 @@ public: AstNode* filep() const { return lhsp(); } }; +class AstFUngetC : public AstNodeBiop { +public: + AstFUngetC(FileLine* fl, AstNode* lhsp, AstNode* rhsp) + : AstNodeBiop(fl, lhsp, rhsp) {} + ASTNODE_NODE_FUNCS(FUngetC) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { + return new AstFUngetC(this->fileline(), lhsp, rhsp); } + virtual string emitVerilog() { return "%f$ungetc(%r, %l)"; } + // Non-existent filehandle returns EOF + virtual string emitC() { return "(%li ? (ungetc(%ri, VL_CVT_I_FP(%li)) >= 0 ? 0 : -1) : -1)"; } + virtual bool cleanOut() const { return false; } + virtual bool cleanLhs() const { return true; } + virtual bool cleanRhs() const { return true; } + virtual bool sizeMattersLhs() const { return false; } + virtual bool sizeMattersRhs() const { return false; } + virtual int instrCount() const { return widthInstrs() * 64; } + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + AstNode* filep() const { return lhsp(); } + AstNode* charp() const { return rhsp(); } +}; + class AstNodeSystemUniop : public AstNodeUniop { public: AstNodeSystemUniop(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 591944a58..58725448b 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -149,6 +149,14 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstFUngetC* nodep) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + iterateAndNextNull(nodep->filep()); + } + m_setRefLvalue = last_setRefLvalue; + } virtual void visit(AstSScanF* nodep) { bool last_setRefLvalue = m_setRefLvalue; { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5d3757fe7..6e340360a 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2380,6 +2380,13 @@ private: userIterateAndNext(nodep->strgp(), WidthVP(SELF, BOTH).p()); } } + virtual void visit(AstFUngetC* nodep) { + if (m_vup->prelim()) { + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + iterateCheckSigned32(nodep, "$fungetc character", nodep->charp(), BOTH); + nodep->dtypeSetLogicUnsized(32, 8, AstNumeric::SIGNED); // Spec says integer return + } + } virtual void visit(AstFRead* nodep) { if (m_vup->prelim()) { nodep->dtypeSetSigned32(); // Spec says integer return diff --git a/src/verilog.l b/src/verilog.l index 303dcb845..36d8c41b6 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -220,6 +220,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$test$plusargs" { FL; return yD_TESTPLUSARGS; } "$time" { FL; return yD_TIME; } "$timeskew" { FL; return yaTIMINGSPEC; } + "$ungetc" { FL; return yD_UNGETC; } "$value$plusargs" { FL; return yD_VALUEPLUSARGS; } "$width" { FL; return yaTIMINGSPEC; } "$write" { FL; return yD_WRITE; } diff --git a/src/verilog.y b/src/verilog.y index b974e0c3f..a2f0fa7d2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -557,6 +557,7 @@ class AstSenTree; %token yD_TANH "$tanh" %token yD_TESTPLUSARGS "$test$plusargs" %token yD_TIME "$time" +%token yD_UNGETC "$ungetc" %token yD_UNIT "$unit" %token yD_UNPACKED_DIMENSIONS "$unpacked_dimensions" %token yD_UNSIGNED "$unsigned" @@ -2970,6 +2971,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_TANH '(' expr ')' { $$ = new AstTanhD($1,$3); } | yD_TESTPLUSARGS '(' str ')' { $$ = new AstTestPlusArgs($1,*$3); } | yD_TIME parenE { $$ = new AstTime($1); } + | yD_UNGETC '(' expr ',' expr ')' { $$ = new AstFUngetC($1, $5, $3); } // Arg swap to file first | yD_UNPACKED_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$3); } | yD_UNSIGNED '(' expr ')' { $$ = new AstUnsigned($1,$3); } | yD_VALUEPLUSARGS '(' expr ',' expr ')' { $$ = new AstValuePlusArgs($1,$3,$5); } diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index b67512811..d0bb007c4 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -90,6 +90,10 @@ module t; if ($fgetc(file) != "i") $stop; if ($fgetc(file) != "\n") $stop; + // $ungetc + if ($ungetc("x", file) != 0) $stop; + if ($fgetc(file) != "x") $stop; + // $fgets chars = $fgets(letterl, file); if (`verbose) $write("c=%0d l=%s\n", chars, letterl);