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

This commit is contained in:
Stephen Henry 2020-05-14 23:03:00 +01:00 committed by GitHub
parent 38d11ecabe
commit 1a0da2e4ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 344 additions and 89 deletions

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 {

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

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;

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;
}
};

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) {

View File

@ -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

View File

@ -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(");

View File

@ -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); }

View File

@ -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 {

View File

@ -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;
{

View File

@ -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));

View File

@ -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);

View File

@ -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 }

View File

@ -0,0 +1,2 @@
Sean Connery was the best Bond.
*-* All Finished *-*

View 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;

View 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

View 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.

View 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.

View 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.