Support / with assoc arrarys. Closes #2100.

This commit is contained in:
Wilson Snyder 2020-01-14 07:01:17 -05:00
parent 7bed17b14b
commit 918df2e618
17 changed files with 588 additions and 355 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View 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

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

View 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

View File

@ -0,0 +1,13 @@
02
03
04
05
06
07
@8
10
@c
14
15
@12c
0a

View File

@ -0,0 +1,11 @@
000000000000000000000002
000000000000000000000003
000000000000000000000004
000000000000000000000005
000000000000000000000006
000000000000000000000007
@8
000000000000000000000010
@c
000000000000000000000014
000000000000000000000015

View File

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

View File

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