separates functional memory into separate unit
This commit is contained in:
parent
fb0f6255e9
commit
a13b7ac6d3
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
59
src/iss/memory/functional_memory.h
Normal file
59
src/iss/memory/functional_memory.h
Normal 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
|
25
src/iss/memory/memory_if.cpp
Normal file
25
src/iss/memory/memory_if.cpp
Normal 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
|
74
src/iss/memory/memory_if.h
Normal file
74
src/iss/memory/memory_if.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user