Add support for , . Bug14.

This commit is contained in:
Wilson Snyder 2008-07-01 14:15:10 -04:00
parent 1a8c8bec0d
commit 701bd38d01
16 changed files with 656 additions and 87 deletions

View File

@ -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.

View File

@ -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

View File

@ -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<obits; nbits--, lsb++, ld>>=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<VL_WORDS_I(obits); i++) owp[i] = 0;
switch (fmt) {
case 'c': {
int c = _vl_vsss_peek(fp,floc,fromp);
if (c==EOF) goto done;
else _vl_vsss_advance(fp,floc);
owp[0] = c;
break;
}
case 's': {
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, NULL);
if (!tmp[0]) goto done;
int pos = strlen(tmp)-1;
for (int i=0; i<obits && pos>=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<obits && pos>=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<obits && pos>=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<obits && pos>=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);

View File

@ -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_BITBIT_I(bit)))
/// Create two 32-bit words from quadword
#define VL_SET_WQ(decl,data) { decl[0]=(data); decl[1]=((data)>>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

View File

@ -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)

View File

@ -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

View File

@ -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 "<<nodep->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 "<<nodep->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 "<<nodep->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: "<<dispp->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"<<inPct<<" List "<<(void*)(elistp)<<endl);
@ -1051,21 +1097,23 @@ void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) {
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
break;
// Special codes
case '~': displayArg(nodep,&elistp,vfmt,'d'); break; // Signed decimal
case '~': displayArg(nodep,&elistp,isScan, vfmt,'d'); break; // Signed decimal
// Spec: h d o b c l
case 'b': displayArg(nodep,&elistp,vfmt,'b'); break;
case 'c': displayArg(nodep,&elistp,vfmt,'c'); break;
case 'b': displayArg(nodep,&elistp,isScan, vfmt,'b'); break;
case 'c': displayArg(nodep,&elistp,isScan, vfmt,'c'); break;
case 't':
case 'd': displayArg(nodep,&elistp,vfmt,'u'); break; // Unsigned decimal
case 'o': displayArg(nodep,&elistp,vfmt,'o'); break;
case 'd': displayArg(nodep,&elistp,isScan, vfmt,'u'); break; // Unsigned decimal
case 'o': displayArg(nodep,&elistp,isScan, vfmt,'o'); break;
case 'h':
case 'x': displayArg(nodep,&elistp,vfmt,'x'); break;
case 's': displayArg(nodep,&elistp,vfmt,'s'); break;
case 'x': displayArg(nodep,&elistp,isScan, vfmt,'x'); break;
case 's': displayArg(nodep,&elistp,isScan, vfmt,'s'); break;
case 'm': {
emitDispState.pushFormat("%S");
emitDispState.pushArg(NULL, "vlSymsp->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);
}
//######################################################################

View File

@ -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(" (");

View File

@ -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;
{

View File

@ -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: %"<<ch);
@ -376,10 +378,19 @@ private:
nodep->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

View File

@ -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); }

View File

@ -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());

View File

@ -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;}

View File

@ -232,6 +232,7 @@ class AstSenTree;
%token<fileline> yD_FGETS "$fgets"
%token<fileline> yD_FINISH "$finish"
%token<fileline> yD_FOPEN "$fopen"
%token<fileline> yD_FSCANF "$fscanf"
%token<fileline> yD_FWRITE "$fwrite"
%token<fileline> yD_INFO "$info"
%token<fileline> yD_ISUNKNOWN "$isunknown"
@ -241,6 +242,7 @@ class AstSenTree;
%token<fileline> yD_READMEMB "$readmemb"
%token<fileline> yD_READMEMH "$readmemh"
%token<fileline> yD_SIGNED "$signed"
%token<fileline> yD_SSCANF "$sscanf"
%token<fileline> yD_STOP "$stop"
%token<fileline> yD_TIME "$time"
%token<fileline> yD_UNSIGNED "$unsigned"
@ -429,6 +431,7 @@ class AstSenTree;
%type<assignwp> gateBuf gateNot gateAnd gateNand gateOr gateNor gateXor gateXnor
%type<nodep> gateAndPinList gateOrPinList gateXorPinList
%type<nodep> commaEListE
%type<nodep> commaVRDListE vrdList
%type<nodep> pslStmt pslDir pslDirOne pslProp
%type<nodep> 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

View File

@ -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

View File

@ -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