Support , .
This commit is contained in:
parent
d962dfe48c
commit
0703843ac1
2
Changes
2
Changes
|
|
@ -5,7 +5,7 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||
|
||||
* Verilator 3.66*
|
||||
|
||||
*** Support $feof, $fflush. [Holger Waechtler]
|
||||
*** Support $feof, $fgetc, $fgets, $fflush. [Holger Waechtler]
|
||||
|
||||
*** Support $random.
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ DISTFILES_INC = $(INFOS) .gitignore COPYING *.in *.ac \
|
|||
test_verilated/vgen*.pl \
|
||||
test_regress/t/*.cpp \
|
||||
test_regress/t/*.h \
|
||||
test_regress/t/*.dat \
|
||||
test_regress/t/*.mem \
|
||||
test_regress/t/*.out \
|
||||
test_regress/t/*.pl \
|
||||
|
|
|
|||
|
|
@ -1554,7 +1554,7 @@ them with a $write with the appropriate format specifier.
|
|||
|
||||
The rarely used optional parameter to $finish and $stop is ignored.
|
||||
|
||||
=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fwrite
|
||||
=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fgetc, $fgets, $fwrite
|
||||
|
||||
File descriptors passed to the file PLI calls must be file descriptors, not
|
||||
MCDs, which includes the mode parameter to $fopen being mandatory.
|
||||
|
|
|
|||
|
|
@ -248,6 +248,34 @@ void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
|
|||
while (isspace(*(destp-1)) && destp>destoutp) *--destp = '\0'; // Drop trailing spaces
|
||||
}
|
||||
|
||||
IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq) {
|
||||
FILE* fp = VL_CVT_Q_FP(fpq);
|
||||
if (!fp) return 0;
|
||||
|
||||
// The string needs to be padded with 0's in unused spaces in front of
|
||||
// any read data. This means we can't know in what location the first
|
||||
// character will finally live, so we need to copy. Yuk.
|
||||
IData bytes = VL_BYTES_I(sbits);
|
||||
char buffer[bytes];
|
||||
|
||||
// We don't use fgets, as we must read \0s.
|
||||
IData got = 0;
|
||||
char* cp = buffer;
|
||||
while (got < bytes) {
|
||||
int c = getc(fp);
|
||||
if (c==EOF) break;
|
||||
*cp++ = c; got++;
|
||||
if (c=='\n') break;
|
||||
}
|
||||
|
||||
// Convert to Verilog format
|
||||
char* op = ((char*)(strgp));
|
||||
IData i;
|
||||
for (i=0; i<got; i++) { *op++ = buffer[got-1-i]; }
|
||||
for (; i<bytes; i++) { *op++ = 0; }
|
||||
return got;
|
||||
}
|
||||
|
||||
QData VL_FOPEN_QI(QData filename, IData mode) {
|
||||
IData fnw[2]; VL_SET_WQ(fnw, filename);
|
||||
return VL_FOPEN_WI(2, fnw, mode);
|
||||
|
|
|
|||
|
|
@ -199,6 +199,23 @@ inline const char* VL_VALUE_FORMATTED_I(int obits, char fmt, bool drop0, IData l
|
|||
}
|
||||
|
||||
/// File I/O
|
||||
extern IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq);
|
||||
inline IData VL_FGETS_IIQ(int, int bits, int, CData& strg, QData fpq) {
|
||||
return VL_FGETS_IXQ(bits, &strg, fpq);
|
||||
}
|
||||
inline IData VL_FGETS_IIQ(int, int bits, int, SData& strg, QData fpq) {
|
||||
return VL_FGETS_IXQ(bits, &strg, fpq);
|
||||
}
|
||||
inline IData VL_FGETS_IIQ(int, int bits, int, IData& strg, QData fpq) {
|
||||
return VL_FGETS_IXQ(bits, &strg, fpq);
|
||||
}
|
||||
inline IData VL_FGETS_IQQ(int, int bits, int, QData& strg, QData fpq) {
|
||||
return VL_FGETS_IXQ(bits, &strg, fpq);
|
||||
}
|
||||
inline IData VL_FGETS_IWQ(int, int bits, int, WDataOutP strgp, QData fpq) {
|
||||
return VL_FGETS_IXQ(bits, strgp, fpq);
|
||||
}
|
||||
|
||||
extern QData VL_FOPEN_WI(int fnwords, WDataInP ofilename, IData mode);
|
||||
extern QData VL_FOPEN_QI(QData ofilename, IData mode);
|
||||
inline QData VL_FOPEN_II(IData ofilename, IData mode) { return VL_FOPEN_QI(ofilename,mode); }
|
||||
|
|
|
|||
|
|
@ -110,10 +110,13 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
//=========================================================================
|
||||
// Integer size macros
|
||||
|
||||
#define VL_BYTESIZE 8 ///< Bits in a byte
|
||||
#define VL_WORDSIZE 32 ///< Bits in a word
|
||||
#define VL_QUADSIZE 64 ///< Bits in a quadword
|
||||
#define VL_WORDSIZE_LOG2 5 ///< log2(VL_WORDSIZE)
|
||||
|
||||
/// Bytes this number of bits needs (1 bit=1 byte)
|
||||
#define VL_BYTES_I(nbits) (((nbits)+(VL_BYTESIZE-1))/VL_BYTESIZE)
|
||||
/// Words this number of bits needs (1 bit=1 word)
|
||||
#define VL_WORDS_I(nbits) (((nbits)+(VL_WORDSIZE-1))/VL_WORDSIZE)
|
||||
|
||||
|
|
|
|||
|
|
@ -2118,6 +2118,21 @@ struct AstFEof : public AstNodeUniop {
|
|||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
||||
struct AstFGetC : public AstNodeUniop {
|
||||
AstFGetC(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {}
|
||||
virtual ~AstFGetC() {}
|
||||
virtual AstType type() const { return AstType::FEOF;}
|
||||
virtual AstNode* clone() { return new AstFGetC(*this); }
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; }
|
||||
virtual string emitVerilog() { return "%k$fgetc(%l)"; }
|
||||
virtual string emitOperator() { return "VL_FGETC"; }
|
||||
virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*64; }
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Binary ops
|
||||
|
||||
|
|
@ -2729,6 +2744,22 @@ struct AstReplicate : public AstNodeBiop {
|
|||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
struct AstFGetS : public AstNodeBiop {
|
||||
AstFGetS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {}
|
||||
virtual ~AstFGetS() {}
|
||||
virtual AstType type() const { return AstType::FEOF;}
|
||||
virtual AstNode* clone() { return new AstFGetS(*this); }
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; }
|
||||
virtual string emitVerilog() { return "%k$fgets(%l,%r)"; }
|
||||
virtual string emitOperator() { return "VL_FGETS"; }
|
||||
virtual bool cleanOut() {return false;}
|
||||
virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*64; }
|
||||
AstNode* strgp() const { return lhsp(); }
|
||||
AstNode* filep() const { return rhsp(); }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// SysVerilog assertions
|
||||
|
|
|
|||
|
|
@ -306,6 +306,13 @@ public:
|
|||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(")); ");
|
||||
}
|
||||
virtual void visit(AstFGetC* nodep, AstNUser*) {
|
||||
puts("(");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts("? fgetc(VL_CVT_Q_FP(");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(")) : -1)"); // Non-existant filehandle should return EOF
|
||||
}
|
||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
puts("while (");
|
||||
|
|
|
|||
|
|
@ -116,6 +116,23 @@ private:
|
|||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstFGetC* nodep, AstNUser*) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstFGetS* nodep, AstNUser*) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
nodep->strgp()->iterateAndNext(*this);
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -332,6 +332,14 @@ private:
|
|||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
}
|
||||
virtual void visit(AstFGetC* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
}
|
||||
virtual void visit(AstFGetS* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
}
|
||||
virtual void visit(AstDisplay* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ private:
|
|||
virtual void visit(AstOneHot* nodep,AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
virtual void visit(AstOneHot0* nodep,AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
virtual void visit(AstFEof* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
virtual void visit(AstFGetC* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
virtual void visit(AstFGetS* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
//
|
||||
virtual void visit(AstConcat* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
virtual void visit(AstReplicate* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
|
||||
|
|
|
|||
|
|
@ -576,12 +576,25 @@ private:
|
|||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
}
|
||||
virtual void visit(AstFEof* nodep, AstNUser*) {
|
||||
nodep->lhsp()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->width(1,1);
|
||||
}
|
||||
virtual void visit(AstFFlush* nodep, AstNUser*) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
}
|
||||
virtual void visit(AstFGetC* nodep, AstNUser* vup) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->width(32,8);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstFGetS* nodep, AstNUser* vup) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->strgp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->width(32,32);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
||||
nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
nodep->memp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ escid \\[^ \t\f\r\n]+
|
|||
"$fdisplay" {yylval.fileline = CRELINE(); return yD_FDISPLAY;}
|
||||
"$feof" {yylval.fileline = CRELINE(); return yD_FEOF;}
|
||||
"$fflush" {yylval.fileline = CRELINE(); return yD_FFLUSH;}
|
||||
"$fgetc" {yylval.fileline = CRELINE(); return yD_FGETC;}
|
||||
"$fgets" {yylval.fileline = CRELINE(); return yD_FGETS;}
|
||||
"$finish" {yylval.fileline = CRELINE(); return yD_FINISH;}
|
||||
"$fopen" {yylval.fileline = CRELINE(); return yD_FOPEN;}
|
||||
"$fullskew" {yylval.fileline = CRELINE(); return yaTIMINGSPEC;}
|
||||
|
|
|
|||
|
|
@ -228,6 +228,8 @@ class AstSenTree;
|
|||
%token<fileline> yD_FDISPLAY "$fdisplay"
|
||||
%token<fileline> yD_FEOF "$feof"
|
||||
%token<fileline> yD_FFLUSH "$fflush"
|
||||
%token<fileline> yD_FGETC "$fgetc"
|
||||
%token<fileline> yD_FGETS "$fgets"
|
||||
%token<fileline> yD_FINISH "$finish"
|
||||
%token<fileline> yD_FOPEN "$fopen"
|
||||
%token<fileline> yD_FWRITE "$fwrite"
|
||||
|
|
@ -1083,6 +1085,8 @@ exprNoStr: expr yP_OROR expr { $$ = new AstLogOr ($2,$1,$3); }
|
|||
| yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); }
|
||||
| yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); }
|
||||
| yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); }
|
||||
| yD_FGETC '(' expr ')' { $$ = new AstFGetC($1,$3); }
|
||||
| yD_FGETS '(' varRefDotBit ',' expr ')' { $$ = new AstFGetS($1,$3,$5); }
|
||||
| yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); }
|
||||
| yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); }
|
||||
| yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@
|
|||
module t;
|
||||
`verilator_file_descriptor file;
|
||||
|
||||
integer chars;
|
||||
reg [1*8:1] letterl;
|
||||
reg [8*8:1] letterq;
|
||||
reg [16*8:1] letterw;
|
||||
|
||||
initial begin
|
||||
// Display formatting
|
||||
`ifdef verilator
|
||||
|
|
@ -30,6 +35,7 @@ module t;
|
|||
`endif
|
||||
|
||||
begin
|
||||
// Check for opening errors
|
||||
file = $fopen("obj_dir/DOES_NOT_EXIST","r"); // The "r" is required so we get a FD not a MFD
|
||||
if (|file) $stop; // Should not exist, IE must return 0
|
||||
end
|
||||
|
|
@ -40,6 +46,37 @@ module t;
|
|||
$fclose(file);
|
||||
end
|
||||
|
||||
begin
|
||||
// Check read functions
|
||||
file = $fopen("t/t_sys_file_input.dat","r");
|
||||
if ($feof(file)) $stop;
|
||||
|
||||
// $fgetc
|
||||
if ($fgetc(file) != "h") $stop;
|
||||
if ($fgetc(file) != "i") $stop;
|
||||
if ($fgetc(file) != "\n") $stop;
|
||||
|
||||
// $fgets
|
||||
chars = $fgets(letterl, file);
|
||||
$write("c=%0d l=%s\n", chars, letterl);
|
||||
if (chars != 1) $stop;
|
||||
if (letterl != "l") $stop;
|
||||
|
||||
chars = $fgets(letterq, file);
|
||||
$write("c=%0d q=%x=%s", chars, letterq, letterq); // Output includes newline
|
||||
if (chars != 5) $stop;
|
||||
if (letterq != "\0\0\0quad\n") $stop;
|
||||
|
||||
letterw = "5432109876543210";
|
||||
chars = $fgets(letterw, file);
|
||||
$write("c=%0d w=%s", chars, letterw); // Output includes newline
|
||||
if (chars != 10) $stop;
|
||||
if (letterw != "\0\0\0\0\0\0widestuff\n") $stop;
|
||||
|
||||
$fclose(file);
|
||||
end
|
||||
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish(0); // Test arguments to finish
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
hi
|
||||
lquad
|
||||
widestuff
|
||||
Loading…
Reference in New Issue