mirror of
https://github.com/verilator/verilator.git
synced 2026-09-02 02:38:15 +02:00
Support multi-channel descriptor (MCD) I/O (#2197)
This commit is contained in:
+27
-31
@@ -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 {
|
||||
|
||||
+4
-8
@@ -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;
|
||||
|
||||
+85
-22
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user