| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "mem_area.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cstring> |
| #include <sstream> |
| |
| #include "sv_scoped.h" |
| |
| // DPI exports, defined in prim_util_memload.svh |
| extern "C" { |
| void simutil_memload(const char *file); |
| int simutil_set_mem(int index, const svBitVecVal *val); |
| int simutil_get_mem(int index, svBitVecVal *val); |
| } |
| |
| MemArea::MemArea(const std::string &scope, uint32_t num_words, |
| uint32_t width_byte) |
| : scope_(scope), num_words_(num_words), width_byte_(width_byte) { |
| assert(0 < num_words); |
| assert(width_byte <= SV_MEM_WIDTH_BYTES); |
| } |
| |
| void MemArea::Write(uint32_t word_offset, |
| const std::vector<uint8_t> &data) const { |
| // This "mini buffer" is used to transfer each write to SystemVerilog. |
| // `simutil_set_mem` takes a fixed SV_MEM_WIDTH_BITS-bit vector but it will |
| // only use the bits required for the RAM width. As an example, for a 32-bit |
| // wide RAM only elements 3:0 of `minibuf` will be written to memory. Since |
| // the simulator may still read bits from minibuf it does not use, we must |
| // use a fixed allocation of the full bit vector size to avoid an out of |
| // bounds access. |
| uint8_t minibuf[SV_MEM_WIDTH_BYTES]; |
| memset(minibuf, 0, sizeof minibuf); |
| assert(width_byte_ <= sizeof minibuf); |
| |
| uint32_t data_words = (data.size() + width_byte_ - 1) / width_byte_; |
| assert(word_offset + data_words <= num_words_); |
| |
| for (uint32_t i = 0; i < data_words; ++i) { |
| uint32_t dst_word = word_offset + i; |
| uint32_t phys_addr = ToPhysAddr(dst_word); |
| |
| WriteBuffer(minibuf, data, i * width_byte_, dst_word); |
| WriteFromMinibuf(phys_addr, minibuf, dst_word); |
| } |
| } |
| |
| std::vector<uint8_t> MemArea::Read(uint32_t word_offset, |
| uint32_t num_words) const { |
| assert(word_offset + num_words <= num_words_); |
| |
| uint32_t num_bytes = width_byte_ * num_words; |
| assert(num_words <= num_bytes); |
| |
| // See Write for an explanation for this buffer. |
| uint8_t minibuf[SV_MEM_WIDTH_BYTES]; |
| memset(minibuf, 0, sizeof minibuf); |
| assert(width_byte_ <= sizeof minibuf); |
| |
| std::vector<uint8_t> ret; |
| ret.reserve(num_bytes); |
| |
| for (uint32_t i = 0; i < num_words; ++i) { |
| uint32_t src_word = word_offset + i; |
| uint32_t phys_addr = ToPhysAddr(src_word); |
| |
| ReadToMinibuf(minibuf, phys_addr); |
| ReadBuffer(ret, minibuf, src_word); |
| } |
| |
| return ret; |
| } |
| |
| void MemArea::LoadVmem(const std::string &path) const { |
| SVScoped scoped(scope_.c_str()); |
| // TODO: Add error handling. |
| simutil_memload(path.c_str()); |
| } |
| |
| void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], |
| const std::vector<uint8_t> &data, size_t start_idx, |
| uint32_t dst_word) const { |
| size_t words_left = data.size() - start_idx; |
| size_t to_copy = std::min(words_left, (size_t)width_byte_); |
| if (to_copy < width_byte_) { |
| memset(buf, 0, SV_MEM_WIDTH_BYTES); |
| } |
| memcpy(buf, &data[start_idx], to_copy); |
| } |
| |
| void MemArea::ReadBuffer(std::vector<uint8_t> &data, |
| const uint8_t buf[SV_MEM_WIDTH_BYTES], |
| uint32_t src_word) const { |
| // Append the first width_byte_ bytes of buf to data. |
| std::copy_n(reinterpret_cast<const char *>(buf), width_byte_, |
| std::back_inserter(data)); |
| } |
| |
| void MemArea::ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const { |
| SVScoped scoped(scope_); |
| if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) { |
| std::ostringstream oss; |
| oss << "Could not read memory word at physical index 0x" << std::hex |
| << phys_addr << "."; |
| throw std::runtime_error(oss.str()); |
| } |
| } |
| |
| void MemArea::WriteFromMinibuf(uint32_t phys_addr, const uint8_t *minibuf, |
| uint32_t dst_word) const { |
| SVScoped scoped(scope_); |
| if (!simutil_set_mem(phys_addr, (const svBitVecVal *)minibuf)) { |
| std::ostringstream oss; |
| oss << "Could not set memory at byte offset 0x" << std::hex |
| << dst_word * width_byte_ << "."; |
| throw std::runtime_error(oss.str()); |
| } |
| } |