From 918df2e6186aaf24e0676df6fe5d614889a9f11a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 14 Jan 2020 07:01:17 -0500 Subject: [PATCH] Support / with assoc arrarys. Closes #2100. --- Changes | 2 + include/verilated.cpp | 584 +++++++++++---------- include/verilated.h | 16 - include/verilated_heavy.h | 78 ++- src/V3EmitC.cpp | 35 +- src/V3EmitCInlines.cpp | 4 + src/V3Width.cpp | 22 +- test_regress/t/t_sys_readmem.v | 76 +-- test_regress/t/t_sys_readmem_assoc.pl | 23 + test_regress/t/t_sys_readmem_assoc.v | 26 + test_regress/t/t_sys_readmem_assoc_bad.out | 9 + test_regress/t/t_sys_readmem_assoc_bad.pl | 18 + test_regress/t/t_sys_readmem_assoc_bad.v | 17 + test_regress/t/t_sys_readmem_assoc_c_b.out | 13 + test_regress/t/t_sys_readmem_assoc_w_h.out | 11 + test_regress/t/t_sys_readmem_bad_end.pl | 2 +- test_regress/t/t_sys_writemem.pl | 7 +- 17 files changed, 588 insertions(+), 355 deletions(-) create mode 100755 test_regress/t/t_sys_readmem_assoc.pl create mode 100644 test_regress/t/t_sys_readmem_assoc.v create mode 100644 test_regress/t/t_sys_readmem_assoc_bad.out create mode 100755 test_regress/t/t_sys_readmem_assoc_bad.pl create mode 100644 test_regress/t/t_sys_readmem_assoc_bad.v create mode 100644 test_regress/t/t_sys_readmem_assoc_c_b.out create mode 100644 test_regress/t/t_sys_readmem_assoc_w_h.out diff --git a/Changes b/Changes index 4844fa77d..dbccd7377 100644 --- a/Changes +++ b/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] diff --git a/include/verilated.cpp b/include/verilated.cpp index dbb0de65f..14521d7b9 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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(memp))[row_offset]; - fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap); - fprintf(fp, "\n"); - } else if (width <= 16) { - const SData* datap = &(reinterpret_cast(memp))[row_offset]; - fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap); - fprintf(fp, "\n"); - } else if (width <= 32) { - const IData* datap = &(reinterpret_cast(memp))[row_offset]; - fprintf(fp, memhFormat(width), VL_MASK_I(width) & *datap); - fprintf(fp, "\n"); - } else if (width <= 64) { - const QData* datap = &(reinterpret_cast(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(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(depth + array_lsb) - || addr < static_cast(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(memp))[entry]; - if (!innum) { *datap = 0; } - *datap = ((*datap << shift) + value) & VL_MASK_I(width); - } else if (width <= 16) { - SData* datap = &(reinterpret_cast(memp))[entry]; - if (!innum) { *datap = 0; } - *datap = ((*datap << shift) + value) & VL_MASK_I(width); - } else if (width <= VL_IDATASIZE) { - IData* datap = &(reinterpret_cast(memp))[entry]; - if (!innum) { *datap = 0; } - *datap = ((*datap << shift) + value) & VL_MASK_I(width); - } else if (width <= VL_QUADSIZE) { - QData* datap = &(reinterpret_cast(memp))[entry]; - if (!innum) { *datap = 0; } - *datap = ((*datap << static_cast(shift)) - + static_cast(value)) & VL_MASK_Q(width); - } else { - WDataOutP datap = &(reinterpret_cast(memp)) - [ entry*VL_WORDS_I(width) ]; - if (!innum) { VL_ZERO_RESET_W(width, datap); } - _VL_SHIFTL_INPLACE_W(width, datap, static_cast(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(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(valuep); + if (!innum) { *datap = 0; } + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= 16) { + SData* datap = reinterpret_cast(valuep); + if (!innum) { *datap = 0; } + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= VL_IDATASIZE) { + IData* datap = reinterpret_cast(valuep); + if (!innum) { *datap = 0; } + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= VL_QUADSIZE) { + QData* datap = reinterpret_cast(valuep); + if (!innum) { *datap = 0; } + *datap = ((*datap << static_cast(shift)) + static_cast(value)) + & VL_MASK_Q(m_bits); + } else { + WDataOutP datap = reinterpret_cast(valuep); + if (!innum) { VL_ZERO_RESET_W(m_bits, datap); } + _VL_SHIFTL_INPLACE_W(m_bits, datap, static_cast(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(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(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(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(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(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(array_lsb) + || addr >= static_cast(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(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= 16) { + SData* datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= VL_IDATASIZE) { + IData* datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= VL_QUADSIZE) { + QData* datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else { + WDataOutP datap = &(reinterpret_cast(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(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 16) { + const SData* datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 32) { + const IData* datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 64) { + const QData* datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else { + WDataInP memDatap = reinterpret_cast(memp); + WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)]; + wmem.print(addr, false, datap); + } + } +} + //=========================================================================== // Timescale conversion diff --git a/include/verilated.h b/include/verilated.h index eec71537f..3c0b0f9d4 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -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, ...); diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 1d9e34529..f1e61a185 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -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& VL_CVT_W_A(WDataInP inp, const VlWide&) { return *((VlWide*)inp); } +template +std::string VL_TO_STRING(const VlWide& 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& obj) { return obj.to_string(); } +template +void VL_READMEM_N(bool hex, int bits, const std::string& filename, + VlAssocArray& 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 +void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, + const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { + VlWriteMem wmem(hex, bits, filename, start, end); + if (VL_UNLIKELY(!wmem.isOpen())) return; + for (typename VlAssocArray::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, diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index b94a3abe0..aebef1637 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -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(")"); diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index 4b05a8194..2fb23cd96 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -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); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 6186ff2da..da4c76c04 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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()); diff --git a/test_regress/t/t_sys_readmem.v b/test_regress/t/t_sys_readmem.v index 66d1a7936..a85269837 100644 --- a/test_regress/t/t_sys_readmem.v +++ b/test_regress/t/t_sys_readmem.v @@ -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"); diff --git a/test_regress/t/t_sys_readmem_assoc.pl b/test_regress/t/t_sys_readmem_assoc.pl new file mode 100755 index 000000000..d9e1753c8 --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc.pl @@ -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; diff --git a/test_regress/t/t_sys_readmem_assoc.v b/test_regress/t/t_sys_readmem_assoc.v new file mode 100644 index 000000000..31c31f052 --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc.v @@ -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 diff --git a/test_regress/t/t_sys_readmem_assoc_bad.out b/test_regress/t/t_sys_readmem_assoc_bad.out new file mode 100644 index 000000000..724b24250 --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc_bad.out @@ -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 diff --git a/test_regress/t/t_sys_readmem_assoc_bad.pl b/test_regress/t/t_sys_readmem_assoc_bad.pl new file mode 100755 index 000000000..43933e6ae --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc_bad.pl @@ -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; diff --git a/test_regress/t/t_sys_readmem_assoc_bad.v b/test_regress/t/t_sys_readmem_assoc_bad.v new file mode 100644 index 000000000..bb3d58455 --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc_bad.v @@ -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 diff --git a/test_regress/t/t_sys_readmem_assoc_c_b.out b/test_regress/t/t_sys_readmem_assoc_c_b.out new file mode 100644 index 000000000..80ea5fa3e --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc_c_b.out @@ -0,0 +1,13 @@ +02 +03 +04 +05 +06 +07 +@8 +10 +@c +14 +15 +@12c +0a diff --git a/test_regress/t/t_sys_readmem_assoc_w_h.out b/test_regress/t/t_sys_readmem_assoc_w_h.out new file mode 100644 index 000000000..daf62faa4 --- /dev/null +++ b/test_regress/t/t_sys_readmem_assoc_w_h.out @@ -0,0 +1,11 @@ +000000000000000000000002 +000000000000000000000003 +000000000000000000000004 +000000000000000000000005 +000000000000000000000006 +000000000000000000000007 +@8 +000000000000000000000010 +@c +000000000000000000000014 +000000000000000000000015 diff --git a/test_regress/t/t_sys_readmem_bad_end.pl b/test_regress/t/t_sys_readmem_bad_end.pl index 9b77a1dd0..debf6df30 100755 --- a/test_regress/t/t_sys_readmem_bad_end.pl +++ b/test_regress/t/t_sys_readmem_bad_end.pl @@ -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); diff --git a/test_regress/t/t_sys_writemem.pl b/test_regress/t/t_sys_writemem.pl index 52810dcc5..ceadb09e2 100755 --- a/test_regress/t/t_sys_writemem.pl +++ b/test_regress/t/t_sys_writemem.pl @@ -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);