From 701bd38d0185900fd69cea0464f0f619deb91f43 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 1 Jul 2008 14:15:10 -0400 Subject: [PATCH] Add support for , . Bug14. --- Changes | 4 +- bin/verilator | 7 +- include/verilated.cpp | 326 +++++++++++++++++++++++++--- include/verilated.h | 9 +- include/verilatedos.h | 1 + src/V3AstNodes.h | 72 ++++++ src/V3EmitC.cpp | 119 +++++++--- src/V3EmitV.cpp | 19 +- src/V3LinkLValue.cpp | 17 ++ src/V3LinkResolve.cpp | 21 +- src/V3Signed.cpp | 2 + src/V3Width.cpp | 14 ++ src/verilog.l | 2 + src/verilog.y | 13 ++ test_regress/t/t_sys_file.v | 112 +++++++++- test_regress/t/t_sys_file_input.dat | 5 + 16 files changed, 656 insertions(+), 87 deletions(-) diff --git a/Changes b/Changes index 91662df0b..b1a8db573 100644 --- a/Changes +++ b/Changes @@ -5,9 +5,9 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.66* -*** Support $feof, $fgetc, $fgets, $fflush. [Holger Waechtler] +*** Add $feof, $fgetc, $fgets, $fflush, $fscanf, $sscanf. [Holger Waechtler] -*** Support $random. +*** Add $random. **** Internal changes to how $displays get compiled and executed. diff --git a/bin/verilator b/bin/verilator index 83810faba..edd6485a5 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, $fgetc, $fgets, $fwrite +=item $fopen, $fclose, $fdisplay, $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. @@ -1564,6 +1564,11 @@ are 32 bits while FILE*s may be 64 bits, the descriptor must be stored in a reg [63:0] rather than an integer. The define `verilator_file_descriptor in verilated.v can be used to hide this difference. +=item $fscanf, $sscanf + +Only integer formats are supported; %e, %f, %m, %r, %v, and %z are not +supported. + =item $fullskew, $hold, $nochange, $period, $recovery, $recrem, $removal, $setup, $setuphold, $skew, $timeskew, $width diff --git a/include/verilated.cpp b/include/verilated.cpp index 2be4f4e9c..de8c83582 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -145,8 +145,11 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { // Format a Verilog $write style format into the output list // The format must be pre-processed (and lower cased) by Verilator // Arguments are in "width, arg-value (or WDataIn* if wide)" form + // // Note uses a single buffer internally; presumes only one usage per printf - static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH]; + // Note also assumes variables < 64 are not wide, this assumption is + // sometimes not true in low-level routines written here in verilated.cpp + static VL_THREAD char tmp[VL_VALUE_STRING_MAX_WIDTH]; bool inPct = false; bool widthSet = false; int width = 0; @@ -184,12 +187,12 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { } default: { // Deal with all read-and-print somethings - int bits = va_arg(ap, int); + const int lbits = va_arg(ap, int); QData ld = 0; WDataInP lwp; - if (bits <= VL_QUADSIZE) { + if (lbits <= VL_QUADSIZE) { WData qlwp[2]; - ld = _VL_VA_ARG_Q(ap, bits); + ld = _VL_VA_ARG_Q(ap, lbits); VL_SET_WQ(qlwp,ld); lwp = qlwp; } else { @@ -197,38 +200,14 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { ld = lwp[0]; if (fmt == 'u' || fmt == 'd') fmt = 'x'; // Not supported, but show something } - int lsb=bits-1; + int lsb=lbits-1; if (widthSet && width==0) while (lsb && !VL_BITISSET_W(lwp,lsb)) lsb--; switch (fmt) { - case 'b': - for (; lsb>=0; lsb--) { - output += ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0'; - } - break; case 'c': { IData charval = ld & 0xff; output += charval; break; } - case 'd': { // Signed decimal - int digits=sprintf(str,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(bits,bits,ld))); - int needmore = width-digits; - if (needmore>0) output.append(needmore,' '); // Pre-pad spaces - output += str; - break; - } - case 'o': - for (; lsb>=0; lsb--) { - lsb = (lsb / 3) * 3; // Next digit - // Octal numbers may span more than one wide word, - // so we need to grab each bit separately and check for overrun - // Octal is rare, so we'll do it a slow simple way - output += ('0' - + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+0)) ? 1 : 0) - + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+1)) ? 2 : 0) - + ((VL_BITISSETLIMIT_W(lwp, bits, lsb+2)) ? 4 : 0)); - } - break; case 's': for (; lsb>=0; lsb--) { lsb = (lsb / 8) * 8; // Next digit @@ -236,13 +215,37 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { output += (charval==0)?' ':charval; } break; - case 'u': { // Unsigned decimal - int digits=sprintf(str,"%llu",ld); + case 'd': { // Signed decimal + int digits=sprintf(tmp,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(lbits,lbits,ld))); int needmore = width-digits; if (needmore>0) output.append(needmore,' '); // Pre-pad spaces - output += str; + output += tmp; break; } + case 'u': { // Unsigned decimal + int digits=sprintf(tmp,"%llu",ld); + int needmore = width-digits; + if (needmore>0) output.append(needmore,' '); // Pre-pad spaces + output += tmp; + break; + } + case 'b': + for (; lsb>=0; lsb--) { + output += ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0'; + } + break; + case 'o': + for (; lsb>=0; lsb--) { + lsb = (lsb / 3) * 3; // Next digit + // Octal numbers may span more than one wide word, + // so we need to grab each bit separately and check for overrun + // Octal is rare, so we'll do it a slow simple way + output += ('0' + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+0)) ? 1 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+1)) ? 2 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+2)) ? 4 : 0)); + } + break; case 'x': for (; lsb>=0; lsb--) { lsb = (lsb / 4) * 4; // Next digit @@ -261,6 +264,227 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { } } +static inline bool _vl_vsss_eof(FILE* fp, int& floc) { + return fp ? feof(fp) : (floc<0); +} +static inline void _vl_vsss_advance(FILE* fp, int& floc) { + if (fp) fgetc(fp); + else floc -= 8; +} +static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp) { + // Get a character without advancing + if (fp) { + int data = fgetc(fp); + if (data == EOF) return EOF; + ungetc(data,fp); + return data; + } else { + if (floc < 0) return EOF; + floc = floc & ~7; // Align to closest character + int data = (fromp[VL_BITWORD_I(floc)] >> VL_BITBIT_I(floc)) & 0xff; + return data; + } +} +static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp) { + while (1) { + int c = _vl_vsss_peek(fp, floc, fromp); + if (c==EOF || !isspace(c)) return; + _vl_vsss_advance(fp, floc); + } +} +static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp, + char* tmpp, const char* acceptp) { + // Read into tmp, consisting of characters from acceptp list + char* cp = tmpp; + while (1) { + int c = _vl_vsss_peek(fp, floc, fromp); + if (c==EOF || isspace(c)) break; + if (acceptp!=NULL // String - allow anything + && NULL==strchr(acceptp, c)) break; + if (acceptp!=NULL) c = tolower(c); // Non-strings we'll simplify + *cp++ = c; + _vl_vsss_advance(fp, floc); + } + *cp++ = '\0'; + //VL_PRINTF("\t_read got='%s'\n", tmpp); +} +static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) { + for (; nbits && lsb>=1) { + VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1); + } +} + +IData _vl_vsscanf(FILE* fp, // If a fscanf + int fbits, WDataInP fromp, // Else if a sscanf + const char* formatp, va_list ap) { + // Read a Verilog $sscanf/$fscanf style format into the output list + // The format must be pre-processed (and lower cased) by Verilator + // Arguments are in "width, arg-value (or WDataIn* if wide)" form + static VL_THREAD char tmp[VL_VALUE_STRING_MAX_WIDTH]; + int floc = fbits - 1; + IData got = 0; + bool inPct = false; + const char* pos = formatp; + for (; *pos && !_vl_vsss_eof(fp,floc); ++pos) { + //VL_PRINTF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc, _vl_vsss_peek(fp,floc,fromp)); + if (!inPct && pos[0]=='%') { + inPct = true; + } else if (!inPct && isspace(pos[0])) { // Format spaces + while (isspace(pos[1])) pos++; + _vl_vsss_skipspace(fp,floc,fromp); + } else if (!inPct) { // Expected Format + _vl_vsss_skipspace(fp,floc,fromp); + int c = _vl_vsss_peek(fp,floc,fromp); + if (c != pos[0]) goto done; + else _vl_vsss_advance(fp,floc); + } else { // Format character + // Skip loading spaces + inPct = false; + char fmt = pos[0]; + switch (fmt) { + case '%': { + int c = _vl_vsss_peek(fp,floc,fromp); + if (c != '%') goto done; + else _vl_vsss_advance(fp,floc); + break; + } + default: { + // Deal with all read-and-scan somethings + // Note LSBs are preserved if there's an overflow + const int obits = va_arg(ap, int); + int lsb = 0; + WData qowp[2]; + WDataOutP owp = qowp; + if (obits > VL_QUADSIZE) { + owp = va_arg(ap,WDataOutP); + } + for (int i=0; i=0; pos--) { + _vl_vsss_setbit(owp,obits,lsb, 8, tmp[pos]); lsb+=8; + } + break; + } + case 'd': { // Signed decimal + _vl_vsss_skipspace(fp,floc,fromp); + _vl_vsss_read(fp,floc,fromp, tmp, "0123456789+-xz?_"); + if (!tmp[0]) goto done; + vlsint64_t ld; + sscanf(tmp,"%lld",&ld); + VL_SET_WQ(owp,ld); + break; + } + case 'u': { // Unsigned decimal + _vl_vsss_skipspace(fp,floc,fromp); + _vl_vsss_read(fp,floc,fromp, tmp, "0123456789+-xz?_"); + if (!tmp[0]) goto done; + QData ld; + sscanf(tmp,"%llu",&ld); + VL_SET_WQ(owp,ld); + break; + } + case 'b': { + _vl_vsss_skipspace(fp,floc,fromp); + _vl_vsss_read(fp,floc,fromp, tmp, "01xz?_"); + if (!tmp[0]) goto done; + int pos = strlen(tmp)-1; + for (int i=0; i=0; pos--) { + switch(tmp[pos]) { + case 'x': case 'z': case '?': //FALLTHRU + case '0': lsb++; break; + case '1': _vl_vsss_setbit(owp,obits,lsb, 1, 1); lsb++; break; + case '_': break; + } + } + break; + } + case 'o': { + _vl_vsss_skipspace(fp,floc,fromp); + _vl_vsss_read(fp,floc,fromp, tmp, "01234567xz?_"); + if (!tmp[0]) goto done; + int pos = strlen(tmp)-1; + for (int i=0; i=0; pos--) { + switch(tmp[pos]) { + case 'x': case 'z': case '?': //FALLTHRU + case '0': lsb+=3; break; + case '1': _vl_vsss_setbit(owp,obits,lsb, 3, 1); lsb+=3; break; + case '2': _vl_vsss_setbit(owp,obits,lsb, 3, 2); lsb+=3; break; + case '3': _vl_vsss_setbit(owp,obits,lsb, 3, 3); lsb+=3; break; + case '4': _vl_vsss_setbit(owp,obits,lsb, 3, 4); lsb+=3; break; + case '5': _vl_vsss_setbit(owp,obits,lsb, 3, 5); lsb+=3; break; + case '6': _vl_vsss_setbit(owp,obits,lsb, 3, 6); lsb+=3; break; + case '7': _vl_vsss_setbit(owp,obits,lsb, 3, 7); lsb+=3; break; + case '_': break; + } + } + break; + } + case 'x': { + _vl_vsss_skipspace(fp,floc,fromp); + _vl_vsss_read(fp,floc,fromp, tmp, "0123456789abcdefxz?_"); + if (!tmp[0]) goto done; + int pos = strlen(tmp)-1; + for (int i=0; i=0; pos--) { + switch(tmp[pos]) { + case 'x': case 'z': case '?': //FALLTHRU + case '0': lsb+=4; break; + case '1': _vl_vsss_setbit(owp,obits,lsb, 4, 1); lsb+=4; break; + case '2': _vl_vsss_setbit(owp,obits,lsb, 4, 2); lsb+=4; break; + case '3': _vl_vsss_setbit(owp,obits,lsb, 4, 3); lsb+=4; break; + case '4': _vl_vsss_setbit(owp,obits,lsb, 4, 4); lsb+=4; break; + case '5': _vl_vsss_setbit(owp,obits,lsb, 4, 5); lsb+=4; break; + case '6': _vl_vsss_setbit(owp,obits,lsb, 4, 6); lsb+=4; break; + case '7': _vl_vsss_setbit(owp,obits,lsb, 4, 7); lsb+=4; break; + case '8': _vl_vsss_setbit(owp,obits,lsb, 4, 8); lsb+=4; break; + case '9': _vl_vsss_setbit(owp,obits,lsb, 4, 9); lsb+=4; break; + case 'a': _vl_vsss_setbit(owp,obits,lsb, 4, 10); lsb+=4; break; + case 'b': _vl_vsss_setbit(owp,obits,lsb, 4, 11); lsb+=4; break; + case 'c': _vl_vsss_setbit(owp,obits,lsb, 4, 12); lsb+=4; break; + case 'd': _vl_vsss_setbit(owp,obits,lsb, 4, 13); lsb+=4; break; + case 'e': _vl_vsss_setbit(owp,obits,lsb, 4, 14); lsb+=4; break; + case 'f': _vl_vsss_setbit(owp,obits,lsb, 4, 15); lsb+=4; break; + case '_': break; + } + } + break; + } + default: + string msg = string("%%Error: Unknown _vl_vsscanf code: ")+pos[0]+"\n"; + vl_fatal(__FILE__,__LINE__,"",msg.c_str()); + break; + } // switch + + got++; + // Reload data if non-wide (if wide, we put it in the right place directly) + if (obits <= VL_BYTESIZE) { + CData* p = va_arg(ap,CData*); *p = owp[0]; + } else if (obits <= VL_SHORTSIZE) { + SData* p = va_arg(ap,SData*); *p = owp[0]; + } else if (obits <= VL_WORDSIZE) { + IData* p = va_arg(ap,IData*); *p = owp[0]; + } else if (obits <= VL_QUADSIZE) { + QData* p = va_arg(ap,QData*); *p = VL_SET_QW(owp); + } + } + } // switch + } + } + done: + return got; +} + //=========================================================================== // File I/O @@ -327,7 +551,6 @@ QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) { } void VL_WRITEF(const char* formatp, ...) { - va_list ap; va_start(ap,formatp); string output; @@ -351,6 +574,43 @@ void VL_FWRITEF(QData fpq, const char* formatp, ...) { fputs(output.c_str(), fp); } +IData VL_FSCANF_IX(QData fpq, const char* formatp, ...) { + FILE* fp = VL_CVT_Q_FP(fpq); + if (VL_UNLIKELY(!fp)) return 0; + + va_list ap; + va_start(ap,formatp); + IData got = _vl_vsscanf(fp, 0, NULL, formatp, ap); + va_end(ap); + return got; +} + +IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) { + IData fnw[2]; VL_SET_WI(fnw, ld); + + va_list ap; + va_start(ap,formatp); + IData got = _vl_vsscanf(NULL, lbits, fnw, formatp, ap); + va_end(ap); + return got; +} +IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) { + IData fnw[2]; VL_SET_WQ(fnw, ld); + + va_list ap; + va_start(ap,formatp); + IData got = _vl_vsscanf(NULL, lbits, fnw, formatp, ap); + va_end(ap); + return got; +} +IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) { + va_list ap; + va_start(ap,formatp); + IData got = _vl_vsscanf(NULL, lbits, lwp, formatp, ap); + va_end(ap); + return got; +} + void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int, QData ofilename, void* memp, IData start, IData end) { IData fnw[2]; VL_SET_WQ(fnw, ofilename); diff --git a/include/verilated.h b/include/verilated.h index 533dfa1f0..3ea9a7295 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -209,6 +209,11 @@ inline void VL_READMEM_I(bool hex, int width, int depth, int array_lsb, int fnwo extern void VL_WRITEF(const char* formatp, ...); extern void VL_FWRITEF(QData fpq, const char* formatp, ...); +extern IData VL_FSCANF_IX(QData fpq, const char* formatp, ...); +extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...); +extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...); +extern IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...); + //========================================================================= // Base macros @@ -219,7 +224,9 @@ extern void VL_FWRITEF(QData fpq, const char* formatp, ...); #define VL_BITISSETLIMIT_W(data,width,bit) (((bit)<(width)) && data[VL_BITWORD_I(bit)] & (VL_UL(1)<>VL_WORDSIZE); } +#define VL_SET_WQ(owp,data) { owp[0]=(data); owp[1]=((data)>>VL_WORDSIZE); } +#define VL_SET_WI(owp,data) { owp[0]=(data); owp[1]=0; } +#define VL_SET_QW(lwp) ( ((QData)(lwp[0])) | ((QData)(lwp[1])<<((QData)(VL_WORDSIZE)) )) // Use a union to avoid cast-to-different-size warnings /// Return FILE* from QData diff --git a/include/verilatedos.h b/include/verilatedos.h index 3e599549b..5037c0a4c 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -111,6 +111,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type // Integer size macros #define VL_BYTESIZE 8 ///< Bits in a byte +#define VL_SHORTSIZE 16 ///< Bits in a short #define VL_WORDSIZE 32 ///< Bits in a word #define VL_QUADSIZE 64 ///< Bits in a quadword #define VL_WORDSIZE_LOG2 5 ///< log2(VL_WORDSIZE) diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 2e4d30135..93b2269f8 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1349,6 +1349,78 @@ struct AstFFlush : public AstNodeStmt { void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } }; +struct AstFScanF : public AstNodeMath { + // Parents: expr + // Children: file which must be a varref + // Children: varrefs to load +private: + string m_text; +public: + AstFScanF(FileLine* fileline, const string& text, AstNode* filep, AstNode* exprsp) + : AstNodeMath (fileline), m_text(text) { + addNOp1p(exprsp); + setNOp2p(filep); + } + virtual ~AstFScanF() {} + virtual AstType type() const { return AstType::FSCANF;} + virtual AstNode* clone() { return new AstFScanF(*this); } + virtual string name() const { return m_text; } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return "$fscanf"; } + 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 isSplittable() 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(text()); } + virtual bool same(AstNode* samep) const { + return text()==samep->castFScanF()->text(); } + AstNode* exprsp() const { return op1p()->castNode(); } // op1 = Expressions to output + void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text=text; } + AstNode* filep() const { return op2p(); } + void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } +}; + +struct AstSScanF : public AstNodeMath { + // Parents: expr + // Children: file which must be a varref + // Children: varrefs to load +private: + string m_text; +public: + AstSScanF(FileLine* fileline, const string& text, AstNode* fromp, AstNode* exprsp) + : AstNodeMath (fileline), m_text(text) { + addNOp1p(exprsp); + setOp2p(fromp); + } + virtual ~AstSScanF() {} + virtual AstType type() const { return AstType::SSCANF;} + virtual AstNode* clone() { return new AstSScanF(*this); } + virtual string name() const { return m_text; } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return "$sscanf"; } + 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 isSplittable() 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(text()); } + virtual bool same(AstNode* samep) const { + return text()==samep->castSScanF()->text(); } + AstNode* exprsp() const { return op1p()->castNode(); } // op1 = Expressions to output + void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text=text; } + AstNode* fromp() const { return op2p(); } + void fromp(AstNode* nodep) { setOp2p(nodep); } +}; + struct AstReadMem : public AstNodeStmt { private: bool m_isHex; // readmemh, not readmemb diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index f86ed50b3..a01030586 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -78,8 +78,10 @@ public: //int debug() { return 9; } // METHODS - void displayEmit(AstDisplay* nodep); - void displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter); + void displayNode(AstNode* nodep, const string& vformat, AstNode* exprsp, bool isScan); + void displayEmit(AstNode* nodep, bool isScan); + void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, + string vfmt, char fmtLetter); void emitVarDecl(AstVar* nodep, const string& prefixIfImp); typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich; @@ -229,7 +231,23 @@ public: nodep->lhsp()->iterateAndNext(*this); puts(");\n"); } - virtual void visit(AstDisplay* nodep, AstNUser*); // BELOW + virtual void visit(AstDisplay* nodep, AstNUser*) { + string text = nodep->text(); + if (nodep->addNewline()) text += "\\n"; + displayNode(nodep, text, nodep->exprsp(), false); + } + virtual void visit(AstFScanF* nodep, AstNUser*) { + displayNode(nodep, nodep->text(), nodep->exprsp(), true); + } + virtual void visit(AstSScanF* nodep, AstNUser*) { + displayNode(nodep, nodep->text(), nodep->exprsp(), true); + } + + void checkMaxWords(AstNode* nodep) { + if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) { + nodep->v3error("String of "<width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); + } + } virtual void visit(AstFOpen* nodep, AstNUser*) { nodep->filep()->iterateAndNext(*this); puts(" = VL_FOPEN_"); @@ -241,9 +259,7 @@ public: puts(cvtToStr(nodep->filenamep()->widthWords())); putbs(", "); } - if (nodep->filenamep()->widthWords() > VL_TO_STRING_MAX_WORDS) { - nodep->v3error("String of "<filenamep()->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); - } + checkMaxWords(nodep->filenamep()); nodep->filenamep()->iterateAndNext(*this); putbs(", "); nodep->modep()->iterateAndNext(*this); @@ -270,9 +286,7 @@ public: puts(cvtToStr(array_lsb)); putbs(","); puts(cvtToStr(nodep->filenamep()->widthWords())); - if (nodep->filenamep()->widthWords() > VL_TO_STRING_MAX_WORDS) { - nodep->v3error("String of "<filenamep()->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); - } + checkMaxWords(nodep->filenamep()); putbs(", "); nodep->filenamep()->iterateAndNext(*this); putbs(", "); @@ -950,16 +964,40 @@ struct EmitDispState { } } emitDispState; -void EmitCStmts::displayEmit(AstDisplay* nodep) { - if (emitDispState.m_format != "") { +void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) { + if (emitDispState.m_format == "" + && nodep->castDisplay()) { // not fscanf etc, as they need to return value + // NOP + } else { // Format - if (nodep->filep()) { - puts("VL_FWRITEF("); - nodep->filep()->iterate(*this); - puts(",\""); + bool isStmt; + if (AstFScanF* dispp = nodep->castFScanF()) { + isStmt = false; + puts("VL_FSCANF_IX("); + dispp->filep()->iterate(*this); + puts(","); + } else if (AstSScanF* dispp = nodep->castSScanF()) { + isStmt = false; + checkMaxWords(dispp->fromp()); + puts("VL_SSCANF_I"); emitIQW(dispp->fromp()); puts("X("); + puts(cvtToStr(dispp->fromp()->widthMin())); + puts(","); + dispp->fromp()->iterate(*this); + puts(","); + } else if (AstDisplay* dispp = nodep->castDisplay()) { + isStmt = true; + if (dispp->filep()) { + puts("VL_FWRITEF("); + dispp->filep()->iterate(*this); + puts(","); + } else { + puts("VL_WRITEF("); + } } else { - puts("VL_WRITEF(\""); + isStmt = true; + nodep->v3fatalSrc("Unknown displayEmit node type"); } + puts("\""); ofp()->putsNoTracking(emitDispState.m_format); puts("\""); // Arguments @@ -970,17 +1008,24 @@ void EmitCStmts::displayEmit(AstDisplay* nodep) { ofp()->indentInc(); ofp()->putbs(""); if (func!="") puts(func); - if (argp) argp->iterate(*this); + if (argp) { + if (isScan) puts("&("); + argp->iterate(*this); + if (isScan) puts(")"); + } ofp()->indentDec(); } // End - puts(");\n"); + puts(")"); + if (isStmt) puts(";\n"); + else puts(" "); // Prep for next emitDispState.clear(); } } -void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, char fmtLetter) { +void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, + string vfmt, char fmtLetter) { // Print display argument, edits elistp AstNode* argp = *elistp; if (!argp) { @@ -993,16 +1038,17 @@ void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, ch } if (argp && argp->isWide() && (fmtLetter=='d'||fmtLetter=='u')) { - argp->v3error("Unsupported: $display of dec format of > 64 bit results (use hex format instead)"); + argp->v3error("Unsupported: "<verilogKwd()<<" of dec format of > 64 bit results (use hex format instead)"); } if (argp && argp->widthMin()>8 && fmtLetter=='c') { // Technically legal, but surely not what the user intended. - argp->v3error("$display of char format of > 8 bit result"); + argp->v3error(dispp->verilogKwd()<<" of char format of > 8 bit result"); } //string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter; string pfmt; if ((fmtLetter=='u' || fmtLetter=='d') + && !isScan && vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros double mantissabits = argp->widthMin() - ((fmtLetter=='d')?1:0); double maxval = pow(2.0, mantissabits); @@ -1021,15 +1067,15 @@ void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string vfmt, ch *elistp = (*elistp)->nextp(); } -void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) { - string vformat = nodep->text(); - AstNode* elistp = nodep->exprsp(); +void EmitCStmts::displayNode(AstNode* nodep, const string& vformat, AstNode* exprsp, + bool isScan) { + AstNode* elistp = exprsp; // Convert Verilog display to C printf formats // "%0t" becomes "%d" emitDispState.clear(); string vfmt = ""; - string::iterator pos = vformat.begin(); + string::const_iterator pos = vformat.begin(); bool inPct = false; for (; pos != vformat.end(); ++pos) { //UINFO(1,"Parse '"<<*pos<<"' IP"<name()"); - if (!nodep->scopeNamep()) nodep->v3fatalSrc("Display with %m but no AstScopeName"); - for (AstText* textp=nodep->scopeNamep()->scopeAttrp(); textp; textp=textp->nextp()->castText()) { + if (!nodep->castDisplay()) nodep->v3fatalSrc("Non-Display with %m"); + AstScopeName* scopenamep = nodep->castDisplay()->scopeNamep(); + if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName"); + for (AstText* textp=scopenamep->scopeAttrp(); textp; textp=textp->nextp()->castText()) { emitDispState.pushFormat(textp->text()); } break; @@ -1086,8 +1134,7 @@ void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) { // expectFormat also checks this, and should have found it first, so internal elistp->v3error("Internal: Extra arguments for $display format\n"); } - if (nodep->addNewline()) emitDispState.pushFormat("\\n"); - displayEmit(nodep); + displayEmit(nodep, isScan); } //###################################################################### diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index a6936dffb..42b046277 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -163,19 +163,30 @@ public: virtual void visit(AstCoverInc*, AstNUser*) { // N/A } - virtual void visit(AstDisplay* nodep, AstNUser*) { + + void visitNodeDisplay(AstNode* nodep, AstNode* filep, const string& text, AstNode* exprsp) { putbs(nodep->verilogKwd()); putbs(" ("); - if (nodep->filep()) { nodep->filep()->iterateAndNext(*this); putbs(","); } + if (filep) { filep->iterateAndNext(*this); putbs(","); } puts("\""); - ofp()->putsNoTracking(nodep->text()); + ofp()->putsNoTracking(text); puts("\""); - for (AstNode* expp=nodep->exprsp(); expp; expp = expp->nextp()) { + for (AstNode* expp=exprsp; expp; expp = expp->nextp()) { puts(","); expp->iterateAndNext(*this); } puts(");\n"); } + virtual void visit(AstDisplay* nodep, AstNUser*) { + visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp()); + } + virtual void visit(AstFScanF* nodep, AstNUser*) { + visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp()); + } + virtual void visit(AstSScanF* nodep, AstNUser*) { + visitNodeDisplay(nodep, nodep->fromp(), nodep->text(), nodep->exprsp()); + } + virtual void visit(AstFOpen* nodep, AstNUser*) { putbs(nodep->verilogKwd()); putbs(" ("); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 40281a7e2..184bf6b20 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -133,6 +133,23 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstFScanF* nodep, AstNUser*) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + nodep->filep()->iterateAndNext(*this); + nodep->exprsp()->iterateAndNext(*this); + } + m_setRefLvalue = last_setRefLvalue; + } + virtual void visit(AstSScanF* nodep, AstNUser*) { + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; + nodep->exprsp()->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 50006fa11..48132181f 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -312,22 +312,24 @@ private: } } - void expectFormat(AstNode* nodep, const string& format, AstNode* argp) { + void expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) { // Check display arguments bool inPct = false; for (const char* inp = format.c_str(); *inp; inp++) { - char ch = *inp; // Breaks with iterators... + char ch = tolower(*inp); // Breaks with iterators... if (!inPct && ch=='%') { inPct = true; } else if (inPct) { inPct = false; - switch (tolower(ch)) { + switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': inPct = true; break; case '%': break; // %% - just output a % - case 'm': break; // %m - auto insert "name" + case 'm': // %m - auto insert "name" + if (isScan) nodep->v3error("Unsupported: %m in $fscanf"); + break; default: // Most operators, just move to next argument if (!V3Number::displayedFmtLegal(ch)) { nodep->v3error("Unknown $display format code: %"<iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } + virtual void visit(AstFScanF* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); + expectFormat(nodep, nodep->text(), nodep->exprsp(), true); + } + virtual void visit(AstSScanF* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectFormat(nodep, nodep->text(), nodep->exprsp(), true); + } virtual void visit(AstDisplay* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); - expectFormat(nodep, nodep->name(), nodep->exprsp()); + expectFormat(nodep, nodep->text(), nodep->exprsp(), false); if (!m_assertp && (nodep->displayType() == AstDisplayType::INFO || nodep->displayType() == AstDisplayType::WARNING diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index 86d7d7265..4f913dd1a 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -83,6 +83,8 @@ private: 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(AstFScanF* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstSScanF* 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 42f34fc25..f39dbddde 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -595,6 +595,20 @@ private: nodep->width(32,32); } } + virtual void visit(AstFScanF* nodep, AstNUser* vup) { + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->exprsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + if (vup->c()->prelim()) { + nodep->width(32,32); + } + } + virtual void visit(AstSScanF* nodep, AstNUser* vup) { + nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->exprsp()->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 f8b8e4680..f198556c4 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -138,6 +138,7 @@ escid \\[^ \t\f\r\n]+ "$fgets" {yylval.fileline = CRELINE(); return yD_FGETS;} "$finish" {yylval.fileline = CRELINE(); return yD_FINISH;} "$fopen" {yylval.fileline = CRELINE(); return yD_FOPEN;} + "$fscanf" {yylval.fileline = CRELINE(); return yD_FSCANF;} "$fullskew" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} "$fwrite" {yylval.fileline = CRELINE(); return yD_FWRITE;} "$hold" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} @@ -153,6 +154,7 @@ escid \\[^ \t\f\r\n]+ "$setup" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} "$setuphold" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} "$skew" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} + "$sscanf" {yylval.fileline = CRELINE(); return yD_SSCANF;} "$stop" {yylval.fileline = CRELINE(); return yD_STOP;} "$time" {yylval.fileline = CRELINE(); return yD_TIME;} "$timeskew" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;} diff --git a/src/verilog.y b/src/verilog.y index dd3a64b37..829af5900 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -232,6 +232,7 @@ class AstSenTree; %token yD_FGETS "$fgets" %token yD_FINISH "$finish" %token yD_FOPEN "$fopen" +%token yD_FSCANF "$fscanf" %token yD_FWRITE "$fwrite" %token yD_INFO "$info" %token yD_ISUNKNOWN "$isunknown" @@ -241,6 +242,7 @@ class AstSenTree; %token yD_READMEMB "$readmemb" %token yD_READMEMH "$readmemh" %token yD_SIGNED "$signed" +%token yD_SSCANF "$sscanf" %token yD_STOP "$stop" %token yD_TIME "$time" %token yD_UNSIGNED "$unsigned" @@ -429,6 +431,7 @@ class AstSenTree; %type gateBuf gateNot gateAnd gateNand gateOr gateNor gateXor gateXnor %type gateAndPinList gateOrPinList gateXorPinList %type commaEListE +%type commaVRDListE vrdList %type pslStmt pslDir pslDirOne pslProp %type pslDecl @@ -1087,6 +1090,8 @@ exprNoStr: expr yP_OROR expr { $$ = new AstLogOr ($2,$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_FSCANF '(' expr ',' yaSTRING commaVRDListE ')' { $$ = new AstFScanF($1,*$5,$3,$6); } + | yD_SSCANF '(' expr ',' yaSTRING commaVRDListE ')' { $$ = new AstSScanF($1,*$5,$3,$6); } | yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); } | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); } | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); } @@ -1136,6 +1141,14 @@ commaEListE: /* empty */ { $$ = NULL; } | ',' exprList { $$ = $2; } ; +vrdList: varRefDotBit { $$ = $1; } + | vrdList ',' varRefDotBit { $$ = $1;$1->addNext($3); } + ; + +commaVRDListE: /* empty */ { $$ = NULL; } + | ',' vrdList { $$ = $2; } + ; + //************************************************ // Gate declarations diff --git a/test_regress/t/t_sys_file.v b/test_regress/t/t_sys_file.v index 787ba4ff8..9424dc9ac 100644 --- a/test_regress/t/t_sys_file.v +++ b/test_regress/t/t_sys_file.v @@ -12,6 +12,13 @@ module t; reg [1*8:1] letterl; reg [8*8:1] letterq; reg [16*8:1] letterw; + reg [16*8:1] letterz; + +`ifdef TEST_VERBOSE + `define verbose 1'b1 +`else + `define verbose 1'b0 +`endif initial begin // Display formatting @@ -55,29 +62,124 @@ module t; 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 (`verbose) $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 (`verbose) $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 (`verbose) $write("c=%0d w=%s", chars, letterw); // Output includes newline if (chars != 10) $stop; if (letterw != "\0\0\0\0\0\0widestuff\n") $stop; + // $sscanf + if ($sscanf("x","")!=0) $stop; + if ($sscanf("z","z")!=0) $stop; + + chars = $sscanf("blabcdefghijklmnop", + "%s", letterq); + if (`verbose) $write("c=%0d sa=%s\n", chars, letterq); + if (chars != 1) $stop; + if (letterq != "ijklmnop") $stop; + + chars = $sscanf("xa=1f xb=12898971238912389712783490823_237904689_02348923", + "xa=%x xb=%x", letterq, letterw); + if (`verbose) $write("c=%0d xa=%x xb=%x\n", chars, letterq, letterw); + if (chars != 2) $stop; + if (letterq != 64'h1f) $stop; + if (letterw != 128'h38971278349082323790468902348923) $stop; + + chars = $sscanf("ba=10 bb=110100101010010101012 note_the_two ", + "ba=%b bb=%b%s", letterq, letterw, letterz); + if (`verbose) $write("c=%0d xa=%x xb=%x z=%0s\n", chars, letterq, letterw, letterz); + if (chars != 3) $stop; + if (letterq != 64'h2) $stop; + if (letterw != 128'hd2a55) $stop; + if (letterz != {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0","2"}) $stop; + + chars = $sscanf("oa=23 ob=125634123615234123681236", + "oa=%o ob=%o", letterq, letterw); + if (`verbose) $write("c=%0d oa=%x ob=%x\n", chars, letterq, letterw); + if (chars != 2) $stop; + if (letterq != 64'h13) $stop; + if (letterw != 128'h55ce14f1a9c29e) $stop; + + chars = $sscanf("d=-236123", + "d=%d", letterq); + if (`verbose) $write("c=%0d d=%d\n", chars, letterq); + if (chars != 1) $stop; + if (letterq != 64'hfffffffffffc65a5) $stop; + + // $fscanf + if ($fscanf(file,"")!=0) $stop; + + if (!sync("*")) $stop; + chars = $fscanf(file, "xa=%x xb=%x", letterq, letterw); + if (`verbose) $write("c=%0d xa=%0x xb=%0x\n", chars, letterq, letterw); + if (chars != 2) $stop; + if (letterq != 64'h1f) $stop; + if (letterw != 128'h23790468902348923) $stop; + + if (!sync("\n")) $stop; + if (!sync("*")) $stop; + chars = $fscanf(file, "ba=%b bb=%b %s", letterq, letterw, letterz); + if (`verbose) $write("c=%0d ba=%0x bb=%0x z=%0s\n", chars, letterq, letterw, letterz); + if (chars != 3) $stop; + if (letterq != 64'h2) $stop; + if (letterw != 128'hd2a55) $stop; + if (letterz != "\0\0\0\0note_the_two") $stop; + + if (!sync("\n")) $stop; + if (!sync("*")) $stop; + chars = $fscanf(file, "oa=%o ob=%o", letterq, letterw); + if (`verbose) $write("c=%0d oa=%0x ob=%0x\n", chars, letterq, letterw); + if (chars != 2) $stop; + if (letterq != 64'h13) $stop; + if (letterw != 128'h1573) $stop; + + if (!sync("\n")) $stop; + if (!sync("*")) $stop; + chars = $fscanf(file, "d=%d", letterq); + if (`verbose) $write("c=%0d d=%0x\n", chars, letterq); + if (chars != 1) $stop; + if (letterq != 64'hfffffffffffc65a5) $stop; + + if (!sync("\n")) $stop; + if (!sync("*")) $stop; + chars = $fscanf(file, "%c%s", letterl, letterw); + if (`verbose) $write("c=%0d q=%c s=%s\n", chars, letterl, letterw); + if (chars != 2) $stop; + if (letterl != "f") $stop; + if (letterw != "\0\0\0\0\0redfishblah") $stop; + + chars = $fscanf(file, "%c", letterl); + if (`verbose) $write("c=%0d l=%x\n", chars, letterl); + if (chars != 1) $stop; + if (letterl != "\n") $stop; + $fclose(file); end - $write("*-* All Finished *-*\n"); $finish(0); // Test arguments to finish end + + function sync; + input [7:0] cexp; + reg [7:0] cgot; + begin + cgot = $fgetc(file); + if (`verbose) $write("sync=%x='%c'\n", cgot,cgot); + sync = (cgot == cexp); + end + endfunction + endmodule diff --git a/test_regress/t/t_sys_file_input.dat b/test_regress/t/t_sys_file_input.dat index 8d97612a7..0a085c6fb 100644 --- a/test_regress/t/t_sys_file_input.dat +++ b/test_regress/t/t_sys_file_input.dat @@ -1,3 +1,8 @@ hi lquad widestuff +*xa=1f xb=237904689_02348923 +*ba=10 bb=11010010101001010101 note_the_two +*oa=23 ob=12563 +*d=-236123 +*fredfishblah