|  | // 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()); | 
|  | } | 
|  | } |