diff --git a/Changes b/Changes index f0a261d92..a5468b37e 100644 --- a/Changes +++ b/Changes @@ -5,7 +5,7 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.66* -*** Support $feof, $fflush. [Holger Waechtler] +*** Support $feof, $fgetc, $fgets, $fflush. [Holger Waechtler] *** Support $random. diff --git a/Makefile.in b/Makefile.in index 21cc5bf0b..08d8195d4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -117,6 +117,7 @@ DISTFILES_INC = $(INFOS) .gitignore COPYING *.in *.ac \ test_verilated/vgen*.pl \ test_regress/t/*.cpp \ test_regress/t/*.h \ + test_regress/t/*.dat \ test_regress/t/*.mem \ test_regress/t/*.out \ test_regress/t/*.pl \ diff --git a/bin/verilator b/bin/verilator index ccea2366f..83810faba 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1554,7 +1554,7 @@ them with a $write with the appropriate format specifier. The rarely used optional parameter to $finish and $stop is ignored. -=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fwrite +=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fgetc, $fgets, $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 1cab28774..983cfc2dd 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -248,6 +248,34 @@ void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) { while (isspace(*(destp-1)) && destp>destoutp) *--destp = '\0'; // Drop trailing spaces } +IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq) { + FILE* fp = VL_CVT_Q_FP(fpq); + if (!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(sbits); + char buffer[bytes]; + + // We don't use fgets, as we must read \0s. + IData got = 0; + char* cp = buffer; + while (got < bytes) { + int c = getc(fp); + if (c==EOF) break; + *cp++ = c; got++; + if (c=='\n') break; + } + + // Convert to Verilog format + char* op = ((char*)(strgp)); + IData i; + for (i=0; ifilep()->iterateAndNext(*this); puts(")); "); } + virtual void visit(AstFGetC* nodep, AstNUser*) { + puts("("); + nodep->filep()->iterateAndNext(*this); + puts("? fgetc(VL_CVT_Q_FP("); + nodep->filep()->iterateAndNext(*this); + puts(")) : -1)"); // Non-existant filehandle should return EOF + } virtual void visit(AstWhile* nodep, AstNUser*) { nodep->precondsp()->iterateAndNext(*this); puts("while ("); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 5d925f420..22e0db4f3 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -116,6 +116,23 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstFGetC* nodep, AstNUser*) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + nodep->filep()->iterateAndNext(*this); + } + m_setRefLvalue = last_setRefLvalue; + } + virtual void visit(AstFGetS* nodep, AstNUser*) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + nodep->filep()->iterateAndNext(*this); + nodep->strgp()->iterateAndNext(*this); + } + m_setRefLvalue = last_setRefLvalue; + } virtual void visit(AstReadMem* nodep, AstNUser*) { bool last_setRefLvalue = m_setRefLvalue; { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 5bd4abe1d..436de9b3b 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -332,6 +332,14 @@ private: nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } + virtual void visit(AstFGetC* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); + } + virtual void visit(AstFGetS* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); + } virtual void visit(AstDisplay* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index c2cf55728..1d903975e 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -81,6 +81,8 @@ private: virtual void visit(AstOneHot* nodep,AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstOneHot0* nodep,AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstFEof* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstFGetC* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstFGetS* nodep, AstNUser*) { signed_Ou_Ix(nodep); } // virtual void visit(AstConcat* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstReplicate* nodep, AstNUser*) { signed_Ou_Ix(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 2fb43dddb..272b994e3 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -576,12 +576,25 @@ private: nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); } virtual void visit(AstFEof* nodep, AstNUser*) { - nodep->lhsp()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); nodep->width(1,1); } virtual void visit(AstFFlush* nodep, AstNUser*) { nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); } + virtual void visit(AstFGetC* nodep, AstNUser* vup) { + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + if (vup->c()->prelim()) { + nodep->width(32,8); + } + } + virtual void visit(AstFGetS* nodep, AstNUser* vup) { + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->strgp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + if (vup->c()->prelim()) { + nodep->width(32,32); + } + } virtual void visit(AstReadMem* nodep, AstNUser*) { nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->memp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); diff --git a/src/verilog.l b/src/verilog.l index b633baa90..f8b8e4680 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -134,6 +134,8 @@ escid \\[^ \t\f\r\n]+ "$fdisplay" {yylval.fileline = CRELINE(); return yD_FDISPLAY;} "$feof" {yylval.fileline = CRELINE(); return yD_FEOF;} "$fflush" {yylval.fileline = CRELINE(); return yD_FFLUSH;} + "$fgetc" {yylval.fileline = CRELINE(); return yD_FGETC;} + "$fgets" {yylval.fileline = CRELINE(); return yD_FGETS;} "$finish" {yylval.fileline = CRELINE(); return yD_FINISH;} "$fopen" {yylval.fileline = CRELINE(); return yD_FOPEN;} "$fullskew" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} diff --git a/src/verilog.y b/src/verilog.y index 44639b188..a32fcfce2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -228,6 +228,8 @@ class AstSenTree; %token yD_FDISPLAY "$fdisplay" %token yD_FEOF "$feof" %token yD_FFLUSH "$fflush" +%token yD_FGETC "$fgetc" +%token yD_FGETS "$fgets" %token yD_FINISH "$finish" %token yD_FOPEN "$fopen" %token yD_FWRITE "$fwrite" @@ -1083,6 +1085,8 @@ exprNoStr: expr yP_OROR expr { $$ = new AstLogOr ($2,$1,$3); } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); } | yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); } | yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); } + | yD_FGETC '(' expr ')' { $$ = new AstFGetC($1,$3); } + | yD_FGETS '(' varRefDotBit ',' expr ')' { $$ = new AstFGetS($1,$3,$5); } | yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); } | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); } | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); } diff --git a/test_regress/t/t_sys_file.v b/test_regress/t/t_sys_file.v index 1f4f24885..787ba4ff8 100644 --- a/test_regress/t/t_sys_file.v +++ b/test_regress/t/t_sys_file.v @@ -8,6 +8,11 @@ module t; `verilator_file_descriptor file; + integer chars; + reg [1*8:1] letterl; + reg [8*8:1] letterq; + reg [16*8:1] letterw; + initial begin // Display formatting `ifdef verilator @@ -30,6 +35,7 @@ module t; `endif begin + // Check for opening errors file = $fopen("obj_dir/DOES_NOT_EXIST","r"); // The "r" is required so we get a FD not a MFD if (|file) $stop; // Should not exist, IE must return 0 end @@ -40,6 +46,37 @@ module t; $fclose(file); end + begin + // Check read functions + file = $fopen("t/t_sys_file_input.dat","r"); + if ($feof(file)) $stop; + + // $fgetc + if ($fgetc(file) != "h") $stop; + if ($fgetc(file) != "i") $stop; + if ($fgetc(file) != "\n") $stop; + + // $fgets + chars = $fgets(letterl, file); + $write("c=%0d l=%s\n", chars, letterl); + if (chars != 1) $stop; + if (letterl != "l") $stop; + + chars = $fgets(letterq, file); + $write("c=%0d q=%x=%s", chars, letterq, letterq); // Output includes newline + if (chars != 5) $stop; + if (letterq != "\0\0\0quad\n") $stop; + + letterw = "5432109876543210"; + chars = $fgets(letterw, file); + $write("c=%0d w=%s", chars, letterw); // Output includes newline + if (chars != 10) $stop; + if (letterw != "\0\0\0\0\0\0widestuff\n") $stop; + + $fclose(file); + end + + $write("*-* All Finished *-*\n"); $finish(0); // Test arguments to finish end diff --git a/test_regress/t/t_sys_file_input.dat b/test_regress/t/t_sys_file_input.dat new file mode 100644 index 000000000..8d97612a7 --- /dev/null +++ b/test_regress/t/t_sys_file_input.dat @@ -0,0 +1,3 @@ +hi +lquad +widestuff