From a13b7ac6d3b7cd034bb3964a5d27b402e6de009c Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Wed, 12 Mar 2025 09:26:51 +0100 Subject: [PATCH] separates functional memory into separate unit --- CMakeLists.txt | 1 + src/iss/arch/riscv_hart_common.h | 68 ++++++- src/iss/arch/riscv_hart_m_p.h | 275 ++--------------------------- src/iss/memory/functional_memory.h | 59 +++++++ src/iss/memory/memory_if.cpp | 25 +++ src/iss/memory/memory_if.h | 74 ++++++++ 6 files changed, 239 insertions(+), 263 deletions(-) create mode 100644 src/iss/memory/functional_memory.h create mode 100644 src/iss/memory/memory_if.cpp create mode 100644 src/iss/memory/memory_if.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 285e363..e951e94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(softfloat) set(LIB_SOURCES src/iss/plugin/instruction_count.cpp src/iss/arch/tgc5c.cpp + src/iss/memory/memory_if.cpp src/vm/interp/vm_tgc5c.cpp src/vm/fp_functions.cpp src/iss/debugger/csr_names.cpp diff --git a/src/iss/arch/riscv_hart_common.h b/src/iss/arch/riscv_hart_common.h index b640b0f..e678196 100644 --- a/src/iss/arch/riscv_hart_common.h +++ b/src/iss/arch/riscv_hart_common.h @@ -37,7 +37,9 @@ #include "iss/arch/traits.h" #include "iss/log_categories.h" +#include "iss/memory/memory_if.h" #include "iss/vm_types.h" +#include "util/delegate.h" #include #include #include @@ -317,7 +319,15 @@ inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const break; } } -template struct riscv_hart_common : public BASE { +template struct priv_if { + std::function read_csr; + std::function write_csr; + std::function exec_htif; + uint64_t& tohost; + uint64_t& fromhost; +}; + +template struct riscv_hart_common : public BASE, public memory::memory_elem { const std::array lvl = {{'U', 'S', 'H', 'M'}}; const std::array trap_str = {{"" "Instruction address misaligned", // 0 @@ -490,6 +500,7 @@ template struct riscv_hart_co } return false; }; + iss::status execute_sys_write(arch_if* aif, const std::array& loaded_payload, unsigned mem_type) { std::stringstream io_buf; uint64_t fd = loaded_payload[1]; @@ -742,7 +753,62 @@ template struct riscv_hart_co return iss::Ok; } + priv_if get_priv_if() { + return priv_if{.read_csr = [this](unsigned addr, reg_t& val) -> iss::status { return read_csr(addr, val); }, + .write_csr = [this](unsigned addr, reg_t val) -> iss::status { return write_csr(addr, val); }, + .exec_htif = [this](uint8_t const* data) -> iss::status { return execute_htif(data); }, + .tohost{this->tohost}, + .fromhost{this->fromhost}}; + } + + iss::status execute_htif(uint8_t const* data) { + reg_t cur_data = *reinterpret_cast(data); + // Extract Device (bits 63:56) + uint8_t device = traits::XLEN == 32 ? *reinterpret_cast(data) >> 24 : (cur_data >> 56) & 0xFF; + // Extract Command (bits 55:48) + uint8_t command = traits::XLEN == 32 ? *reinterpret_cast(data) >> 16 : (cur_data >> 48) & 0xFF; + // Extract payload (bits 47:0) + uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; + if(payload_addr & 1) { + CPPLOG(FATAL) << "this->tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr + << "), stopping simulation"; + this->reg.trap_state = std::numeric_limits::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } else if(device == 0 && command == 0) { + std::array loaded_payload; + if(memory.rd_mem(access_type::DEBUG_READ, payload_addr, 8 * sizeof(uint64_t), + reinterpret_cast(loaded_payload.data())) == iss::Err) + CPPLOG(ERR) << "Syscall read went wrong"; + uint64_t syscall_num = loaded_payload.at(0); + if(syscall_num == 64) { // SYS_WRITE + return this->execute_sys_write(this, loaded_payload, traits::MEM); + } else { + CPPLOG(ERR) << "this->tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num + << ") not implemented"; + this->reg.trap_state = std::numeric_limits::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } + } else { + CPPLOG(ERR) << "this->tohost functionality not implemented for device " << device << " and command " << command; + this->reg.trap_state = std::numeric_limits::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } + } + + memory::memory_hierarchy memories; + + virtual memory::memory_if get_mem_if() override { + assert(false || "This function should nevver be called"); + return memory::memory_if{}; + } + + virtual void set_next(memory::memory_if mem_if) { memory = mem_if; }; + protected: + memory::memory_if memory; struct riscv_instrumentation_if : public iss::instrumentation_if { riscv_instrumentation_if(riscv_hart_common& arch) diff --git a/src/iss/arch/riscv_hart_m_p.h b/src/iss/arch/riscv_hart_m_p.h index cbdf752..1e9405a 100644 --- a/src/iss/arch/riscv_hart_m_p.h +++ b/src/iss/arch/riscv_hart_m_p.h @@ -53,11 +53,11 @@ #include #include #include +#include #include #include #include #include - namespace iss { namespace arch { @@ -245,37 +245,9 @@ public: void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } protected: - virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t* const data); - virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data); - - iss::status read_clic(uint64_t addr, unsigned length, uint8_t* const data); - iss::status write_clic(uint64_t addr, unsigned length, const uint8_t* const data); - - using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const); - using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const); - hart_state_type state; - using mem_type = util::sparse_array; - mem_type mem; - std::unordered_map ptw; std::unordered_map atomic_reservation; - uint8_t clic_cfg_reg{0}; - std::array clic_inttrig_reg; - union clic_int_reg_t { - struct { - uint8_t ip; - uint8_t ie; - uint8_t attr; - uint8_t ctl; - }; - uint32_t raw; - }; - std::vector clic_int_reg; - uint8_t clic_mprev_lvl{0}; - uint8_t clic_mact_lvl{0}; - - std::vector tcm; iss::status read_status(unsigned addr, reg_t& val); iss::status write_status(unsigned addr, reg_t val); @@ -284,8 +256,6 @@ protected: iss::status read_ie(unsigned addr, reg_t& val); iss::status write_ie(unsigned addr, reg_t val); iss::status read_ip(unsigned addr, reg_t& val); - iss::status read_intstatus(unsigned addr, reg_t& val); - iss::status write_intthresh(unsigned addr, reg_t val); iss::status write_xtvt(unsigned addr, reg_t val); iss::status write_dcsr(unsigned addr, reg_t val); iss::status read_debug(unsigned addr, reg_t& val); @@ -294,30 +264,20 @@ protected: iss::status write_dpc(unsigned addr, reg_t val); void check_interrupt(); - std::vector> memfn_range; - std::vector> memfn_read; - std::vector> memfn_write; - void insert_mem_range(uint64_t, uint64_t, std::function, std::function); feature_config cfg; unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast(traits::CLIC_NUM_IRQ)) : 16U}; - - std::pair, std::function> replace_mem_access(std::function rd, - std::function wr) { - std::pair, std::function> ret{hart_mem_rd_delegate, hart_mem_wr_delegate}; - hart_mem_rd_delegate = rd; - hart_mem_wr_delegate = wr; - return ret; - } - std::function hart_mem_rd_delegate; - std::function hart_mem_wr_delegate; + memory::functional_memory default_mem; }; template riscv_hart_m_p::riscv_hart_m_p(feature_config cfg) : state() -, cfg(cfg) { +, cfg(cfg) +, default_mem(base::get_priv_if()) { this->rd_func = util::delegate::from(this); this->wr_func = util::delegate::from(this); + this->memories.prepend(*this); + this->memories.append(default_mem); const std::array rwaddrs{{mepc, mtvec, mscratch, mtval}}; for(auto addr : rwaddrs) { @@ -340,42 +300,6 @@ riscv_hart_m_p::riscv_hart_m_p(feature_config cfg) this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null); this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null); - if(FEAT & FEAT_CLIC) { - this->csr_rd_cb[mtvt] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[mtvt] = MK_CSR_WR_CB(write_xtvt); - // this->csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);}; - this->csr_rd_cb[mintstatus] = MK_CSR_RD_CB(read_intstatus); - this->csr_wr_cb[mintstatus] = MK_CSR_WR_CB(write_null); - // this->csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);}; - // this->csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);}; - this->csr_rd_cb[mintthresh] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[mintthresh] = MK_CSR_WR_CB(write_intthresh); - clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0}); - clic_cfg_reg = 0x20; - clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1; - this->csr[mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; - insert_mem_range( - cfg.clic_base, 0x5000UL, - [this](phys_addr_t addr, unsigned length, uint8_t* const data) { return read_clic(addr.val, length, data); }, - [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { return write_clic(addr.val, length, data); }); - } - if(FEAT & FEAT_TCM) { - tcm.resize(cfg.tcm_size); - std::function read_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(tcm.data() + offset, tcm.data() + offset + length, data); - return iss::Ok; - }; - std::function write_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(data, data + length, tcm.data() + offset); - return iss::Ok; - }; - insert_mem_range(cfg.tcm_base, cfg.tcm_size, read_clic_cb, write_clic_cb); - } if(FEAT & FEAT_DEBUG) { this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch); this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug); @@ -386,21 +310,6 @@ riscv_hart_m_p::riscv_hart_m_p(feature_config cfg) this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr); this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug); } - hart_mem_rd_delegate = [this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status { return this->read_mem(a, l, d); }; - hart_mem_wr_delegate = [this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status { return this->write_mem(a, l, d); }; -} - -template -inline void riscv_hart_m_p::insert_mem_range(uint64_t base, uint64_t size, std::function rd_f, - std::function wr_fn) { - std::tuple entry{base, size}; - auto it = std::upper_bound( - memfn_range.begin(), memfn_range.end(), entry, - [](std::tuple const& a, std::tuple const& b) { return std::get<0>(a) < std::get<0>(b); }); - auto idx = std::distance(memfn_range.begin(), it); - memfn_range.insert(it, entry); - memfn_read.insert(std::begin(memfn_read) + idx, rd_f); - memfn_write.insert(std::begin(memfn_write) + idx, wr_fn); } template @@ -432,21 +341,7 @@ iss::status riscv_hart_m_p::read(const address_type type, co this->fault_data = addr; return iss::Err; } - phys_addr_t phys_addr{access, space, addr}; - auto res = iss::Err; - if(!is_fetch(access) && memfn_range.size()) { - auto it = - std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple const& a) { - return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val; - }); - if(it != std::end(memfn_range)) { - auto idx = std::distance(std::begin(memfn_range), it); - res = memfn_read[idx](phys_addr, length, data); - } else - res = hart_mem_rd_delegate(phys_addr, length, data); - } else { - res = hart_mem_rd_delegate(phys_addr, length, data); - } + auto res = this->memory.rd_mem(access, addr, length, data); if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { this->reg.trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault this->fault_data = addr; @@ -466,8 +361,6 @@ iss::status riscv_hart_m_p::read(const address_type type, co return this->read_csr(addr, *reinterpret_cast(data)); } break; case traits::FENCE: { - if((addr + length) > mem.size()) - return iss::Err; return iss::Ok; } break; case traits::RES: { @@ -534,21 +427,7 @@ iss::status riscv_hart_m_p::write(const address_type type, c this->fault_data = addr; return iss::Err; } - phys_addr_t phys_addr{access, space, addr}; - auto res = iss::Err; - if(!is_fetch(access) && memfn_range.size()) { - auto it = - std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple const& a) { - return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val; - }); - if(it != std::end(memfn_range)) { - auto idx = std::distance(std::begin(memfn_range), it); - res = memfn_write[idx](phys_addr, length, data); - } else - res = write_mem(phys_addr, length, data); - } else { - res = write_mem(phys_addr, length, data); - } + auto res = this->memory.wr_mem(access, addr, length, data); if(unlikely(res != iss::Ok && !is_debug(access))) { this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault) this->fault_data = addr; @@ -565,18 +444,8 @@ iss::status riscv_hart_m_p::write(const address_type type, c return iss::Err; return this->write_csr(addr, *reinterpret_cast(data)); } break; - case traits::FENCE: { - if((addr + length) > mem.size()) - return iss::Err; - switch(addr) { - case 2: - case 3: { - ptw.clear(); - auto tvm = state.mstatus.TVM; - return iss::Ok; - } - } - } break; + case traits::FENCE: + break; case traits::RES: { atomic_reservation[addr] = data[0]; } break; @@ -610,7 +479,6 @@ template iss::status riscv_hart_m_p::read_cause(unsigned addr, reg_t& val) { if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - val |= clic_mprev_lvl << 16; val |= state.mstatus.MPIE << 27; val |= state.mstatus.MPP << 28; } else @@ -623,7 +491,6 @@ iss::status riscv_hart_m_p::write_cause(unsigned addr, reg_t if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); - clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; state.mstatus.MPIE = (val >> 27) & 0x1; state.mstatus.MPP = (val >> 28) & 0x3; } else { @@ -655,121 +522,6 @@ iss::status riscv_hart_m_p::read_ip(unsigned addr, reg_t& va return iss::Ok; } -template -iss::status riscv_hart_m_p::read_intstatus(unsigned addr, reg_t& val) { - val = (clic_mact_lvl & 0xff) << 24; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_intthresh(unsigned addr, reg_t val) { - this->csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) { - switch(paddr.val) { - default: { - for(auto offs = 0U; offs < length; ++offs) { - *(data + offs) = mem[(paddr.val + offs) % mem.size()]; - } - } - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) { - mem_type::page_type& p = mem(paddr.val / mem.page_size); - std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask)); - // this->tohost handling in case of riscv-test - // according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754: - if(paddr.access && iss::access_type::FUNC) { - if(paddr.val == this->tohost) { - reg_t cur_data = *reinterpret_cast(data); - // Extract Device (bits 63:56) - uint8_t device = traits::XLEN == 32 - ? *reinterpret_cast(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 24 - : (cur_data >> 56) & 0xFF; - // Extract Command (bits 55:48) - uint8_t command = traits::XLEN == 32 - ? *reinterpret_cast(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 16 - : (cur_data >> 48) & 0xFF; - // Extract payload (bits 47:0) - uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; - if(payload_addr & 1) { - CPPLOG(FATAL) << "this->tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr - << "), stopping simulation"; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } else if(device == 0 && command == 0) { - std::array loaded_payload; - if(read(address_type::PHYSICAL, access_type::DEBUG_READ, traits::MEM, payload_addr, 8 * sizeof(uint64_t), - reinterpret_cast(loaded_payload.data())) == iss::Err) - CPPLOG(ERR) << "Syscall read went wrong"; - uint64_t syscall_num = loaded_payload.at(0); - if(syscall_num == 64) { // SYS_WRITE - return this->execute_sys_write(this, loaded_payload, traits::MEM); - } else { - CPPLOG(ERR) << "this->tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num - << ") not implemented"; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } - } else { - CPPLOG(ERR) << "this->tohost functionality not implemented for device " << device << " and command " << command; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } - } - if((traits::XLEN == 32 && paddr.val == this->fromhost + 4) || (traits::XLEN == 64 && paddr.val == this->fromhost)) { - uint64_t fhostvar = *reinterpret_cast(p.data() + (this->fromhost & mem.page_addr_mask)); - *reinterpret_cast(p.data() + (this->tohost & mem.page_addr_mask)) = fhostvar; - } - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_clic(uint64_t addr, unsigned length, uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - *data = clic_cfg_reg; - for(auto i = 1; i < length; ++i) - *(data + i) = 0; - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - read_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - read_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - } else { - for(auto i = 0U; i < length; ++i) - *(data + i) = 0; - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e); - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - write_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - write_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1 - } - return iss::Ok; -} - template inline void riscv_hart_m_p::reset(uint64_t address) { BASE::reset(address); state.mstatus = hart_state_type::mstatus_reset_val; @@ -835,12 +587,11 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t } if(this->semihosting_cb) { // Check for semihosting call - phys_addr_t p_addr(access_type::DEBUG_READ, traits::MEM, addr - 4); std::array data; // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 - this->read_mem(p_addr, 4, data.data()); - p_addr.val += 8; - this->read_mem(p_addr, 4, data.data() + 4); + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data()); + addr += 8; + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4); const std::array ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; if(data == ref_data) { diff --git a/src/iss/memory/functional_memory.h b/src/iss/memory/functional_memory.h new file mode 100644 index 0000000..ca7e880 --- /dev/null +++ b/src/iss/memory/functional_memory.h @@ -0,0 +1,59 @@ + +#include "iss/arch/riscv_hart_common.h" +#include "iss/vm_types.h" +#include "memory_if.h" +#include +#include + +namespace iss { +namespace memory { +template struct functional_memory : public memory_elem { + using this_class = functional_memory; + constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; + + functional_memory(arch::priv_if hart_if) + : hart_if(hart_if) {} + + ~functional_memory() = default; + + memory_if get_mem_if() override { + return memory_if{.rd_mem{util::delegate::from(this)}, + .wr_mem{util::delegate::from(this)}}; + } + + void set_next(memory_if) override { + // intenrionally left empty, leaf element + } + +private: + iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { + for(auto offs = 0U; offs < length; ++offs) { + *(data + offs) = mem[(addr + offs) % mem.size()]; + } + return iss::Ok; + } + + iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { + mem_type::page_type& p = mem(addr / mem.page_size); + std::copy(data, data + length, p.data() + (addr & mem.page_addr_mask)); + // this->tohost handling in case of riscv-test + // according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754: + if(access && iss::access_type::FUNC) { + if(addr == hart_if.tohost) { + return hart_if.exec_htif(data); + } + if((WORD_LEN == 32 && addr == hart_if.fromhost + 4) || (WORD_LEN == 64 && addr == hart_if.fromhost)) { + uint64_t fhostvar = *reinterpret_cast(p.data() + (hart_if.fromhost & mem.page_addr_mask)); + *reinterpret_cast(p.data() + (hart_if.tohost & mem.page_addr_mask)) = fhostvar; + } + } + return iss::Ok; + } + +protected: + using mem_type = util::sparse_array; + mem_type mem; + arch::priv_if hart_if; +}; +} // namespace memory +} // namespace iss diff --git a/src/iss/memory/memory_if.cpp b/src/iss/memory/memory_if.cpp new file mode 100644 index 0000000..61f7d24 --- /dev/null +++ b/src/iss/memory/memory_if.cpp @@ -0,0 +1,25 @@ +#include "memory_if.h" + +namespace iss { +namespace memory { +void memory_hierarchy::prepend(memory_elem& e) { + hierarchy.push_front(e); + update_chain(); +} +void memory_hierarchy::append(memory_elem& e) { + hierarchy.push_back(e); + update_chain(); +} +void memory_hierarchy::insert_before(memory_elem&) {} +void memory_hierarchy::insert_after(memory_elem&) {} +void memory_hierarchy::replace_last(memory_elem&) {} +void memory_hierarchy::update_chain() { + bool tail = false; + if(hierarchy.size() > 1) + for(size_t i = 1; i < hierarchy.size(); ++i) { + hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if()); + } +} + +} // namespace memory +} // namespace iss \ No newline at end of file diff --git a/src/iss/memory/memory_if.h b/src/iss/memory/memory_if.h new file mode 100644 index 0000000..f1454cd --- /dev/null +++ b/src/iss/memory/memory_if.h @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (C) 2025 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial implementation + ******************************************************************************/ + +#ifndef _MEMORY_MEMORY_IF_ +#define _MEMORY_MEMORY_IF_ + +#include "iss/vm_types.h" +#include +#include +#include +#include + +namespace iss { +namespace memory { + +using rd_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t*); +using wr_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t const*); + +struct memory_if { + util::delegate rd_mem; + util::delegate wr_mem; +}; + +struct memory_elem { + virtual memory_if get_mem_if() = 0; + virtual void set_next(memory_if) = 0; +}; + +struct memory_hierarchy { + void prepend(memory_elem&); + void append(memory_elem&); + void insert_before(memory_elem&); + void insert_after(memory_elem&); + void replace_last(memory_elem&); + +protected: + void update_chain(); + std::deque> hierarchy; +}; + +} // namespace memory +} // namespace iss +#endif \ No newline at end of file