mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Support multi-channel descriptor (MCD) I/O (#2197)
This commit is contained in:
parent
38d11ecabe
commit
1a0da2e4ec
@ -1141,7 +1141,13 @@ done:
|
||||
//===========================================================================
|
||||
// File I/O
|
||||
|
||||
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE { return VerilatedImp::fdToFp(lhs); }
|
||||
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
|
||||
// Expected non-MCD case; returns ONLY the first file descriptor seen in lhs (which
|
||||
// in the MCD case can result in descriptors being ignored).
|
||||
FILE* fp[1] = {NULL};
|
||||
VerilatedImp::fdToFp(lhs, fp, 1);
|
||||
return fp[0];
|
||||
}
|
||||
|
||||
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
|
||||
// See also VL_DATA_TO_STRING_NW
|
||||
@ -1209,38 +1215,25 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
|
||||
return ret;
|
||||
}
|
||||
|
||||
IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
char modez[5];
|
||||
EData modee = mode;
|
||||
_VL_VINT_TO_STRING(VL_IDATASIZE, modez, &modee);
|
||||
return VL_FOPEN_S(filename.c_str(), modez);
|
||||
IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) {
|
||||
return VerilatedImp::fdNew(filename.c_str(), mode.c_str());
|
||||
}
|
||||
IData VL_FOPEN_QI(QData filename, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
WData fnw[VL_WQ_WORDS_E];
|
||||
VL_SET_WQ(fnw, filename);
|
||||
return VL_FOPEN_WI(VL_WQ_WORDS_E, fnw, mode);
|
||||
}
|
||||
IData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
_VL_VINT_TO_STRING(fnwords * VL_EDATASIZE, filenamez, filenamep);
|
||||
EData modee = mode;
|
||||
char modez[5];
|
||||
_VL_VINT_TO_STRING(4 * sizeof(char), modez, &modee);
|
||||
return VL_FOPEN_S(filenamez, modez);
|
||||
}
|
||||
IData VL_FOPEN_S(const char* filenamep, const char* modep) VL_MT_SAFE {
|
||||
return VerilatedImp::fdNew(fopen(filenamep, modep));
|
||||
IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE {
|
||||
return VerilatedImp::fdNewMcd(filename.c_str());
|
||||
}
|
||||
|
||||
void VL_FFLUSH_I(IData fdi) VL_MT_SAFE {
|
||||
VerilatedImp::fdFlush(fdi);
|
||||
}
|
||||
IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE {
|
||||
return VerilatedImp::fdSeek(fdi, offset, origin);
|
||||
}
|
||||
IData VL_FTELL_I(IData fdi) VL_MT_SAFE {
|
||||
return VerilatedImp::fdTell(fdi);
|
||||
}
|
||||
void VL_FCLOSE_I(IData fdi) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fdi);
|
||||
if (VL_UNLIKELY(!fp)) return;
|
||||
fclose(fp);
|
||||
VerilatedImp::fdDelete(fdi);
|
||||
VerilatedImp::fdClose(fdi);
|
||||
}
|
||||
|
||||
void VL_FFLUSH_ALL() VL_MT_SAFE { fflush(stdout); }
|
||||
@ -1335,15 +1328,18 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
_vl_vsformat(output, formatp, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputs(output.c_str(), fp);
|
||||
FILE* fp[30];
|
||||
const int n = VerilatedImp::fdToFp(fpi, fp, 30);
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
if (VL_UNLIKELY(!fp[i])) continue;
|
||||
fputs(output.c_str(), fp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
|
@ -673,13 +673,9 @@ extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP r
|
||||
/// File I/O
|
||||
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi);
|
||||
|
||||
extern IData VL_FOPEN_S(const char* filenamep, const char* modep);
|
||||
extern IData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode);
|
||||
extern IData VL_FOPEN_QI(QData filename, IData mode);
|
||||
inline IData VL_FOPEN_II(IData filename, IData mode) VL_MT_SAFE {
|
||||
return VL_FOPEN_QI(filename, mode);
|
||||
}
|
||||
|
||||
extern void VL_FFLUSH_I(IData fdi);
|
||||
extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin);
|
||||
extern IData VL_FTELL_I(IData fdi);
|
||||
extern void VL_FCLOSE_I(IData fdi);
|
||||
|
||||
extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi,
|
||||
@ -737,7 +733,7 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
||||
#define _VL_SET_QII(ld, rd) ((static_cast<QData>(ld) << VL_ULL(32)) | static_cast<QData>(rd))
|
||||
|
||||
/// Return FILE* from IData
|
||||
extern FILE* VL_CVT_I_FP(IData lhs);
|
||||
extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE;
|
||||
|
||||
// clang-format off
|
||||
// Use a union to avoid cast-to-different-size warnings
|
||||
|
@ -427,7 +427,8 @@ extern std::string VL_TOLOWER_NN(const std::string& ld);
|
||||
extern std::string VL_TOUPPER_NN(const std::string& ld);
|
||||
|
||||
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#ifdef VL_THREADED
|
||||
# include <functional>
|
||||
# include <queue>
|
||||
@ -229,17 +230,19 @@ protected:
|
||||
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
|
||||
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
|
||||
/// List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
std::deque<IData> m_fdFree VL_GUARDED_BY(m_fdMutex);
|
||||
std::vector<IData> m_fdFree VL_GUARDED_BY(m_fdMutex);
|
||||
// List of free descriptors in the MCT region [4, 32)
|
||||
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
VerilatedImp()
|
||||
: m_argVecLoaded(false)
|
||||
, m_exportNext(0) {
|
||||
m_fdps.resize(3);
|
||||
m_fdps[0] = stdin;
|
||||
m_fdps[1] = stdout;
|
||||
m_fdps[2] = stderr;
|
||||
s_s.m_fdps.resize(31);
|
||||
std::fill(s_s.m_fdps.begin(), s_s.m_fdps.end(), (FILE*)0);
|
||||
s_s.m_fdFreeMct.resize(30);
|
||||
std::iota(s_s.m_fdFreeMct.begin(), s_s.m_fdFreeMct.end(), 1);
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
|
||||
@ -448,36 +451,96 @@ public: // But only for verilated*.cpp
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) VL_MT_SAFE {
|
||||
static IData fdNewMcd(const char* filenamep) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (s_s.m_fdFreeMct.empty()) return 0;
|
||||
IData idx = s_s.m_fdFreeMct.back();
|
||||
s_s.m_fdFreeMct.pop_back();
|
||||
s_s.m_fdps[idx] = fopen(filenamep, "w");
|
||||
if(VL_UNLIKELY(!s_s.m_fdps[idx])) return 0;
|
||||
return (1 << idx);
|
||||
}
|
||||
static IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE {
|
||||
FILE* fp = fopen(filenamep, modep);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
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(static_cast<IData>(i));
|
||||
}
|
||||
const size_t start = std::max(31ul + 1ul + 3ul, s_s.m_fdps.size());
|
||||
const size_t excess = 10;
|
||||
s_s.m_fdps.resize(start + excess);
|
||||
std::fill(s_s.m_fdps.begin() + start, s_s.m_fdps.end(), (FILE*)0);
|
||||
s_s.m_fdFree.resize(excess);
|
||||
std::iota(s_s.m_fdFree.begin(), s_s.m_fdFree.end(), start);
|
||||
}
|
||||
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) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
static void fdFlush(IData fdi) VL_MT_SAFE {
|
||||
FILE* fp[30];
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (VL_UNLIKELY(!(fdi & (VL_ULL(1) << 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);
|
||||
const int n = fdToFp(fdi, fp, 30);
|
||||
for (int i = 0; i < n; i++) fflush(fp[i]);
|
||||
}
|
||||
static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex); // This might get slow, if it does we can cache it
|
||||
if (VL_UNLIKELY(!(fdi & (VL_ULL(1) << 31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
static IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE {
|
||||
FILE* fp;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
const int n = fdToFp(fdi, &fp);
|
||||
if(VL_UNLIKELY(!fp || (n != 1))) return 0;
|
||||
return static_cast<IData>(fseek(fp, static_cast<long>(offset), static_cast<int>(origin)));
|
||||
}
|
||||
static IData fdTell(IData fdi) VL_MT_SAFE {
|
||||
FILE* fp;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
const int n = fdToFp(fdi, &fp);
|
||||
if(VL_UNLIKELY(!fp || (n != 1))) return 0;
|
||||
return static_cast<IData>(ftell(fp));
|
||||
}
|
||||
static void fdClose(IData fdi) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if ((fdi & (1 << 31)) != 0) {
|
||||
// Non-MCD case
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
fclose(s_s.m_fdps[idx]);
|
||||
s_s.m_fdps[idx] = (FILE*)0;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
} else {
|
||||
// MCD case
|
||||
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) {
|
||||
fclose(s_s.m_fdps[i]);
|
||||
s_s.m_fdps[i] = NULL;
|
||||
s_s.m_fdFreeMct.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline int fdToFp(IData fdi, FILE** fp, std::size_t max = 1) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!fp || (max == 0))) return 0;
|
||||
int out = 0;
|
||||
if ((fdi & (1 << 31)) != 0) {
|
||||
// Non-MCD case
|
||||
IData idx = fdi & VL_MASK_I(31);
|
||||
switch (idx) {
|
||||
case 0: fp[out++] = stdin; break;
|
||||
case 1: fp[out++] = stdout; break;
|
||||
case 2: fp[out++] = stderr; break;
|
||||
default:
|
||||
if (VL_LIKELY(idx < s_s.m_fdps.size()))
|
||||
fp[out++] = s_s.m_fdps[idx];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// MCD Case
|
||||
for (int i = 0; (fdi != 0) && (out < max) && (i < 31); i++, fdi >>= 1)
|
||||
if (fdi & VL_MASK_I(1)) fp[out++] = s_s.m_fdps[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1937,7 +1937,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
PLI_UINT32 vpi_mcd_open(PLI_BYTE8* filenamep) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET();
|
||||
return VL_FOPEN_S(filenamep, "wb");
|
||||
return VL_FOPEN_NN(filenamep, "wb");
|
||||
}
|
||||
|
||||
PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
|
||||
|
@ -3801,6 +3801,27 @@ public:
|
||||
AstNode* modep() const { return op3p(); }
|
||||
};
|
||||
|
||||
class AstFOpenMcd : public AstNodeStmt {
|
||||
// Although a system function in IEEE, here a statement which sets the file pointer (MCD)
|
||||
public:
|
||||
AstFOpenMcd(FileLine* fl, AstNode* filep, AstNode* filenamep)
|
||||
: ASTGEN_SUPER(fl) {
|
||||
setOp1p(filep);
|
||||
setOp2p(filenamep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(FOpenMcd)
|
||||
virtual string verilogKwd() const { return "$fopen"; }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool isPure() const { return false; }
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* filep() const { return op1p(); }
|
||||
AstNode* filenamep() const { return op2p(); }
|
||||
};
|
||||
|
||||
class AstFFlush : public AstNodeStmt {
|
||||
// Parents: stmtlist
|
||||
// Children: file which must be a varref
|
||||
|
@ -526,20 +526,18 @@ public:
|
||||
}
|
||||
virtual void visit(AstFOpen* nodep) VL_OVERRIDE {
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts(" = VL_FOPEN_");
|
||||
emitIQW(nodep->filenamep());
|
||||
emitIQW(nodep->modep());
|
||||
puts(" = VL_FOPEN_NN(");
|
||||
emitCvtPackStr(nodep->filenamep());
|
||||
putbs(", ");
|
||||
if (nodep->modep()->width() > 4 * 8)
|
||||
nodep->modep()->v3error("$fopen mode should be <= 4 characters");
|
||||
puts("(");
|
||||
if (nodep->filenamep()->isWide()) {
|
||||
puts(cvtToStr(nodep->filenamep()->widthWords()));
|
||||
putbs(", ");
|
||||
}
|
||||
checkMaxWords(nodep->filenamep());
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
putbs(", ");
|
||||
iterateAndNextNull(nodep->modep());
|
||||
emitCvtPackStr(nodep->modep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts(" = VL_FOPEN_MCD_N(");
|
||||
emitCvtPackStr(nodep->filenamep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstNodeReadWriteMem* nodep) VL_OVERRIDE {
|
||||
@ -599,29 +597,29 @@ public:
|
||||
} else {
|
||||
puts("if (");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts(") { fflush(VL_CVT_I_FP(");
|
||||
puts(") { VL_FFLUSH_I(");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts(")); }\n");
|
||||
puts("); }\n");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstFSeek* nodep) VL_OVERRIDE {
|
||||
puts("(fseek(VL_CVT_I_FP(");
|
||||
puts("(VL_FSEEK_I(");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts("),");
|
||||
puts(",");
|
||||
iterateAndNextNull(nodep->offset());
|
||||
puts(",");
|
||||
iterateAndNextNull(nodep->operation());
|
||||
puts(")==-1?-1:0)");
|
||||
}
|
||||
virtual void visit(AstFTell* nodep) VL_OVERRIDE {
|
||||
puts("ftell(VL_CVT_I_FP(");
|
||||
puts("VL_FTELL_I(");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts("))");
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstFRewind* nodep) VL_OVERRIDE {
|
||||
puts("(fseek(VL_CVT_I_FP(");
|
||||
puts("(VL_FSEEK_I(");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
puts("), 0, 0)==-1?-1:0)");
|
||||
puts(", 0, 0)==-1?-1:0)");
|
||||
}
|
||||
virtual void visit(AstFRead* nodep) VL_OVERRIDE {
|
||||
puts("VL_FREAD_I(");
|
||||
|
@ -100,6 +100,14 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstFOpen* nodep) VL_OVERRIDE {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
||||
|
@ -252,12 +252,16 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
}
|
||||
virtual void visit(AstFOpen* nodep) VL_OVERRIDE {
|
||||
putfs(nodep, nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) iterateAndNextNull(nodep->filep());
|
||||
putbs("(");
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
putbs(",");
|
||||
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
|
||||
putbs(",");
|
||||
if (nodep->modep()) iterateAndNextNull(nodep->modep());
|
||||
iterateAndNextNull(nodep->modep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
putfs(nodep, nodep->verilogKwd());
|
||||
putbs("(");
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) VL_OVERRIDE {
|
||||
|
@ -91,6 +91,16 @@ private:
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
iterateAndNextNull(nodep->filep());
|
||||
m_setRefLvalue = false;
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) VL_OVERRIDE {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
|
@ -397,6 +397,10 @@ private:
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
|
@ -3317,6 +3317,11 @@ private:
|
||||
userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
|
||||
userIterateAndNext(nodep->modep(), WidthVP(SELF, BOTH).p());
|
||||
}
|
||||
virtual void visit(AstFOpenMcd* nodep) VL_OVERRIDE {
|
||||
assertAtStatement(nodep);
|
||||
iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
|
||||
userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) VL_OVERRIDE {
|
||||
assertAtStatement(nodep);
|
||||
iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
|
||||
|
@ -2936,7 +2936,7 @@ statementVerilatorPragmas<nodep>:
|
||||
|
||||
foperator_assignment<nodep>: // IEEE: operator_assignment (for first part of expression)
|
||||
fexprLvalue '=' delayE expr { $$ = new AstAssign($2,$1,$4); }
|
||||
| fexprLvalue '=' yD_FOPEN '(' expr ')' { $$ = NULL; BBUNSUP($3, "Unsupported: $fopen with multichannel descriptor. Add ,\"w\" as second argument to open a file descriptor."); }
|
||||
| fexprLvalue '=' yD_FOPEN '(' expr ')' { $$ = new AstFOpenMcd($3,$1,$5); }
|
||||
| fexprLvalue '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen($3,$1,$5,$7); }
|
||||
//
|
||||
//UNSUP ~f~exprLvalue '=' delay_or_event_controlE expr { UNSUP }
|
||||
|
2
test_regress/t/t_sys_file_basic_mcd.out
Normal file
2
test_regress/t/t_sys_file_basic_mcd.out
Normal file
@ -0,0 +1,2 @@
|
||||
Sean Connery was the best Bond.
|
||||
*-* All Finished *-*
|
29
test_regress/t/t_sys_file_basic_mcd.pl
Executable file
29
test_regress/t/t_sys_file_basic_mcd.pl
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
unlink("$Self->{obj_dir}/t_sys_file_basic_mcd.log");
|
||||
|
||||
compile();
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
files_identical("$Self->{obj_dir}/t_sys_file_basic_mcd_test2_0.dat",
|
||||
"$Self->{t_dir}/t_sys_file_basic_mcd_test2_0.dat");
|
||||
files_identical("$Self->{obj_dir}/t_sys_file_basic_mcd_test2_1.dat",
|
||||
"$Self->{t_dir}/t_sys_file_basic_mcd_test2_1.dat");
|
||||
files_identical("$Self->{obj_dir}/t_sys_file_basic_mcd_test2_2.dat",
|
||||
"$Self->{t_dir}/t_sys_file_basic_mcd_test2_2.dat");
|
||||
|
||||
ok(1);
|
||||
1;
|
109
test_regress/t/t_sys_file_basic_mcd.v
Normal file
109
test_regress/t/t_sys_file_basic_mcd.v
Normal file
@ -0,0 +1,109 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2020 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
module t;
|
||||
`define STR(__s) `"__s`"
|
||||
|
||||
task automatic fail(string s); begin
|
||||
$display({"FAIL! Reason: ", s});
|
||||
$stop;
|
||||
end endtask
|
||||
|
||||
task automatic test1; begin
|
||||
int fd[30], fd_fail, fd_success, fd_close, tmp;
|
||||
for (int i = 0; i < 30; i++) begin
|
||||
// Attempt to allocate 30 MCD descriptors; returned descriptors
|
||||
// should fall within correct range: [1, 30].
|
||||
tmp = $fopen($sformatf("%s/some_file%0d.dat", `STR(`TEST_OBJ_DIR), i));
|
||||
fd[i] = tmp;
|
||||
if ((fd[i] == 0) || !$onehot(fd[i]))
|
||||
fail($sformatf("MCD descriptor out of range %d", fd[i]));
|
||||
end
|
||||
// Attempt to allocate another MCD descriptor when all should
|
||||
// be used. We expect this operation to fail and return the
|
||||
// invalid descriptor (0).
|
||||
fd_fail = $fopen($sformatf("%s/another_file.dat", `STR(`TEST_OBJ_DIR)));
|
||||
if (fd_fail != 0)
|
||||
fail("Able to allocate MCD descriptor when fully utilized.");
|
||||
// Return descriptor back to pool
|
||||
fd_close = fd[0];
|
||||
$fclose(fd_close);
|
||||
// Re-attempt MCD allocation; should pass at this point.
|
||||
fd_success = $fopen($sformatf("%s/yet_another_file.dat", `STR(`TEST_OBJ_DIR)));
|
||||
if (fd_success == 0)
|
||||
fail("Expect to have free descriptors at this point.");
|
||||
// Returned descriptor should have a value matching that which
|
||||
// had previously just been returned back to the pool.
|
||||
if (fd_success != fd[0])
|
||||
fail("Descriptor has incorrect value.");
|
||||
// Return all descriptors back to the pool.
|
||||
for (int i = 1; i < 30; i++) begin
|
||||
fd_close = fd[i];
|
||||
$fclose(fd_close);
|
||||
end
|
||||
end endtask
|
||||
|
||||
task automatic test2; begin
|
||||
// Validate basic MCD functionality.
|
||||
|
||||
integer fd[3], fd_all, tmp;
|
||||
for (int i = 0; i < 3; i++) begin
|
||||
|
||||
tmp = $fopen($sformatf("%s/t_sys_file_basic_mcd_test2_%0d.dat", `STR(`TEST_OBJ_DIR), i));
|
||||
fd[i] = tmp;
|
||||
end
|
||||
|
||||
fd_all = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
fd_all |= fd[i];
|
||||
|
||||
$fwrite(fd_all, "Scotland is the greatest country.\n");
|
||||
$fwrite(fd_all, "All other countries are inferior.\n");
|
||||
$fwrite(fd_all, "Woe betide those to stand against the mighty Scottish nation.\n");
|
||||
$fclose(fd_all);
|
||||
end endtask
|
||||
|
||||
task automatic test3; begin
|
||||
// Write some things to standard output.
|
||||
$fwrite(32'h8000_0001, "Sean Connery was the best Bond.\n");
|
||||
end endtask
|
||||
|
||||
task automatic test4; begin
|
||||
int fd;
|
||||
// Small
|
||||
fd = $fopen("a");
|
||||
if (fd == 0) fail("Small filename could not be opened.");
|
||||
$fclose(fd);
|
||||
// Large
|
||||
fd = $fopen({"some_very_large_filename_that_no_one_would_ever_use_",
|
||||
"except_to_purposefully_break_my_beautiful_code_",
|
||||
"in a regression setting.dat"});
|
||||
if (fd == 0) fail("Long filename could not be opened.");
|
||||
$fclose(fd);
|
||||
end endtask
|
||||
|
||||
initial begin
|
||||
|
||||
// Test1: Validate file descriptor region.
|
||||
test1;
|
||||
|
||||
// Test2: Validate basic MCD functionality.
|
||||
test2;
|
||||
|
||||
// Test3: Validate explicit descriptor ID
|
||||
test3;
|
||||
|
||||
// Test4: Validate filename lengths
|
||||
test4;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish(0); // Test arguments to finish
|
||||
|
||||
end // initial begin
|
||||
|
||||
`undef STR
|
||||
endmodule // t
|
3
test_regress/t/t_sys_file_basic_mcd_test2_0.dat
Normal file
3
test_regress/t/t_sys_file_basic_mcd_test2_0.dat
Normal file
@ -0,0 +1,3 @@
|
||||
Scotland is the greatest country.
|
||||
All other countries are inferior.
|
||||
Woe betide those to stand against the mighty Scottish nation.
|
3
test_regress/t/t_sys_file_basic_mcd_test2_1.dat
Normal file
3
test_regress/t/t_sys_file_basic_mcd_test2_1.dat
Normal file
@ -0,0 +1,3 @@
|
||||
Scotland is the greatest country.
|
||||
All other countries are inferior.
|
||||
Woe betide those to stand against the mighty Scottish nation.
|
3
test_regress/t/t_sys_file_basic_mcd_test2_2.dat
Normal file
3
test_regress/t/t_sys_file_basic_mcd_test2_2.dat
Normal file
@ -0,0 +1,3 @@
|
||||
Scotland is the greatest country.
|
||||
All other countries are inferior.
|
||||
Woe betide those to stand against the mighty Scottish nation.
|
Loading…
Reference in New Issue
Block a user