separates functional memory into separate unit

This commit is contained in:
Eyck Jentzsch 2025-03-12 09:26:51 +01:00
parent fb0f6255e9
commit a13b7ac6d3
6 changed files with 239 additions and 263 deletions

View File

@ -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

View File

@ -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 <array>
#include <cstdint>
#include <elfio/elfio.hpp>
@ -317,7 +319,15 @@ inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const
break;
}
}
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE {
template <typename WORD_TYPE> struct priv_if {
std::function<iss::status(unsigned, WORD_TYPE&)> read_csr;
std::function<iss::status(unsigned, WORD_TYPE)> write_csr;
std::function<iss::status(uint8_t const*)> exec_htif;
uint64_t& tohost;
uint64_t& fromhost;
};
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public memory::memory_elem {
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
const std::array<const char*, 16> trap_str = {{""
"Instruction address misaligned", // 0
@ -490,6 +500,7 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
}
return false;
};
iss::status execute_sys_write(arch_if* aif, const std::array<uint64_t, 8>& loaded_payload, unsigned mem_type) {
std::stringstream io_buf;
uint64_t fd = loaded_payload[1];
@ -742,7 +753,62 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
return iss::Ok;
}
priv_if<reg_t> get_priv_if() {
return priv_if<reg_t>{.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<const reg_t*>(data);
// Extract Device (bits 63:56)
uint8_t device = traits<BASE>::XLEN == 32 ? *reinterpret_cast<uint32_t const*>(data) >> 24 : (cur_data >> 56) & 0xFF;
// Extract Command (bits 55:48)
uint8_t command = traits<BASE>::XLEN == 32 ? *reinterpret_cast<uint32_t const*>(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<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
} else if(device == 0 && command == 0) {
std::array<uint64_t, 8> loaded_payload;
if(memory.rd_mem(access_type::DEBUG_READ, payload_addr, 8 * sizeof(uint64_t),
reinterpret_cast<uint8_t*>(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<BASE>::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<uint32_t>::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<uint32_t>::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<BASE, LOGCAT>& arch)

View File

@ -53,11 +53,11 @@
#include <fmt/format.h>
#include <functional>
#include <iomanip>
#include <iss/memory/functional_memory.h>
#include <sstream>
#include <type_traits>
#include <unordered_map>
#include <util/bit_field.h>
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<uint8_t, 1ULL << 32>;
mem_type mem;
std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
uint8_t clic_cfg_reg{0};
std::array<uint32_t, 32> 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_t> clic_int_reg;
uint8_t clic_mprev_lvl{0};
uint8_t clic_mact_lvl{0};
std::vector<uint8_t> 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<std::tuple<uint64_t, uint64_t>> memfn_range;
std::vector<std::function<mem_read_f>> memfn_read;
std::vector<std::function<mem_write_f>> memfn_write;
void insert_mem_range(uint64_t, uint64_t, std::function<mem_read_f>, std::function<mem_write_f>);
feature_config cfg;
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> replace_mem_access(std::function<mem_read_f> rd,
std::function<mem_write_f> wr) {
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate};
hart_mem_rd_delegate = rd;
hart_mem_wr_delegate = wr;
return ret;
}
std::function<mem_read_f> hart_mem_rd_delegate;
std::function<mem_write_f> hart_mem_wr_delegate;
memory::functional_memory<reg_t> default_mem;
};
template <typename BASE, features_e FEAT, typename LOGCAT>
riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
: state()
, cfg(cfg) {
, cfg(cfg)
, default_mem(base::get_priv_if()) {
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
this->memories.prepend(*this);
this->memories.append(default_mem);
const std::array<unsigned, 4> rwaddrs{{mepc, mtvec, mscratch, mtval}};
for(auto addr : rwaddrs) {
@ -340,42 +300,6 @@ riscv_hart_m_p<BASE, FEAT, LOGCAT>::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<mem_read_f> 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<mem_write_f> 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<BASE, FEAT, LOGCAT>::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 <typename BASE, features_e FEAT, typename LOGCAT>
inline void riscv_hart_m_p<BASE, FEAT, LOGCAT>::insert_mem_range(uint64_t base, uint64_t size, std::function<mem_read_f> rd_f,
std::function<mem_write_f> wr_fn) {
std::tuple<uint64_t, uint64_t> entry{base, size};
auto it = std::upper_bound(
memfn_range.begin(), memfn_range.end(), entry,
[](std::tuple<uint64_t, uint64_t> const& a, std::tuple<uint64_t, uint64_t> 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 <typename BASE, features_e FEAT, typename LOGCAT>
@ -432,21 +341,7 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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<uint64_t, uint64_t> 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<BASE, FEAT, LOGCAT>::read(const address_type type, co
return this->read_csr(addr, *reinterpret_cast<reg_t* const>(data));
} break;
case traits<BASE>::FENCE: {
if((addr + length) > mem.size())
return iss::Err;
return iss::Ok;
} break;
case traits<BASE>::RES: {
@ -534,21 +427,7 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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<uint64_t, uint64_t> 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<BASE, FEAT, LOGCAT>::write(const address_type type, c
return iss::Err;
return this->write_csr(addr, *reinterpret_cast<const reg_t*>(data));
} break;
case traits<BASE>::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<BASE>::FENCE:
break;
case traits<BASE>::RES: {
atomic_reservation[addr] = data[0];
} break;
@ -610,7 +479,6 @@ template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_cause(unsigned addr, reg_t& val) {
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
val = this->csr[addr] & ((1UL << (traits<BASE>::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<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
auto mask = ((1UL << (traits<BASE>::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<BASE, FEAT, LOGCAT>::read_ip(unsigned addr, reg_t& va
return iss::Ok;
}
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_intstatus(unsigned addr, reg_t& val) {
val = (clic_mact_lvl & 0xff) << 24;
return iss::Ok;
}
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write_intthresh(unsigned addr, reg_t val) {
this->csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1;
return iss::Ok;
}
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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 <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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<const reg_t*>(data);
// Extract Device (bits 63:56)
uint8_t device = traits<BASE>::XLEN == 32
? *reinterpret_cast<uint32_t*>(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 24
: (cur_data >> 56) & 0xFF;
// Extract Command (bits 55:48)
uint8_t command = traits<BASE>::XLEN == 32
? *reinterpret_cast<uint32_t*>(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<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
} else if(device == 0 && command == 0) {
std::array<uint64_t, 8> loaded_payload;
if(read(address_type::PHYSICAL, access_type::DEBUG_READ, traits<BASE>::MEM, payload_addr, 8 * sizeof(uint64_t),
reinterpret_cast<uint8_t*>(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<BASE>::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<uint32_t>::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<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
}
}
if((traits<BASE>::XLEN == 32 && paddr.val == this->fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == this->fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (this->fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (this->tohost & mem.page_addr_mask)) = fhostvar;
}
}
return iss::Ok;
}
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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 <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::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 <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_m_p<BASE, FEAT, LOGCAT>::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<BASE, FEAT, LOGCAT>::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<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> 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<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
if(data == ref_data) {

View File

@ -0,0 +1,59 @@
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include "memory_if.h"
#include <util/logging.h>
#include <util/sparse_array.h>
namespace iss {
namespace memory {
template <typename WORD_TYPE> struct functional_memory : public memory_elem {
using this_class = functional_memory<WORD_TYPE>;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
functional_memory(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {}
~functional_memory() = default;
memory_if get_mem_if() override {
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
.wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(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<uint64_t*>(p.data() + (hart_if.fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (hart_if.tohost & mem.page_addr_mask)) = fhostvar;
}
}
return iss::Ok;
}
protected:
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
mem_type mem;
arch::priv_if<WORD_TYPE> hart_if;
};
} // namespace memory
} // namespace iss

View File

@ -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

View File

@ -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 <deque>
#include <functional>
#include <memory>
#include <util/delegate.h>
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<iss::status(access_type, uint64_t, unsigned, uint8_t*)> rd_mem;
util::delegate<iss::status(access_type, uint64_t, unsigned, uint8_t const*)> 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<std::reference_wrapper<memory_elem>> hierarchy;
};
} // namespace memory
} // namespace iss
#endif