forked from github/verilator
Support / with assoc arrarys. Closes #2100.
This commit is contained in:
parent
7bed17b14b
commit
918df2e618
2
Changes
2
Changes
@ -9,6 +9,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
** Add -match to lint_off to waive warnings. [Philipp Wagner]
|
||||
|
||||
*** Support $readmem/$writemem with assoc arrarys. Closes #2100. [agrobman]
|
||||
|
||||
**** Add error on misused define. [Topa Tota]
|
||||
|
||||
|
||||
|
@ -1320,139 +1320,6 @@ IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_
|
||||
return got;
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_Q(bool hex, int width, QData depth, int array_lsb, int,
|
||||
QData filename, const void* memp, QData start,
|
||||
QData end) VL_MT_SAFE {
|
||||
WData fnw[VL_WQ_WORDS_E]; VL_SET_WQ(fnw, filename);
|
||||
return VL_WRITEMEM_W(hex, width, depth, array_lsb, VL_WQ_WORDS_E, fnw, memp, start, end);
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_W(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
WDataInP filenamep, const void* memp, QData start,
|
||||
QData end) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
_VL_VINT_TO_STRING(fnwords * VL_EDATASIZE, filenamez, filenamep);
|
||||
std::string filenames(filenamez);
|
||||
return VL_WRITEMEM_N(hex, width, depth, array_lsb, filenames, memp, start, end);
|
||||
}
|
||||
|
||||
const char* memhFormat(int nBits) {
|
||||
assert((nBits >= 1) && (nBits <= 32));
|
||||
|
||||
static char buf[32];
|
||||
switch ((nBits - 1) / 4) {
|
||||
case 0: VL_SNPRINTF(buf, 32, "%%01x"); break;
|
||||
case 1: VL_SNPRINTF(buf, 32, "%%02x"); break;
|
||||
case 2: VL_SNPRINTF(buf, 32, "%%03x"); break;
|
||||
case 3: VL_SNPRINTF(buf, 32, "%%04x"); break;
|
||||
case 4: VL_SNPRINTF(buf, 32, "%%05x"); break;
|
||||
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
|
||||
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
|
||||
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
int width, // Width of each array row
|
||||
QData depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& filename, // Output file name
|
||||
const void* memp, // Array state
|
||||
QData start, // First array row address to write
|
||||
QData end // Last address to write, or ~0 when not specified
|
||||
) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!hex)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||
"VL_WRITEMEM_N only supports hex format for now, sorry!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate row address limits
|
||||
QData row_min = array_lsb;
|
||||
QData row_max = row_min + depth - 1;
|
||||
|
||||
// Normalize the last address argument: ~0 => row_max
|
||||
QData nend = (end == ~VL_ULL(0)) ? row_max : end;
|
||||
|
||||
// Bounds check the write address range
|
||||
if (VL_UNLIKELY((start < row_min) || (start > row_max)
|
||||
|| (nend < row_min) || (nend > row_max))) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem specified address out-of-bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(start > nend)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate row offset range
|
||||
QData row_start = start - row_min;
|
||||
QData row_end = nend - row_min;
|
||||
|
||||
// Bail out on possible 32-bit size_t overflow
|
||||
if (VL_UNLIKELY(row_end + 1 == 0)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem address is too large");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* fp = fopen(filename.c_str(), "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
||||
// cppcheck-suppress resourceLeak // fp is NULL - bug in cppcheck
|
||||
return;
|
||||
}
|
||||
|
||||
for (QData row_offset = row_start; row_offset <= row_end; ++row_offset) {
|
||||
if (width <= 8) {
|
||||
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||
fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap);
|
||||
fprintf(fp, "\n");
|
||||
} else if (width <= 16) {
|
||||
const SData* datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
|
||||
fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap);
|
||||
fprintf(fp, "\n");
|
||||
} else if (width <= 32) {
|
||||
const IData* datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
|
||||
fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap);
|
||||
fprintf(fp, "\n");
|
||||
} else if (width <= 64) {
|
||||
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
||||
vluint64_t value = VL_MASK_Q(width) & *datap;
|
||||
vluint32_t lo = value & 0xffffffff;
|
||||
vluint32_t hi = value >> 32;
|
||||
fprintf(fp, memhFormat(width - 32), hi);
|
||||
fprintf(fp, "%08x\n", lo);
|
||||
} else {
|
||||
WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
|
||||
WDataInP datap = &memDatap[row_offset * VL_WORDS_I(width)];
|
||||
// output as a sequence of VL_EDATASIZE'd words
|
||||
// from MSB to LSB. Mask off the MSB word which could
|
||||
// contain junk above the top of valid data.
|
||||
int word_idx = ((width - 1) / VL_EDATASIZE);
|
||||
bool first = true;
|
||||
while (word_idx >= 0) {
|
||||
EData data = datap[word_idx];
|
||||
if (first) {
|
||||
data &= VL_MASK_E(width);
|
||||
int top_word_nbits = ((width - 1) & (VL_EDATASIZE - 1)) + 1;
|
||||
fprintf(fp, memhFormat(top_word_nbits), data);
|
||||
} else {
|
||||
fprintf(fp, "%08x", data);
|
||||
}
|
||||
word_idx--;
|
||||
first = false;
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
IData VL_FREAD_I(int width, int array_lsb, int array_size,
|
||||
void* memp, IData fpi, IData start, IData count) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
@ -1506,143 +1373,6 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size,
|
||||
return read_count;
|
||||
}
|
||||
|
||||
void VL_READMEM_Q(bool hex, int width, QData depth, int array_lsb, int,
|
||||
QData filename, void* memp, QData start, QData end) VL_MT_SAFE {
|
||||
WData fnw[VL_WQ_WORDS_E]; VL_SET_WQ(fnw, filename);
|
||||
return VL_READMEM_W(hex, width, depth, array_lsb, VL_WQ_WORDS_E, fnw, memp, start, end);
|
||||
}
|
||||
|
||||
void VL_READMEM_W(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
WDataInP filenamep, void* memp, QData start, QData end) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
_VL_VINT_TO_STRING(fnwords * VL_EDATASIZE, filenamez, filenamep);
|
||||
std::string filenames(filenamez);
|
||||
return VL_READMEM_N(hex, width, depth, array_lsb, filenames, memp, start, end);
|
||||
}
|
||||
|
||||
void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
int width, // Width of each array row
|
||||
QData depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& filename, // Input file name
|
||||
void* memp, // Array state
|
||||
QData start, // First array row address to read
|
||||
QData end // Last row address to read
|
||||
) VL_MT_SAFE {
|
||||
FILE* fp = fopen(filename.c_str(), "r");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
// We don't report the Verilog source filename as it slow to have to pass it down
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$readmem file not found");
|
||||
// cppcheck-suppress resourceLeak // fp is NULL - bug in cppcheck
|
||||
return;
|
||||
}
|
||||
// Prep for reading
|
||||
QData addr = start;
|
||||
int linenum = 1;
|
||||
bool innum = false;
|
||||
bool ignore_to_eol = false;
|
||||
bool ignore_to_cmt = false;
|
||||
bool needinc = false;
|
||||
bool reading_addr = false;
|
||||
int lastc = ' ';
|
||||
// Read the data
|
||||
// We process a character at a time, as then we don't need to deal
|
||||
// with changing buffer sizes dynamically, etc.
|
||||
while (1) {
|
||||
int c = fgetc(fp);
|
||||
if (VL_UNLIKELY(c == EOF)) break;
|
||||
// printf("%d: Got '%c' Addr%x IN%d IgE%d IgC%d ninc%d\n",
|
||||
// linenum, c, addr, innum, ignore_to_eol, ignore_to_cmt, needinc);
|
||||
if (c=='\n') {
|
||||
linenum++; ignore_to_eol = false;
|
||||
if (innum) reading_addr = false;
|
||||
innum = false;
|
||||
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
||||
if (innum) reading_addr = false;
|
||||
innum = false;
|
||||
}
|
||||
// Skip // comments and detect /* comments
|
||||
else if (ignore_to_cmt && lastc == '*' && c == '/') {
|
||||
ignore_to_cmt = false;
|
||||
if (innum) reading_addr = false;
|
||||
innum = false;
|
||||
} else if (!ignore_to_eol && !ignore_to_cmt) {
|
||||
if (lastc=='/' && c=='*') { ignore_to_cmt = true; }
|
||||
else if (lastc=='/' && c=='/') { ignore_to_eol = true; }
|
||||
else if (c=='/') {} // Part of /* or //
|
||||
else if (c=='#') { ignore_to_eol = true; }
|
||||
else if (c=='_') {}
|
||||
else if (c=='@') { reading_addr = true; innum = false; needinc = false; }
|
||||
// Check for hex or binary digits as file format requests
|
||||
else if (isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
||||
c = tolower(c);
|
||||
int value = (c >= 'a' ? (c=='x' ? VL_RAND_RESET_I(4) : (c-'a'+10)) : (c-'0'));
|
||||
if (!innum) { // Prep for next number
|
||||
if (needinc) { addr++; needinc=false; }
|
||||
}
|
||||
if (reading_addr) {
|
||||
// Decode @ addresses
|
||||
if (!innum) addr = 0;
|
||||
addr = (addr << 4) + value;
|
||||
} else {
|
||||
needinc = true;
|
||||
// printf(" Value width=%d @%x = %c\n", width, addr, c);
|
||||
if (VL_UNLIKELY(addr >= static_cast<QData>(depth + array_lsb)
|
||||
|| addr < static_cast<QData>(array_lsb))) {
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "",
|
||||
"$readmem file address beyond bounds of array");
|
||||
} else {
|
||||
QData entry = addr - array_lsb;
|
||||
QData shift = hex ? VL_ULL(4) : VL_ULL(1);
|
||||
// Shift value in
|
||||
if (width <= 8) {
|
||||
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
|
||||
} else if (width <= 16) {
|
||||
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
|
||||
} else if (width <= VL_IDATASIZE) {
|
||||
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
|
||||
} else if (width <= VL_QUADSIZE) {
|
||||
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << static_cast<QData>(shift))
|
||||
+ static_cast<QData>(value)) & VL_MASK_Q(width);
|
||||
} else {
|
||||
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))
|
||||
[ entry*VL_WORDS_I(width) ];
|
||||
if (!innum) { VL_ZERO_RESET_W(width, datap); }
|
||||
_VL_SHIFTL_INPLACE_W(width, datap, static_cast<IData>(shift));
|
||||
datap[0] |= value;
|
||||
}
|
||||
if (VL_UNLIKELY(value >= (1 << shift))) {
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
}
|
||||
}
|
||||
innum = true;
|
||||
} else {
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
}
|
||||
if (needinc) { addr++; }
|
||||
|
||||
// Final checks
|
||||
fclose(fp);
|
||||
if (VL_UNLIKELY(end != ~VL_ULL(0) && addr != (end + 1))) {
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "",
|
||||
"$readmem file ended before specified ending-address");
|
||||
}
|
||||
}
|
||||
|
||||
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
WData lhsw[VL_WQ_WORDS_E]; VL_SET_WQ(lhsw, lhs);
|
||||
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
|
||||
@ -1787,6 +1517,9 @@ std::string VL_TO_STRING(IData lhs) {
|
||||
std::string VL_TO_STRING(QData lhs) {
|
||||
return VL_SFORMATF_NX("'h%0x", 64, lhs);
|
||||
}
|
||||
std::string VL_TO_STRING_W(int words, WDataInP obj) {
|
||||
return VL_SFORMATF_NX("'h%0x", words * VL_EDATASIZE, obj);
|
||||
}
|
||||
|
||||
std::string VL_TOLOWER_NN(const std::string& ld) VL_MT_SAFE {
|
||||
std::string out = ld;
|
||||
@ -1859,6 +1592,317 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
||||
return static_cast<IData>(v);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Readmem/writemem
|
||||
|
||||
static const char* memhFormat(int nBits) {
|
||||
assert((nBits >= 1) && (nBits <= 32));
|
||||
|
||||
static char buf[32];
|
||||
switch ((nBits - 1) / 4) {
|
||||
case 0: VL_SNPRINTF(buf, 32, "%%01x"); break;
|
||||
case 1: VL_SNPRINTF(buf, 32, "%%02x"); break;
|
||||
case 2: VL_SNPRINTF(buf, 32, "%%03x"); break;
|
||||
case 3: VL_SNPRINTF(buf, 32, "%%04x"); break;
|
||||
case 4: VL_SNPRINTF(buf, 32, "%%05x"); break;
|
||||
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
|
||||
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
|
||||
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end)
|
||||
: m_hex(hex)
|
||||
, m_bits(bits)
|
||||
, m_filename(filename)
|
||||
, m_end(end)
|
||||
, m_addr(start)
|
||||
, m_linenum(0) {
|
||||
m_fp = fopen(filename.c_str(), "r");
|
||||
if (VL_UNLIKELY(!m_fp)) {
|
||||
// We don't report the Verilog source filename as it slow to have to pass it down
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$readmem file not found");
|
||||
// cppcheck-suppress resourceLeak // m_fp is NULL - bug in cppcheck
|
||||
return;
|
||||
}
|
||||
}
|
||||
VlReadMem::~VlReadMem() {
|
||||
if (m_fp) { fclose(m_fp); m_fp = NULL; }
|
||||
}
|
||||
bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
if (VL_UNLIKELY(!m_fp)) return false;
|
||||
valuer = "";
|
||||
// Prep for reading
|
||||
bool indata = false;
|
||||
bool ignore_to_eol = false;
|
||||
bool ignore_to_cmt = false;
|
||||
bool reading_addr = false;
|
||||
int lastc = ' ';
|
||||
// Read the data
|
||||
// We process a character at a time, as then we don't need to deal
|
||||
// with changing buffer sizes dynamically, etc.
|
||||
while (1) {
|
||||
int c = fgetc(m_fp);
|
||||
if (VL_UNLIKELY(c == EOF)) break;
|
||||
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
|
||||
// m_linenum, c, m_addr, indata, ignore_to_eol, ignore_to_cmt);
|
||||
// See if previous data value has completed, and if so return
|
||||
if (c == '_') continue; // Ignore _ e.g. inside a number
|
||||
if (indata && !isxdigit(c) && c != 'x' && c != 'X') {
|
||||
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
|
||||
indata = false;
|
||||
ungetc(c, m_fp);
|
||||
addrr = m_addr;
|
||||
++m_addr;
|
||||
return true;
|
||||
}
|
||||
// Parse line
|
||||
if (c == '\n') {
|
||||
++m_linenum; ignore_to_eol = false;
|
||||
reading_addr = false;
|
||||
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
||||
reading_addr = false;
|
||||
}
|
||||
// Skip // comments and detect /* comments
|
||||
else if (ignore_to_cmt && lastc == '*' && c == '/') {
|
||||
ignore_to_cmt = false;
|
||||
reading_addr = false;
|
||||
} else if (!ignore_to_eol && !ignore_to_cmt) {
|
||||
if (lastc == '/' && c == '*') { ignore_to_cmt = true; }
|
||||
else if (lastc == '/' && c == '/') { ignore_to_eol = true; }
|
||||
else if (c == '/') {} // Part of /* or //
|
||||
else if (c == '#') { ignore_to_eol = true; }
|
||||
else if (c == '@') { reading_addr = true; m_addr = 0; }
|
||||
// Check for hex or binary digits as file format requests
|
||||
else if (isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
||||
c = tolower(c);
|
||||
int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c-'a'+10)) : (c-'0'));
|
||||
if (reading_addr) {
|
||||
// Decode @ addresses
|
||||
m_addr = (m_addr << 4) + value;
|
||||
} else {
|
||||
indata = true;
|
||||
valuer += c;
|
||||
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
|
||||
if (VL_UNLIKELY(value > 1 && !m_hex)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_end != ~VL_ULL(0) && m_addr <= m_end)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem file ended before specified final address (IEEE 2017 21.4)");
|
||||
}
|
||||
|
||||
return false; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
QData shift = m_hex ? VL_ULL(4) : VL_ULL(1);
|
||||
bool innum = false;
|
||||
// Shift value in
|
||||
for (std::string::const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
|
||||
char c = tolower(*it);
|
||||
int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (m_bits <= 8) {
|
||||
CData* datap = reinterpret_cast<CData*>(valuep);
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= 16) {
|
||||
SData* datap = reinterpret_cast<SData*>(valuep);
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= VL_IDATASIZE) {
|
||||
IData* datap = reinterpret_cast<IData*>(valuep);
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= VL_QUADSIZE) {
|
||||
QData* datap = reinterpret_cast<QData*>(valuep);
|
||||
if (!innum) { *datap = 0; }
|
||||
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
|
||||
& VL_MASK_Q(m_bits);
|
||||
} else {
|
||||
WDataOutP datap = reinterpret_cast<WDataOutP>(valuep);
|
||||
if (!innum) { VL_ZERO_RESET_W(m_bits, datap); }
|
||||
_VL_SHIFTL_INPLACE_W(m_bits, datap, static_cast<IData>(shift));
|
||||
datap[0] |= value;
|
||||
}
|
||||
innum = true;
|
||||
}
|
||||
}
|
||||
|
||||
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
|
||||
: m_hex(hex)
|
||||
, m_bits(bits)
|
||||
, m_filename(filename)
|
||||
, m_addr(0) {
|
||||
if (VL_UNLIKELY(!hex)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||
"Unsupported: $writemem binary format (suggest hex format)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(start > end)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
|
||||
return;
|
||||
}
|
||||
|
||||
m_fp = fopen(filename.c_str(), "w");
|
||||
if (VL_UNLIKELY(!m_fp)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
||||
// cppcheck-suppress resourceLeak // m_fp is NULL - bug in cppcheck
|
||||
return;
|
||||
}
|
||||
}
|
||||
VlWriteMem::~VlWriteMem() {
|
||||
if (m_fp) { fclose(m_fp); m_fp = NULL; }
|
||||
}
|
||||
void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
if (VL_UNLIKELY(!m_fp)) return;
|
||||
if (addr != m_addr && addrstamp) { // Only assoc has time stamps
|
||||
fprintf(m_fp, "@%" VL_PRI64 "x\n", addr);
|
||||
}
|
||||
m_addr = addr + 1;
|
||||
if (m_bits <= 8) {
|
||||
const CData* datap = reinterpret_cast<const CData*>(valuep);
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
} else if (m_bits <= 16) {
|
||||
const SData* datap = reinterpret_cast<const SData*>(valuep);
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
} else if (m_bits <= 32) {
|
||||
const IData* datap = reinterpret_cast<const IData*>(valuep);
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
} else if (m_bits <= 64) {
|
||||
const QData* datap = reinterpret_cast<const QData*>(valuep);
|
||||
vluint64_t value = VL_MASK_Q(m_bits) & *datap;
|
||||
vluint32_t lo = value & 0xffffffff;
|
||||
vluint32_t hi = value >> 32;
|
||||
fprintf(m_fp, memhFormat(m_bits - 32), hi);
|
||||
fprintf(m_fp, "%08x\n", lo);
|
||||
} else {
|
||||
WDataInP datap = reinterpret_cast<WDataInP>(valuep);
|
||||
// output as a sequence of VL_EDATASIZE'd words
|
||||
// from MSB to LSB. Mask off the MSB word which could
|
||||
// contain junk above the top of valid data.
|
||||
int word_idx = ((m_bits - 1) / VL_EDATASIZE);
|
||||
bool first = true;
|
||||
while (word_idx >= 0) {
|
||||
EData data = datap[word_idx];
|
||||
if (first) {
|
||||
data &= VL_MASK_E(m_bits);
|
||||
int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
|
||||
fprintf(m_fp, memhFormat(top_word_nbits), data);
|
||||
} else {
|
||||
fprintf(m_fp, "%08x", data);
|
||||
}
|
||||
word_idx--;
|
||||
first = false;
|
||||
}
|
||||
fprintf(m_fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
int bits, // M_Bits of each array row
|
||||
QData depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& filename, // Input file name
|
||||
void* memp, // Array state
|
||||
QData start, // First array row address to read
|
||||
QData end // Last row address to read
|
||||
) VL_MT_SAFE {
|
||||
QData addr_max = array_lsb + depth - 1;
|
||||
if (start < array_lsb) start = array_lsb;
|
||||
QData addr_end = end;
|
||||
if (addr_end > addr_max) addr_end = addr_max;
|
||||
|
||||
VlReadMem rmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (1) {
|
||||
QData addr;
|
||||
std::string value;
|
||||
if (rmem.get(addr /*ref*/, value/*ref*/)) {
|
||||
if (VL_UNLIKELY(addr < static_cast<QData>(array_lsb)
|
||||
|| addr >= static_cast<QData>(array_lsb + depth))) {
|
||||
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
|
||||
"$readmem file address beyond bounds of array");
|
||||
} else {
|
||||
QData entry = addr - array_lsb;
|
||||
if (bits <= 8) {
|
||||
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= 16) {
|
||||
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= VL_IDATASIZE) {
|
||||
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= VL_QUADSIZE) {
|
||||
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else {
|
||||
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))
|
||||
[ entry*VL_WORDS_I(bits) ];
|
||||
rmem.setData(datap, value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
int bits, // Width of each array row
|
||||
QData depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& filename, // Output file name
|
||||
const void* memp, // Array state
|
||||
QData start, // First array row address to write
|
||||
QData end // Last address to write, or ~0 when not specified
|
||||
) VL_MT_SAFE {
|
||||
QData addr_max = array_lsb + depth - 1;
|
||||
if (start < array_lsb) start = array_lsb;
|
||||
if (end > addr_max) end = addr_max;
|
||||
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
|
||||
for (QData addr = start; addr <= end; ++addr) {
|
||||
QData row_offset = addr - array_lsb;
|
||||
if (bits <= 8) {
|
||||
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 16) {
|
||||
const SData* datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 32) {
|
||||
const IData* datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 64) {
|
||||
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else {
|
||||
WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
|
||||
WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
|
||||
wmem.print(addr, false, datap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Timescale conversion
|
||||
|
||||
|
@ -630,22 +630,6 @@ extern void VL_FCLOSE_I(IData fdi);
|
||||
extern IData VL_FREAD_I(int width, int array_lsb, int array_size,
|
||||
void* memp, IData fpi, IData start, IData count);
|
||||
|
||||
extern void VL_READMEM_W(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
WDataInP filenamep, void* memp, QData start, QData end);
|
||||
extern void VL_READMEM_Q(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
QData filename, void* memp, QData start, QData end);
|
||||
inline void VL_READMEM_I(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
IData filename, void* memp, QData start, QData end) VL_MT_SAFE {
|
||||
VL_READMEM_Q(hex, width, depth, array_lsb, fnwords, filename, memp, start, end); }
|
||||
|
||||
extern void VL_WRITEMEM_W(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
WDataInP filenamep, const void* memp, QData start, QData end);
|
||||
extern void VL_WRITEMEM_Q(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
QData filename, const void* memp, QData start, QData end);
|
||||
inline void VL_WRITEMEM_I(bool hex, int width, QData depth, int array_lsb, int fnwords,
|
||||
IData filename, const void* memp, QData start, QData end) VL_MT_SAFE {
|
||||
VL_WRITEMEM_Q(hex, width, depth, array_lsb, fnwords, filename, memp, start, end); }
|
||||
|
||||
extern void VL_WRITEF(const char* formatp, ...);
|
||||
extern void VL_FWRITEF(IData fpi, const char* formatp, ...);
|
||||
|
||||
|
@ -42,6 +42,40 @@ extern std::string VL_TO_STRING(SData obj);
|
||||
extern std::string VL_TO_STRING(IData obj);
|
||||
extern std::string VL_TO_STRING(QData obj);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, WDataInP obj);
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
class VlReadMem {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlReadMem();
|
||||
bool isOpen() const { return m_fp != NULL; }
|
||||
int linenum() const { return m_linenum; }
|
||||
bool get(QData& addrr, std::string& valuer);
|
||||
void setData(void* valuep, const std::string& rhs);
|
||||
};
|
||||
|
||||
class VlWriteMem {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
bool isOpen() const { return m_fp != NULL; }
|
||||
void print(QData addr, bool addrstamp, const void* valuep);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Verilog array container
|
||||
@ -72,6 +106,10 @@ VlWide<T_Words>& VL_CVT_W_A(WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words>
|
||||
std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
@ -180,6 +218,34 @@ std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlReadMem rmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (1) {
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (typename VlAssocArray<T_Key, T_Value>::const_iterator it = obj.begin(); it != obj.end();
|
||||
++it) {
|
||||
QData addr = it->first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(it->second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
@ -312,12 +378,12 @@ extern std::string VL_TOLOWER_NN(const std::string& ld);
|
||||
extern std::string VL_TOUPPER_NN(const std::string& ld);
|
||||
|
||||
extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int width, QData depth, int array_lsb,
|
||||
const std::string& filename,
|
||||
void* memp, QData start, QData end) VL_MT_SAFE;
|
||||
extern void VL_WRITEMEM_N(bool hex, int width, QData depth, int array_lsb,
|
||||
const std::string& filename,
|
||||
const void* memp, QData start, QData end) 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;
|
||||
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, const void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld,
|
||||
const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output,
|
||||
|
@ -397,41 +397,40 @@ public:
|
||||
}
|
||||
virtual void visit(AstNodeReadWriteMem* nodep) {
|
||||
puts(nodep->cFuncPrefixp());
|
||||
emitIQW(nodep->filenamep());
|
||||
puts("("); // We take a void* rather than emitIQW(nodep->memp());
|
||||
puts(nodep->isHex()?"true":"false");
|
||||
putbs(",");
|
||||
puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width
|
||||
putbs(",");
|
||||
puts("N(");
|
||||
puts(nodep->isHex() ? "true" : "false");
|
||||
putbs(", ");
|
||||
// Need real storage width
|
||||
puts(cvtToStr(nodep->memp()->dtypep()->subDTypep()->widthMin()));
|
||||
uint32_t array_lsb = 0;
|
||||
{
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->memp(), VarRef);
|
||||
if (!varrefp) { nodep->v3error(nodep->verilogKwd() << " loading non-variable"); }
|
||||
else if (const AstAssocArrayDType* adtypep
|
||||
= VN_CAST(varrefp->varp()->dtypeSkipRefp(), AssocArrayDType)) {
|
||||
// nodep->memp() below will when verilated code is compiled create a C++ template
|
||||
}
|
||||
else if (const AstUnpackArrayDType* adtypep
|
||||
= VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) {
|
||||
putbs(", ");
|
||||
puts(cvtToStr(varrefp->varp()->dtypep()->arrayUnpackedElements()));
|
||||
array_lsb = adtypep->lsb();
|
||||
putbs(", ");
|
||||
puts(cvtToStr(array_lsb));
|
||||
}
|
||||
else {
|
||||
nodep->v3error(nodep->verilogKwd()
|
||||
<< " loading other than unpacked-array variable");
|
||||
<< " loading other than unpacked/associative-array variable");
|
||||
}
|
||||
}
|
||||
putbs(", ");
|
||||
puts(cvtToStr(array_lsb));
|
||||
putbs(",");
|
||||
if (!nodep->filenamep()->dtypep()->isString()) {
|
||||
puts(cvtToStr(nodep->filenamep()->widthWords()));
|
||||
checkMaxWords(nodep->filenamep());
|
||||
putbs(", ");
|
||||
}
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
emitCvtPackStr(nodep->filenamep());
|
||||
putbs(", ");
|
||||
iterateAndNextNull(nodep->memp());
|
||||
putbs(",");
|
||||
putbs(", ");
|
||||
if (nodep->lsbp()) { iterateAndNextNull(nodep->lsbp()); }
|
||||
else puts(cvtToStr(array_lsb));
|
||||
putbs(",");
|
||||
putbs(", ");
|
||||
if (nodep->msbp()) { iterateAndNextNull(nodep->msbp()); } else puts("~VL_ULL(0)");
|
||||
puts(");\n");
|
||||
}
|
||||
@ -782,7 +781,7 @@ public:
|
||||
puts("(");
|
||||
if (nodep->isWide()) {
|
||||
puts(cvtToStr(nodep->widthWords())); // Note argument width, not node width (which is always 32)
|
||||
puts(",");
|
||||
puts(", ");
|
||||
}
|
||||
iterateAndNextNull(nodep);
|
||||
puts(")");
|
||||
|
@ -54,6 +54,10 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeReadWriteMem* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstValuePlusArgs* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
|
@ -2929,9 +2929,27 @@ private:
|
||||
assertAtStatement(nodep);
|
||||
userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
|
||||
userIterateAndNext(nodep->memp(), WidthVP(SELF, BOTH).p());
|
||||
if (!VN_IS(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
AstNodeDType* subp = NULL;
|
||||
if (AstAssocArrayDType* adtypep
|
||||
= VN_CAST(nodep->memp()->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||
subp = adtypep->subDTypep();
|
||||
if (!adtypep->keyDTypep()->skipRefp()->basicp()
|
||||
|| !adtypep->keyDTypep()->skipRefp()->basicp()->keyword().isIntNumeric()) {
|
||||
nodep->memp()->v3error(nodep->verilogKwd()
|
||||
<< " address/key must be integral (IEEE 21.4.1)");
|
||||
}
|
||||
} else if (AstUnpackArrayDType* adtypep
|
||||
= VN_CAST(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
subp = adtypep->subDTypep();
|
||||
} else {
|
||||
nodep->memp()->v3error("Unsupported: "
|
||||
<< nodep->verilogKwd()
|
||||
<< " into other than unpacked or associative array");
|
||||
}
|
||||
if (subp && (!subp->skipRefp()->basicp()
|
||||
|| !subp->skipRefp()->basicp()->keyword().isIntNumeric())) {
|
||||
nodep->memp()->v3error("Unsupported: " << nodep->verilogKwd()
|
||||
<< " into other than unpacked array");
|
||||
<< " array values must be integral");
|
||||
}
|
||||
userIterateAndNext(nodep->lsbp(), WidthVP(SELF, BOTH).p());
|
||||
userIterateAndNext(nodep->msbp(), WidthVP(SELF, BOTH).p());
|
||||
|
@ -61,23 +61,25 @@ module t;
|
||||
$writememh(`OUT_TMP1, binary_nostart_tmp);
|
||||
$readmemh(`OUT_TMP1, binary_nostart);
|
||||
`else
|
||||
$readmemb("t/t_sys_readmem_b.mem", binary_nostart);
|
||||
$readmemb("t/t_sys_readmem_b.mem", binary_nostart);
|
||||
`endif
|
||||
`ifdef TEST_VERBOSE
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_nostart[i]);
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_nostart[i]);
|
||||
`endif
|
||||
if (binary_nostart['h2] != 6'h02) $stop;
|
||||
if (binary_nostart['h3] != 6'h03) $stop;
|
||||
if (binary_nostart['h4] != 6'h04) $stop;
|
||||
if (binary_nostart['h5] != 6'h05) $stop;
|
||||
if (binary_nostart['h6] != 6'h06) $stop;
|
||||
if (binary_nostart['h7] != 6'h07) $stop;
|
||||
if (binary_nostart['h8] != 6'h10) $stop;
|
||||
if (binary_nostart['hc] != 6'h14) $stop;
|
||||
if (binary_nostart['hd] != 6'h15) $stop;
|
||||
if (binary_nostart['h2] != 6'h02) $stop;
|
||||
if (binary_nostart['h3] != 6'h03) $stop;
|
||||
if (binary_nostart['h4] != 6'h04) $stop;
|
||||
if (binary_nostart['h5] != 6'h05) $stop;
|
||||
if (binary_nostart['h6] != 6'h06) $stop;
|
||||
if (binary_nostart['h7] != 6'h07) $stop;
|
||||
if (binary_nostart['h8] != 6'h10) $stop;
|
||||
if (binary_nostart['hc] != 6'h14) $stop;
|
||||
if (binary_nostart['hd] != 6'h15) $stop;
|
||||
end
|
||||
|
||||
begin
|
||||
binary_start['h0c] = 6'h3f; // Not in read range
|
||||
//
|
||||
`ifdef WRITEMEM_READ_BACK
|
||||
$readmemb("t/t_sys_readmem_b_8.mem", binary_start_tmp, 4, 4+7);
|
||||
`ifdef TEST_VERBOSE
|
||||
@ -86,19 +88,21 @@ module t;
|
||||
$writememh(`OUT_TMP2, binary_start_tmp, 4, 4+7);
|
||||
$readmemh(`OUT_TMP2, binary_start, 4, 4+7);
|
||||
`else
|
||||
$readmemb("t/t_sys_readmem_b_8.mem", binary_start, 4, 4+7);
|
||||
$readmemb("t/t_sys_readmem_b_8.mem", binary_start, 4, 4+7); // 4-11
|
||||
`endif
|
||||
`ifdef TEST_VERBOSE
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_start[i]);
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_start[i]);
|
||||
`endif
|
||||
if (binary_start['h04] != 6'h10) $stop;
|
||||
if (binary_start['h05] != 6'h11) $stop;
|
||||
if (binary_start['h06] != 6'h12) $stop;
|
||||
if (binary_start['h07] != 6'h13) $stop;
|
||||
if (binary_start['h08] != 6'h14) $stop;
|
||||
if (binary_start['h09] != 6'h15) $stop;
|
||||
if (binary_start['h0a] != 6'h16) $stop;
|
||||
if (binary_start['h0b] != 6'h17) $stop;
|
||||
if (binary_start['h04] != 6'h10) $stop;
|
||||
if (binary_start['h05] != 6'h11) $stop;
|
||||
if (binary_start['h06] != 6'h12) $stop;
|
||||
if (binary_start['h07] != 6'h13) $stop;
|
||||
if (binary_start['h08] != 6'h14) $stop;
|
||||
if (binary_start['h09] != 6'h15) $stop;
|
||||
if (binary_start['h0a] != 6'h16) $stop;
|
||||
if (binary_start['h0b] != 6'h17) $stop;
|
||||
//
|
||||
if (binary_start['h0c] != 6'h3f) $stop;
|
||||
end
|
||||
|
||||
begin
|
||||
@ -112,15 +116,15 @@ module t;
|
||||
$writememh(`OUT_TMP3, hex_tmp, 0);
|
||||
$readmemh(`OUT_TMP3, hex, 0);
|
||||
`else
|
||||
$readmemh("t/t_sys_readmem_h.mem", hex, 0);
|
||||
$readmemh("t/t_sys_readmem_h.mem", hex, 0);
|
||||
`endif
|
||||
`ifdef TEST_VERBOSE
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, hex[i]);
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, hex[i]);
|
||||
`endif
|
||||
if (hex['h04] != 176'h400437654321276543211765432107654321abcdef10) $stop;
|
||||
if (hex['h0a] != 176'h400a37654321276543211765432107654321abcdef11) $stop;
|
||||
if (hex['h0b] != 176'h400b37654321276543211765432107654321abcdef12) $stop;
|
||||
if (hex['h0c] != 176'h400c37654321276543211765432107654321abcdef13) $stop;
|
||||
if (hex['h04] != 176'h400437654321276543211765432107654321abcdef10) $stop;
|
||||
if (hex['h0a] != 176'h400a37654321276543211765432107654321abcdef11) $stop;
|
||||
if (hex['h0b] != 176'h400b37654321276543211765432107654321abcdef12) $stop;
|
||||
if (hex['h0c] != 176'h400c37654321276543211765432107654321abcdef13) $stop;
|
||||
end
|
||||
|
||||
begin
|
||||
@ -134,15 +138,15 @@ module t;
|
||||
$writememh(`OUT_TMP4, hex_align_tmp, 0);
|
||||
$readmemh(`OUT_TMP4, hex_align, 0);
|
||||
`else
|
||||
$readmemh("t/t_sys_readmem_align_h.mem", hex_align, 0);
|
||||
$readmemh("t/t_sys_readmem_align_h.mem", hex_align, 0);
|
||||
`endif
|
||||
`ifdef TEST_VERBOSE
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, hex_align[i]);
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, hex_align[i]);
|
||||
`endif
|
||||
if (hex_align['h04] != 192'h77554004_37654321_27654321_17654321_07654321_abcdef10) $stop;
|
||||
if (hex_align['h0a] != 192'h7755400a_37654321_27654321_17654321_07654321_abcdef11) $stop;
|
||||
if (hex_align['h0b] != 192'h7755400b_37654321_27654321_17654321_07654321_abcdef12) $stop;
|
||||
if (hex_align['h0c] != 192'h7755400c_37654321_27654321_17654321_07654321_abcdef13) $stop;
|
||||
if (hex_align['h04] != 192'h77554004_37654321_27654321_17654321_07654321_abcdef10) $stop;
|
||||
if (hex_align['h0a] != 192'h7755400a_37654321_27654321_17654321_07654321_abcdef11) $stop;
|
||||
if (hex_align['h0b] != 192'h7755400b_37654321_27654321_17654321_07654321_abcdef12) $stop;
|
||||
if (hex_align['h0c] != 192'h7755400c_37654321_27654321_17654321_07654321_abcdef13) $stop;
|
||||
end
|
||||
|
||||
begin
|
||||
@ -156,12 +160,12 @@ module t;
|
||||
$writememh(fns_tmp, binary_string_tmp);
|
||||
$readmemh(fns_tmp, binary_string);
|
||||
`else
|
||||
$readmemb(fns, binary_string);
|
||||
$readmemb(fns, binary_string);
|
||||
`endif
|
||||
`ifdef TEST_VERBOSE
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_string[i]);
|
||||
for (i=0; i<16; i=i+1) $write(" @%x = %x\n", i, binary_string[i]);
|
||||
`endif
|
||||
if (binary_string['h2] != 6'h02) $stop;
|
||||
if (binary_string['h2] != 6'h02) $stop;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
23
test_regress/t/t_sys_readmem_assoc.pl
Executable file
23
test_regress/t/t_sys_readmem_assoc.pl
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
files_identical("$Self->{obj_dir}/t_sys_writemem_c_b.mem", "t/t_sys_readmem_assoc_c_b.out");
|
||||
files_identical("$Self->{obj_dir}/t_sys_writemem_w_h.mem", "t/t_sys_readmem_assoc_w_h.out");
|
||||
|
||||
ok(1);
|
||||
1;
|
26
test_regress/t/t_sys_readmem_assoc.v
Normal file
26
test_regress/t/t_sys_readmem_assoc.v
Normal file
@ -0,0 +1,26 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2020 by Wilson Snyder.
|
||||
|
||||
`define STRINGIFY(x) `"x`"
|
||||
|
||||
module t;
|
||||
|
||||
reg [5:0] assoc_c[int];
|
||||
reg [95:0] assoc_w[int];
|
||||
|
||||
initial begin
|
||||
assoc_c[300] = 10; // See if clearing must happen first
|
||||
$readmemb("t/t_sys_readmem_b.mem", assoc_c);
|
||||
$display("assoc_c=%p", assoc_c);
|
||||
$writememh({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_writemem_c_b.mem"}, assoc_c);
|
||||
|
||||
$readmemb("t/t_sys_readmem_b.mem", assoc_w);
|
||||
// Not conditional with TEST_VERBOSE as found bug with wide display
|
||||
$display("assoc_w=%p", assoc_w);
|
||||
$writememh({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_writemem_w_h.mem"}, assoc_w);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
9
test_regress/t/t_sys_readmem_assoc_bad.out
Normal file
9
test_regress/t/t_sys_readmem_assoc_bad.out
Normal file
@ -0,0 +1,9 @@
|
||||
%Error: t/t_sys_readmem_assoc_bad.v:12: $readmemb address/key must be integral (IEEE 21.4.1)
|
||||
: ... In instance t
|
||||
$readmemb("not", assoc_bad_key);
|
||||
^~~~~~~~~~~~~
|
||||
%Error: t/t_sys_readmem_assoc_bad.v:13: Unsupported: $readmemb array values must be integral
|
||||
: ... In instance t
|
||||
$readmemb("not", assoc_bad_value);
|
||||
^~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
18
test_regress/t/t_sys_readmem_assoc_bad.pl
Executable file
18
test_regress/t/t_sys_readmem_assoc_bad.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
fails => $Self->{vlt_all},
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
17
test_regress/t/t_sys_readmem_assoc_bad.v
Normal file
17
test_regress/t/t_sys_readmem_assoc_bad.v
Normal file
@ -0,0 +1,17 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2020 by Wilson Snyder.
|
||||
|
||||
module t;
|
||||
|
||||
reg [5:0] assoc_bad_key[real];
|
||||
real assoc_bad_value[int];
|
||||
|
||||
initial begin
|
||||
$readmemb("not", assoc_bad_key);
|
||||
$readmemb("not", assoc_bad_value);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
13
test_regress/t/t_sys_readmem_assoc_c_b.out
Normal file
13
test_regress/t/t_sys_readmem_assoc_c_b.out
Normal file
@ -0,0 +1,13 @@
|
||||
02
|
||||
03
|
||||
04
|
||||
05
|
||||
06
|
||||
07
|
||||
@8
|
||||
10
|
||||
@c
|
||||
14
|
||||
15
|
||||
@12c
|
||||
0a
|
11
test_regress/t/t_sys_readmem_assoc_w_h.out
Normal file
11
test_regress/t/t_sys_readmem_assoc_w_h.out
Normal file
@ -0,0 +1,11 @@
|
||||
000000000000000000000002
|
||||
000000000000000000000003
|
||||
000000000000000000000004
|
||||
000000000000000000000005
|
||||
000000000000000000000006
|
||||
000000000000000000000007
|
||||
@8
|
||||
000000000000000000000010
|
||||
@c
|
||||
000000000000000000000014
|
||||
000000000000000000000015
|
@ -15,7 +15,7 @@ compile(
|
||||
execute(
|
||||
fails => $Self->{vlt_all},
|
||||
expect =>
|
||||
'%Error: t/t_sys_readmem_bad_end.mem:\d+: \$readmem file ended before specified ending-address',
|
||||
'%Error: t/t_sys_readmem_bad_end.mem:\d+: \$readmem file ended before specified final address',
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
@ -31,12 +31,7 @@ execute(
|
||||
for (my $i = 1; $i <= 5; $i++) {
|
||||
my $gold = "$Self->{t_dir}/t_sys_writemem.gold${i}.mem";
|
||||
my $out = "$Self->{obj_dir}/tmp${i}.mem";
|
||||
print "> diff $gold $out\n";
|
||||
my @diffs = `diff $gold $out`;
|
||||
if (0 < scalar @diffs) {
|
||||
print @diffs;
|
||||
error("Got unexpected diffs against gold.");
|
||||
}
|
||||
files_identical($out, $gold);
|
||||
}
|
||||
|
||||
ok(1);
|
||||
|
Loading…
Reference in New Issue
Block a user