Support multi-channel descriptor (MCD) I/O (#2197)

This commit is contained in:
Stephen Henry
2020-05-14 18:03:00 -04:00
committed by GitHub
parent 38d11ecabe
commit 1a0da2e4ec
19 changed files with 344 additions and 89 deletions
+27 -31
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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;
}
};
+1 -1
View File
@@ -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) {