From d9078df650a3b79e8b8a873ca93fb707e860ff65 Mon Sep 17 00:00:00 2001 From: Ethan Sifferman Date: Tue, 21 May 2024 14:27:32 -0700 Subject: [PATCH] Fix 4-state value support for $readmem (#5070) (#5078) --- include/verilated.cpp | 81 +++++++++++----------- test_regress/t/t_sys_readmem_4state.mem | 14 ++++ test_regress/t/t_sys_readmem_4state.pl | 26 +++++++ test_regress/t/t_sys_readmem_4state.v | 25 +++++++ test_regress/t/t_sys_readmem_4state_b.out | 6 ++ test_regress/t/t_sys_readmem_4state_h.out | 6 ++ test_regress/t/t_sys_readmem_bad_addr2.mem | 9 +++ test_regress/t/t_sys_readmem_bad_addr2.out | 2 + test_regress/t/t_sys_readmem_bad_addr2.pl | 22 ++++++ test_regress/t/t_sys_readmem_bad_addr2.v | 15 ++++ 10 files changed, 166 insertions(+), 40 deletions(-) create mode 100644 test_regress/t/t_sys_readmem_4state.mem create mode 100755 test_regress/t/t_sys_readmem_4state.pl create mode 100644 test_regress/t/t_sys_readmem_4state.v create mode 100644 test_regress/t/t_sys_readmem_4state_b.out create mode 100644 test_regress/t/t_sys_readmem_4state_h.out create mode 100644 test_regress/t/t_sys_readmem_bad_addr2.mem create mode 100644 test_regress/t/t_sys_readmem_bad_addr2.out create mode 100755 test_regress/t/t_sys_readmem_bad_addr2.pl create mode 100644 test_regress/t/t_sys_readmem_bad_addr2.v diff --git a/include/verilated.cpp b/include/verilated.cpp index fc1bcd26c..3045d01d2 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -2046,22 +2046,26 @@ 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 = ' '; + bool inData = false; + bool ignoreToEol = false; + bool ignoreToComment = false; + bool readingAddress = false; + int lastCh = ' '; // 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 (true) { int c = std::fgetc(m_fp); if (VL_UNLIKELY(c == EOF)) break; + const bool chIs4StateBin + = c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z'; + const bool chIs2StateHex = std::isxdigit(c); + const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin; // 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); + // m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment); // See if previous data value has completed, and if so return if (c == '_') continue; // Ignore _ e.g. inside a number - if (indata && !std::isxdigit(c) && c != 'x' && c != 'X') { + if (inData && !chIs4StateHex) { // printf("Got data @%lx = %s\n", m_addr, valuer.c_str()); ungetc(c, m_fp); addrr = m_addr; @@ -2071,50 +2075,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) { // Parse line if (c == '\n') { ++m_linenum; - ignore_to_eol = false; - reading_addr = false; + ignoreToEol = false; + readingAddress = false; } else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') { - reading_addr = false; + readingAddress = 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 (ignoreToComment && lastCh == '*' && c == '/') { + ignoreToComment = false; + readingAddress = false; + } else if (!ignoreToEol && !ignoreToComment) { + if (lastCh == '/' && c == '*') { + ignoreToComment = true; + } else if (lastCh == '/' && c == '/') { + ignoreToEol = true; } else if (c == '/') { // Part of /* or // } else if (c == '#') { - ignore_to_eol = true; + ignoreToEol = true; } else if (c == '@') { - reading_addr = true; + readingAddress = true; m_anyAddr = true; m_addr = 0; - } - // Check for hex or binary digits as file format requests - else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) { + } else if (readingAddress && chIs2StateHex) { c = std::tolower(c); - const 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 += static_cast(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"); - } + const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0'); + m_addr = (m_addr << 4) + addressValue; + } else if (readingAddress && chIs4StateHex) { + VL_FATAL_MT(m_filename.c_str(), m_linenum, "", + "$readmem address contains 4-state characters"); + } else if (chIs4StateHex) { + inData = true; + valuer += static_cast(c); + if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) { + 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; + lastCh = c; } if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) { @@ -2123,7 +2123,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) { } addrr = m_addr; - return indata; // EOF + return inData; // EOF } void VlReadMem::setData(void* valuep, const std::string& rhs) { const QData shift = m_hex ? 4ULL : 1ULL; @@ -2131,8 +2131,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { // Shift value in for (const auto& i : rhs) { const char c = std::tolower(i); - const int value - = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0')); + const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1) + : (c >= 'a') ? (c - 'a' + 10) + : (c - '0'); if (m_bits <= 8) { CData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; diff --git a/test_regress/t/t_sys_readmem_4state.mem b/test_regress/t/t_sys_readmem_4state.mem new file mode 100644 index 000000000..b3e4ce7a4 --- /dev/null +++ b/test_regress/t/t_sys_readmem_4state.mem @@ -0,0 +1,14 @@ +// DESCRIPTION: Verilator: Verilog Test data file +// +// Copyright 2024 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +0 +1 +x +X +z +Z diff --git a/test_regress/t/t_sys_readmem_4state.pl b/test_regress/t/t_sys_readmem_4state.pl new file mode 100755 index 000000000..d82700967 --- /dev/null +++ b/test_regress/t/t_sys_readmem_4state.pl @@ -0,0 +1,26 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--x-initial unique"], + ); + +execute( + all_run_flags => ["+verilator+rand+reset+1"], + check_finished => 1, + ); + +files_identical("$Self->{obj_dir}/t_sys_readmem_4state_b.mem", "t/t_sys_readmem_4state_b.out"); +files_identical("$Self->{obj_dir}/t_sys_readmem_4state_h.mem", "t/t_sys_readmem_4state_h.out"); + +ok(1); +1; diff --git a/test_regress/t/t_sys_readmem_4state.v b/test_regress/t/t_sys_readmem_4state.v new file mode 100644 index 000000000..e7acd7fc2 --- /dev/null +++ b/test_regress/t/t_sys_readmem_4state.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define STRINGIFY(x) `"x`" + +module t; + reg [3:0] MEMB [6]; + reg [3:0] MEMH [6]; + + initial begin + $readmemb("t/t_sys_readmem_4state.mem", MEMB); + $display("MEMB=%p", MEMB); + $writememh({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_readmem_4state_b.mem"}, MEMB); + + $readmemh("t/t_sys_readmem_4state.mem", MEMH); + $display("MEMH=%p", MEMH); + $writememh({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_readmem_4state_h.mem"}, MEMH); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_sys_readmem_4state_b.out b/test_regress/t/t_sys_readmem_4state_b.out new file mode 100644 index 000000000..4e9829665 --- /dev/null +++ b/test_regress/t/t_sys_readmem_4state_b.out @@ -0,0 +1,6 @@ +0 +1 +1 +1 +1 +1 diff --git a/test_regress/t/t_sys_readmem_4state_h.out b/test_regress/t/t_sys_readmem_4state_h.out new file mode 100644 index 000000000..1292e246a --- /dev/null +++ b/test_regress/t/t_sys_readmem_4state_h.out @@ -0,0 +1,6 @@ +0 +1 +f +f +f +f diff --git a/test_regress/t/t_sys_readmem_bad_addr2.mem b/test_regress/t/t_sys_readmem_bad_addr2.mem new file mode 100644 index 000000000..85086709b --- /dev/null +++ b/test_regress/t/t_sys_readmem_bad_addr2.mem @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Verilog Test data file +// +// Copyright 2024 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +@10x 10x diff --git a/test_regress/t/t_sys_readmem_bad_addr2.out b/test_regress/t/t_sys_readmem_bad_addr2.out new file mode 100644 index 000000000..91a3428af --- /dev/null +++ b/test_regress/t/t_sys_readmem_bad_addr2.out @@ -0,0 +1,2 @@ +%Error: t/t_sys_readmem_bad_addr2.mem:8: $readmem address contains 4-state characters +Aborting... diff --git a/test_regress/t/t_sys_readmem_bad_addr2.pl b/test_regress/t/t_sys_readmem_bad_addr2.pl new file mode 100755 index 000000000..0530bf7e8 --- /dev/null +++ b/test_regress/t/t_sys_readmem_bad_addr2.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_sys_readmem_bad_addr2.v b/test_regress/t/t_sys_readmem_bad_addr2.v new file mode 100644 index 000000000..12f27a92b --- /dev/null +++ b/test_regress/t/t_sys_readmem_bad_addr2.v @@ -0,0 +1,15 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + reg [175:0] hex [15:0]; + + initial begin + $readmemh("t/t_sys_readmem_bad_addr2.mem", hex); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule