diff --git a/Changes b/Changes index 756dd7f90..f0f87919f 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks! *** Add +verilator+seed, bug1396. [Stan Sokorac] +*** Support $fread. [Leendert van Doorn] + **** Report PORTSHORT errors on concat constants, bug 1400. [Will Korteland] **** Fix VERILATOR_GDB being ignored, msg2860. [Yu Sheng Lin] diff --git a/include/verilated.cpp b/include/verilated.cpp index 4277a555a..64c308ed6 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1384,6 +1384,59 @@ void VL_WRITEMEM_N( fclose(fp); } +IData VL_FREAD_I(int width, int array_lsb, int array_size, + void* memp, IData fpi, IData start, IData count) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + FILE* fp = VL_CVT_I_FP(fpi); + if (VL_UNLIKELY(!fp)) return 0; + if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb); + // Prep for reading + IData read_count = 0; + IData read_elements = 0; + int start_shift = (width-1) & ~7; // bit+7:bit gets first character + int shift = start_shift; + // Read the data + // We process a character at a time, as then we don't need to deal + // with changing buffer sizes dynamically, etc. + while (1) { + int c = fgetc(fp); + if (VL_UNLIKELY(c==EOF)) break; + // Shift value in + IData entry = read_elements + start - array_lsb; + if (width <= 8) { + CData* datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) { *datap = 0; } + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= 16) { + SData* datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) { *datap = 0; } + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= VL_WORDSIZE) { + IData* datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) { *datap = 0; } + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= VL_QUADSIZE) { + QData* datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) { *datap = 0; } + *datap |= ((static_cast(c) << static_cast(shift)) + & VL_MASK_Q(width)); + } else { + WDataOutP datap = &(reinterpret_cast(memp))[ entry*VL_WORDS_I(width) ]; + if (shift == start_shift) { VL_ZERO_RESET_W(width, datap); } + datap[VL_BITWORD_I(shift)] |= (c << VL_BITBIT_I(shift)); + } + // Prep for next + ++read_count; + shift -= 8; + if (shift < 0) { + shift = start_shift; + ++read_elements; + if (VL_UNLIKELY(read_elements >= count)) break; + } + } + return read_count; +} + void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int, QData filename, void* memp, IData start, IData end) VL_MT_SAFE { WData fnw[2]; VL_SET_WQ(fnw, filename); diff --git a/include/verilated.h b/include/verilated.h index fc19cdb1c..65e84f7d0 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -594,6 +594,9 @@ inline IData VL_FOPEN_II(IData filename, IData mode) VL_MT_SAFE { extern void VL_FCLOSE_I(IData fdi); +extern IData VL_FREAD_I(int width, int array_lsb, int array_size, + void* memp, IData fpi, IData start, IData count); + extern void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords, WDataInP filenamep, void* memp, IData start, IData end); extern void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int fnwords, diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 72ad1096c..9ee51d010 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2741,6 +2741,42 @@ public: void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } }; +class AstFRead : public AstNodeMath { + // Parents: expr + // Children: varrefs to load + // Children: file which must be a varref + // Children: low index + // Children: count +public: + AstFRead(FileLine* fileline, AstNode* memp, AstNode* filep, + AstNode* startp, AstNode* countp) + : AstNodeMath(fileline) { + setOp1p(memp); + setOp2p(filep); + setNOp3p(startp); + setNOp4p(countp); + } + ASTNODE_NODE_FUNCS(FRead) + virtual string verilogKwd() const { return "$fread"; } + virtual string emitVerilog() { V3ERROR_NA; return ""; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: makes output + virtual bool cleanOut() { return false; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } + AstNode* memp() const { return op1p(); } + void memp(AstNode* nodep) { setOp1p(nodep); } + AstNode* filep() const { return op2p(); } + void filep(AstNode* nodep) { setOp2p(nodep); } + AstNode* startp() const { return op3p(); } + void startp(AstNode* nodep) { setNOp3p(nodep); } + AstNode* countp() const { return op4p(); } + void countp(AstNode* nodep) { setNOp4p(nodep); } +}; + class AstFScanF : public AstNodeMath { // Parents: expr // Children: file which must be a varref diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index e0d5b06d8..93325e962 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -387,6 +387,45 @@ public: puts(")); }\n"); } } + virtual void visit(AstFRead* nodep) { + puts("VL_FREAD_I("); + puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width + putbs(","); + bool memory = false; + uint32_t array_lsb = 0; + uint32_t array_size = 0; + { + const AstVarRef* varrefp = VN_CAST(nodep->memp(), VarRef); + if (!varrefp) { nodep->v3error(nodep->verilogKwd() << " loading non-variable"); } + else if (VN_CAST(varrefp->varp()->dtypeSkipRefp(), BasicDType)) { } + else if (const AstUnpackArrayDType* adtypep + = VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) { + memory = true; + array_lsb = adtypep->lsb(); + array_size = adtypep->elementsConst(); + } + else { + nodep->v3error(nodep->verilogKwd() + << " loading other than unpacked-array variable"); + } + } + puts(cvtToStr(array_lsb)); + putbs(","); + puts(cvtToStr(array_size)); + putbs(", "); + if (!memory) puts("&("); + iterateAndNextNull(nodep->memp()); + if (!memory) puts(")"); + putbs(", "); + iterateAndNextNull(nodep->filep()); + putbs(", "); + if (nodep->startp()) iterateAndNextNull(nodep->startp()); + else puts(cvtToStr(array_lsb)); + putbs(", "); + if (nodep->countp()) iterateAndNextNull(nodep->countp()); + else puts(cvtToStr(array_size)); + puts(");\n"); + } virtual void visit(AstSysFuncAsTask* nodep) { if (!nodep->lhsp()->isWide()) puts("(void)"); iterateAndNextNull(nodep->lhsp()); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index fb0a4a84f..88d666e8f 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -131,6 +131,15 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstFRead* nodep) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + iterateAndNextNull(nodep->memp()); + iterateAndNextNull(nodep->filep()); + } + m_setRefLvalue = last_setRefLvalue; + } virtual void visit(AstFScanF* nodep) { bool last_setRefLvalue = m_setRefLvalue; { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 36d6b3b9c..5922ed472 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -362,6 +362,10 @@ private: iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } + virtual void visit(AstFRead* nodep) { + iterateChildren(nodep); + expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); + } virtual void visit(AstFScanF* nodep) { iterateChildren(nodep); expectFormat(nodep, nodep->text(), nodep->exprsp(), true); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a9a1557a9..b651775b4 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2232,6 +2232,19 @@ private: userIterateAndNext(nodep->strgp(), WidthVP(SELF,BOTH).p()); } } + virtual void visit(AstFRead* nodep) { + if (m_vup->prelim()) { + nodep->dtypeSetSigned32(); // Spec says integer return + userIterateAndNext(nodep->memp(), WidthVP(SELF,BOTH).p()); + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + if (nodep->startp()) { + iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH); + } + if (nodep->countp()) { + iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH); + } + } + } virtual void visit(AstFScanF* nodep) { if (m_vup->prelim()) { nodep->dtypeSetSigned32(); // Spec says integer return @@ -3172,6 +3185,18 @@ private: underp = iterateCheck(nodep,"file_descriptor",underp,SELF,FINAL,expDTypep,EXTEND_EXP); if (underp) {} // cppcheck } + void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { + // Coerce child to signed32 if not already. Child is self-determined + // underp may change as a result of replacement + if (stage & PRELIM) { + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p()); + } + if (stage & FINAL) { + AstNodeDType* expDTypep = nodep->findSigned32DType(); + underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); + } + if (underp) {} // cppcheck + } void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { // Coerce child to real if not already. Child is self-determined // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST) diff --git a/src/verilog.l b/src/verilog.l index d0a9b7868..49242f0e0 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -227,6 +227,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$finish" { FL; return yD_FINISH; } "$floor" { FL; return yD_FLOOR; } "$fopen" { FL; return yD_FOPEN; } + "$fread" { FL; return yD_FREAD; } "$fscanf" { FL; return yD_FSCANF; } "$fullskew" { FL; return yaTIMINGSPEC; } "$fwrite" { FL; return yD_FWRITE; } diff --git a/src/verilog.y b/src/verilog.y index 492da7f27..afc5d9f50 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -475,6 +475,7 @@ class AstSenTree; %token yD_FINISH "$finish" %token yD_FLOOR "$floor" %token yD_FOPEN "$fopen" +%token yD_FREAD "$fread" %token yD_FSCANF "$fscanf" %token yD_FWRITE "$fwrite" %token yD_HIGH "$high" @@ -2815,6 +2816,9 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); } | 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); } + | yD_FREAD '(' idClassSel ',' expr ',' expr ')' { $$ = new AstFRead($1,$3,$5,$7,NULL); } + | yD_FREAD '(' idClassSel ',' expr ',' expr ',' expr ')' { $$ = new AstFRead($1,$3,$5,$7,$9); } | yD_FLOOR '(' expr ')' { $$ = new AstFloorD($1,$3); } | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF($1,*$5,$3,$6); } | yD_HIGH '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,NULL); } diff --git a/test_regress/t/t_sys_fread.out b/test_regress/t/t_sys_fread.out new file mode 100644 index 000000000..a10cdbc6d --- /dev/null +++ b/test_regress/t/t_sys_fread.out @@ -0,0 +1,27 @@ +Dump: + r_i: 00010203 + r_upb: 0e 0d 0c 0b 0a 09 08 07 06 05 04 + r_dnb: 19 18 17 16 15 14 13 12 11 10 0f + r_ups: 2e2f 2c2d 2a2b 2829 2627 2425 2223 2021 1e1f 1c1d 1a1b + r_dns: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0041 0243 0445 + r_upi: 6e6f7071 6a6b6c6d 66676869 62636465 5e5f6061 5a5b5c5d 56575859 52535455 4e4f5051 4a4b4c4d 46474849 + r_dni: 72737475 76777879 7a7b7c7d 7e7f8081 02838485 06878889 0a8b8c8d 0e8f9091 12939495 16979899 1a9b9c9d + r_upq: 2eeff0f1f2f3f4f5 26e7e8e9eaebeced 1edfe0e1e2e3e4e5 16d7d8d9dadbdcdd 0ecfd0d1d2d3d4d5 06c7c8c9cacbcccd 3ebfc0c1c2c3c4c5 36b7b8b9babbbcbd 2eafb0b1b2b3b4b5 26a7a8a9aaabacad 1e9fa0a1a2a3a4a5 + r_dnq: 36f7f8f9fafbfcfd 3eff000102030405 060708090a0b0c0d 0e0f101112131415 161718191a1b1c1d 1e1f202122232425 262728292a2b2c2d 2e2f303132333435 363738393a3b3c3d 3e3f404142434445 064748494a4b4c4d + r_upw: a8a9aaabacadaeafb0 9fa0a1a2a3a4a5a6a7 969798999a9b9c9d9e 8d8e8f909192939495 8485868788898a8b8c 7b7c7d7e7f80818283 72737475767778797a 696a6b6c6d6e6f7071 606162636465666768 5758595a5b5c5d5e5f 4e4f50515253545556 + r_dnw: b1b2b3b4b5b6b7b8b9 babbbcbdbebfc0c1c2 c3c4c5c6c7c8c9cacb cccdcecfd0d1d2d3d4 d5d6d7d8d9dadbdcdd dedfe0e1e2e3e4e5e6 e7e8e9eaebecedeeef f0f1f2f3f4f5f6f7f8 f9fafbfcfdfeff0001 02030405060708090a 0b0c0d0e0f10111213 + +Dump: + r_i: ffffffff + r_upb: 05 04 03 02 01 00 ff ff ff ff ff + r_dnb: ff ff ff ff ff ff ff ff ff ff ff + r_ups: 3fff 3fff 3fff 3fff 0809 0607 3fff 3fff 3fff 3fff 3fff + r_dns: 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff + r_upi: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff + r_dni: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff + r_upq: 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff + r_dnq: 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff + r_upw: ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff + r_dnw: ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff + +*-* All Finished *-* diff --git a/test_regress/t/t_sys_fread.pl b/test_regress/t/t_sys_fread.pl new file mode 100755 index 000000000..d92562d68 --- /dev/null +++ b/test_regress/t/t_sys_fread.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +use IO::File; +#use Data::Dumper; +use strict; +use vars qw($Self); + +scenarios(simulator => 1); + +sub gen { + my $filename = shift; + + my $fh = IO::File->new(">$filename"); + for (my $copy=0; $copy<32; ++$copy) { + for (my $i=0; $i<=255; ++$i) { + $fh->print(chr($i)); + } + } +} + +gen("$Self->{obj_dir}/t_sys_fread.mem"); + +compile( + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_sys_fread.v b/test_regress/t/t_sys_fread.v new file mode 100644 index 000000000..9f44ce048 --- /dev/null +++ b/test_regress/t/t_sys_fread.v @@ -0,0 +1,87 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Wilson Snyder. + +`define STRINGIFY(x) `"x`" +`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; + integer file; + integer r_i; + byte r_upb[20:10]; + byte r_dnb[20:10]; + reg [13:0] r_ups[20:10]; + reg [13:0] r_dns[10:20]; + reg [30:0] r_upi[20:10]; + reg [30:0] r_dni[10:20]; + reg [61:0] r_upq[20:10]; + reg [61:0] r_dnq[10:20]; + reg [71:0] r_upw[20:10]; + reg [71:0] r_dnw[10:20]; + + task clear; + // Initialize memories to zero, + // avoid differences between 2-state and 4-state. + r_i = ~0; + foreach (r_upb[i]) r_upb[i] = ~0; + foreach (r_dnb[i]) r_dnb[i] = ~0; + foreach (r_ups[i]) r_ups[i] = ~0; + foreach (r_dns[i]) r_dns[i] = ~0; + foreach (r_upi[i]) r_upi[i] = ~0; + foreach (r_dni[i]) r_dni[i] = ~0; + foreach (r_upq[i]) r_upq[i] = ~0; + foreach (r_dnq[i]) r_dnq[i] = ~0; + foreach (r_upw[i]) r_upw[i] = ~0; + foreach (r_dnw[i]) r_dnw[i] = ~0; + + // Open file + $fclose(file); + file = $fopen({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_fread.mem"}, "r"); + if ($feof(file)) $stop; + endtask + + task dump; + $write("Dump:"); + $write("\n r_i:"); $write(" %x",r_i); + $write("\n r_upb:"); foreach (r_upb[i]) $write(" %x", r_upb[i]); + $write("\n r_dnb:"); foreach (r_dnb[i]) $write(" %x", r_dnb[i]); + $write("\n r_ups:"); foreach (r_ups[i]) $write(" %x", r_ups[i]); + $write("\n r_dns:"); foreach (r_dns[i]) $write(" %x", r_dns[i]); + $write("\n r_upi:"); foreach (r_upi[i]) $write(" %x", r_upi[i]); + $write("\n r_dni:"); foreach (r_dni[i]) $write(" %x", r_dni[i]); + $write("\n r_upq:"); foreach (r_upq[i]) $write(" %x", r_upq[i]); + $write("\n r_dnq:"); foreach (r_dnq[i]) $write(" %x", r_dnq[i]); + $write("\n r_upw:"); foreach (r_upw[i]) $write(" %x", r_upw[i]); + $write("\n r_dnw:"); foreach (r_dnw[i]) $write(" %x", r_dnw[i]); + $write("\n\n"); + endtask + + integer code; + + initial begin + clear; + code = $fread(r_i, file); `checkd(code, 4); + code = $fread(r_upb, file); `checkd(code, 11); + code = $fread(r_dnb, file); `checkd(code, 11); + code = $fread(r_ups, file); `checkd(code, 22); + code = $fread(r_dns, file); `checkd(code, 22); + code = $fread(r_upi, file); `checkd(code, 44); + code = $fread(r_dni, file); `checkd(code, 44); + code = $fread(r_upq, file); `checkd(code, 88); + code = $fread(r_dnq, file); `checkd(code, 88); + code = $fread(r_upw, file); `checkd(code, 99); + code = $fread(r_dnw, file); `checkd(code, 99); + dump; + + clear; + code = $fread(r_upb, file, 15); `checkd(code, 6); + code = $fread(r_ups, file, 15, 2); `checkd(code, 4); + dump; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule