Support $fopen and I/O with integer instead of `verilator_file_descriptor.
This commit is contained in:
parent
6e41d532fe
commit
a901e171b2
2
Changes
2
Changes
|
|
@ -7,6 +7,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||
|
||||
*** Support disable for loop escapes.
|
||||
|
||||
*** Support $fopen and I/O with integer instead of `verilator_file_descriptor.
|
||||
|
||||
**** Use 'vluint64_t' for SystemC instead of (same sized) 'uint64' for MSVC++.
|
||||
|
||||
* Verilator 3.813 2011/06/28
|
||||
|
|
|
|||
|
|
@ -2313,11 +2313,6 @@ The rarely used optional parameter to $finish and $stop is ignored.
|
|||
|
||||
File descriptors passed to the file PLI calls must be file descriptors, not
|
||||
MCDs, which includes the mode parameter to $fopen being mandatory.
|
||||
Verilator will convert the integer used to hold the file descriptor into a
|
||||
internal FILE*. To prevent core dumps due to mis-use, and because integers
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#define _VERILATED_CPP_
|
||||
#include "verilated_imp.h"
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
|
||||
#define VL_VALUE_STRING_MAX_WIDTH 1024 ///< Max static char array for VL_VALUE_STRING
|
||||
|
||||
|
|
@ -628,6 +627,10 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
|||
//===========================================================================
|
||||
// File I/O
|
||||
|
||||
FILE* VL_CVT_I_FP(IData lhs) {
|
||||
return VerilatedImp::fdToFp(lhs);
|
||||
}
|
||||
|
||||
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
|
||||
// See also VL_DATA_TO_STRING_NW
|
||||
int lsb=obits-1;
|
||||
|
|
@ -655,8 +658,8 @@ void _VL_STRING_TO_VINT(int obits, void* destp, int srclen, const char* srcp) {
|
|||
for (; i<bytes; i++) { *op++ = 0; }
|
||||
}
|
||||
|
||||
IData VL_FGETS_IXQ(int obits, void* destp, QData fpq) {
|
||||
FILE* fp = VL_CVT_Q_FP(fpq);
|
||||
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) {
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
// The string needs to be padded with 0's in unused spaces in front of
|
||||
|
|
@ -692,7 +695,13 @@ QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) {
|
|||
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
|
||||
char modez[5];
|
||||
_VL_VINT_TO_STRING(VL_WORDSIZE, modez, &mode);
|
||||
return VL_CVT_FP_Q(fopen(filenamez,modez));
|
||||
return VerilatedImp::fdNew(fopen(filenamez,modez));
|
||||
}
|
||||
void VL_FCLOSE_I(IData fdi) {
|
||||
FILE* fp = VL_CVT_I_FP(fdi);
|
||||
if (VL_UNLIKELY(!fp)) return;
|
||||
fclose(fp);
|
||||
VerilatedImp::fdDelete(fdi);
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) {
|
||||
|
|
@ -726,8 +735,8 @@ void VL_WRITEF(const char* formatp, ...) {
|
|||
VL_PRINTF("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_FWRITEF(QData fpq, const char* formatp, ...) {
|
||||
FILE* fp = VL_CVT_Q_FP(fpq);
|
||||
void VL_FWRITEF(IData fpi, const char* formatp, ...) {
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return;
|
||||
|
||||
va_list ap;
|
||||
|
|
@ -739,8 +748,8 @@ 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);
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) {
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
va_list ap;
|
||||
|
|
|
|||
|
|
@ -364,13 +364,13 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
|||
#define VL_SET_QW(lwp) ( ((QData)(lwp[0])) | ((QData)(lwp[1])<<((QData)(VL_WORDSIZE)) ))
|
||||
#define _VL_SET_QII(ld,rd) ( ((QData)(ld)<<VL_ULL(32)) | (QData)(rd) )
|
||||
|
||||
/// Return FILE* from IData
|
||||
FILE* VL_CVT_I_FP(IData lhs);
|
||||
/// Return IData from FILE*
|
||||
IData VL_CVT_FP_I(FILE* fp);
|
||||
// Use a union to avoid cast-to-different-size warnings
|
||||
/// Return FILE* from QData
|
||||
static inline FILE* VL_CVT_Q_FP(QData lhs) { union { FILE* fp; QData q; } u; u.q=lhs; return u.fp; }
|
||||
/// Return void* from QData
|
||||
static inline void* VL_CVT_Q_VP(QData lhs) { union { void* fp; QData q; } u; u.q=lhs; return u.fp; }
|
||||
/// Return QData from FILE*
|
||||
static inline QData VL_CVT_FP_Q(FILE* fp) { union { FILE* fp; QData q; } u; u.q=0; u.fp=fp; return u.q; }
|
||||
/// Return QData from void*
|
||||
static inline QData VL_CVT_VP_Q(void* fp) { union { void* fp; QData q; } u; u.q=0; u.fp=fp; return u.q; }
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,7 @@
|
|||
`define coverage_block_off
|
||||
`endif
|
||||
|
||||
// Hide file descriptor difference
|
||||
`ifdef verilator
|
||||
`define verilator_file_descriptor reg [63:0]
|
||||
`else
|
||||
`define verilator_file_descriptor integer
|
||||
`endif
|
||||
// Hide file descriptor difference - deprecated - for older versions
|
||||
`define verilator_file_descriptor integer
|
||||
|
||||
`endif // guard
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
class VerilatedScope;
|
||||
|
|
@ -62,9 +63,18 @@ class VerilatedImp {
|
|||
ExportNameMap m_exportMap; ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext; ///< Next export funcnum
|
||||
|
||||
// File I/O
|
||||
vector<FILE*> m_fdps; ///< File descriptors
|
||||
deque<IData> m_fdFree; ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
VerilatedImp() : m_argVecLoaded(false), m_exportNext(0) {}
|
||||
VerilatedImp() : m_argVecLoaded(false), m_exportNext(0) {
|
||||
m_fdps.resize(3);
|
||||
m_fdps[0] = stdin;
|
||||
m_fdps[1] = stdout;
|
||||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
|
||||
// METHODS - arguments
|
||||
|
|
@ -184,6 +194,34 @@ public: // But only for verilated*.cpp
|
|||
}
|
||||
// We don't free up m_exportMap until the end, because we can't be sure
|
||||
// what other models are using the assigned funcnum's.
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) {
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
s_s.m_fdps.resize(start*2);
|
||||
for (size_t i=start; i<start*2; i++) s_s.m_fdFree.push_back((IData)i);
|
||||
}
|
||||
IData idx = s_s.m_fdFree.back(); s_s.m_fdFree.pop_back();
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
}
|
||||
static void fdDelete(IData fdi) {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
s_s.m_fdps[idx] = NULL;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
}
|
||||
static inline FILE* fdToFp(IData fdi) {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -2697,7 +2697,7 @@ struct AstFEof : public AstNodeUniop {
|
|||
ASTNODE_NODE_FUNCS(FEof, FEOF)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; }
|
||||
virtual string emitVerilog() { return "%f$feof(%l)"; }
|
||||
virtual string emitC() { return "(%li ? feof(VL_CVT_Q_FP(%li)) : true)"; }
|
||||
virtual string emitC() { return "(%li ? feof(VL_CVT_I_FP(%li)) : true)"; }
|
||||
virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*16; }
|
||||
|
|
@ -2710,7 +2710,7 @@ struct AstFGetC : public AstNodeUniop {
|
|||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; }
|
||||
virtual string emitVerilog() { return "%f$fgetc(%l)"; }
|
||||
// Non-existent filehandle returns EOF
|
||||
virtual string emitC() { return "(%li ? fgetc(VL_CVT_Q_FP(%li)) : -1)"; }
|
||||
virtual string emitC() { return "(%li ? fgetc(VL_CVT_I_FP(%li)) : -1)"; }
|
||||
virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*64; }
|
||||
|
|
|
|||
|
|
@ -344,13 +344,11 @@ public:
|
|||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFClose* nodep, AstNUser*) {
|
||||
puts("if (");
|
||||
puts("VL_FCLOSE_I(");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(") { fclose (VL_CVT_Q_FP(");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(")); ");
|
||||
puts("); ");
|
||||
nodep->filep()->iterateAndNext(*this); // For saftey, so user doesn't later WRITE with it.
|
||||
puts("=0; }\n");
|
||||
puts("=0;\n");
|
||||
}
|
||||
virtual void visit(AstFFlush* nodep, AstNUser*) {
|
||||
if (!nodep->filep()) {
|
||||
|
|
@ -358,7 +356,7 @@ public:
|
|||
} else {
|
||||
puts("if (");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(") { fflush (VL_CVT_Q_FP(");
|
||||
puts(") { fflush (VL_CVT_I_FP(");
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
puts(")); }\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -759,55 +759,55 @@ private:
|
|||
}
|
||||
virtual void visit(AstDisplay* nodep, AstNUser*) {
|
||||
if (nodep->filep()) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
// Just let all arguments seek their natural sizes
|
||||
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
}
|
||||
virtual void visit(AstFOpen* nodep, AstNUser*) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
nodep->modep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstFClose* nodep, AstNUser*) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstFEof* nodep, AstNUser*) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
nodep->width(1,1);
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstFFlush* nodep, AstNUser*) {
|
||||
if (nodep->filep()) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstFGetC* nodep, AstNUser* vup) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->width(32,8);
|
||||
}
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstFGetS* nodep, AstNUser* vup) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
nodep->strgp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->width(32,32);
|
||||
}
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstFScanF* nodep, AstNUser* vup) {
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
||||
nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p());
|
||||
nodep->exprsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->width(32,32);
|
||||
}
|
||||
widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64);
|
||||
widthCheck(nodep,"file_descriptor",nodep->filep(),32,32);
|
||||
}
|
||||
virtual void visit(AstSScanF* nodep, AstNUser* vup) {
|
||||
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
|
|
|
|||
|
|
@ -2497,11 +2497,7 @@ module t_case_write1_tasks ();
|
|||
endtask
|
||||
|
||||
task big_case;
|
||||
`ifdef verilator
|
||||
input [ 63:0] fd;
|
||||
`else
|
||||
input [ 31:0] fd;
|
||||
`endif
|
||||
input [ 31:0] foo;
|
||||
reg [STRLEN*8: 1] foobar;
|
||||
// verilator no_inline_task
|
||||
|
|
|
|||
|
|
@ -10,11 +10,7 @@ module t_case_write2_tasks ();
|
|||
// verilator lint_off WIDTH
|
||||
// verilator lint_off CASEINCOMPLETE
|
||||
|
||||
`ifdef verilator
|
||||
`define FD_BITS 63:0
|
||||
`else
|
||||
`define FD_BITS 31:0
|
||||
`endif
|
||||
|
||||
parameter STRLEN = 78;
|
||||
task ozonerab;
|
||||
|
|
|
|||
Loading…
Reference in New Issue