16 Commits

Author SHA1 Message Date
31c6bb55f4 applies clang format 2025-03-16 14:38:45 +01:00
63d0162119 adds license header 2025-03-16 13:33:01 +01:00
3b294d9da0 fixes sc_core_adapter wrt refactored memory hierarchy 2025-03-16 12:29:03 +01:00
54233b448d moves mmu related code into mmu unit 2025-03-16 08:50:01 +01:00
e238369e18 cleansup htif call 2025-03-15 06:54:21 +01:00
cfc980a069 Merge branch 'feature/privilege_refactor' into develop 2025-03-14 20:00:07 +01:00
502f3e8df9 fixes htif behavior and instrumentation interface 2025-03-14 19:43:20 +01:00
88475bfa55 changes the io_buf 2025-03-14 12:14:20 +01:00
71260a3ef4 Merge remote-tracking branch 'origin/feature/htif' into develop 2025-03-14 11:32:36 +01:00
23842742a6 factors clic & pmp into separate units 2025-03-13 12:13:41 +01:00
a13b7ac6d3 separates functional memory into separate unit 2025-03-12 09:26:51 +01:00
fb0f6255e9 replaces virtual functions with memory pointers (kind of) 2025-03-11 08:31:25 +01:00
57d5ea92be moves common functionality to base class 2025-03-10 16:00:26 +01:00
383d762abc applies clang-format and updates SystemC HTIF implementation 2025-03-06 12:10:12 +01:00
03cbd305c6 replaces literal constant with symbolic definition 2025-02-28 19:34:07 +01:00
9f5326c110 extends htif for 32bit systems 2025-02-13 13:39:47 +01:00
26 changed files with 2778 additions and 3086 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/mem/memory_if.cpp
src/vm/interp/vm_tgc5c.cpp
src/vm/fp_functions.cpp
src/iss/debugger/csr_names.cpp

View File

@ -1,3 +1,37 @@
/*******************************************************************************
* Copyright (C) 2024 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
******************************************************************************/
#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#define ELFIO_NO_INTTYPES
@ -32,4 +66,4 @@ int main(int argc, char** argv) {
dump::segment_datas(std::cout, reader);
return 0;
}
}

233
src/iss/arch/mstatus.h Normal file
View File

@ -0,0 +1,233 @@
/*******************************************************************************
* 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 _MSTATUS_TYPE
#define _MSTATUS_TYPE
#include <cstdint>
#include <type_traits>
#include <util/bit_field.h>
#include <util/ities.h>
namespace iss {
namespace arch {
template <class T, class Enable = void> struct status {};
// specialization 32bit
template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> {
static inline unsigned SD(T v) { return bit_sub<63, 1>(v); }
// value of XLEN for S-mode
static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); };
// value of XLEN for U-mode
static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); };
// Trap SRET
static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); };
// Timeout Wait
static inline unsigned TW(T v) { return bit_sub<21, 1>(v); };
// Trap Virtual Memory
static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); };
// Make eXecutable Readable
static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); };
// permit Supervisor User Memory access
static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); };
// Modify PRiVilege
static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); };
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
// dirty, some clean/Some dirty
static inline unsigned XS(T v) { return bit_sub<15, 2>(v); };
// floating-point unit status Off/Initial/Clean/Dirty
static inline unsigned FS(T v) { return bit_sub<13, 2>(v); };
// machine previous privilege
static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); };
// supervisor previous privilege
static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); };
// previous machine interrupt-enable
static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); };
// previous supervisor interrupt-enable
static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); };
// previous user interrupt-enable
static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); };
// machine interrupt-enable
static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); };
// supervisor interrupt-enable
static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); };
// user interrupt-enable
static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); };
};
template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> {
public:
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR
// XS==11)))
static inline unsigned SD(T v) { return bit_sub<63, 1>(v); };
// value of XLEN for S-mode
static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); };
// value of XLEN for U-mode
static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); };
// Trap SRET
static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); };
// Timeout Wait
static inline unsigned TW(T v) { return bit_sub<21, 1>(v); };
// Trap Virtual Memory
static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); };
// Make eXecutable Readable
static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); };
// permit Supervisor User Memory access
static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); };
// Modify PRiVilege
static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); };
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
// dirty, some clean/Some dirty
static inline unsigned XS(T v) { return bit_sub<15, 2>(v); };
// floating-point unit status Off/Initial/Clean/Dirty
static inline unsigned FS(T v) { return bit_sub<13, 2>(v); };
// machine previous privilege
static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); };
// supervisor previous privilege
static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); };
// previous machine interrupt-enable
static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); };
// previous supervisor interrupt-enable
static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); };
// previous user interrupt-enable
static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); };
// machine interrupt-enable
static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); };
// supervisor interrupt-enable
static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); };
// user interrupt-enable
static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); };
};
// primary template
template <class T, class Enable = void> struct hart_state {};
// specialization 32bit
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> {
public:
BEGIN_BF_DECL(mstatus_t, T);
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR
// XS==11)))
BF_FIELD(SD, 31, 1);
// Trap SRET
BF_FIELD(TSR, 22, 1);
// Timeout Wait
BF_FIELD(TW, 21, 1);
// Trap Virtual Memory
BF_FIELD(TVM, 20, 1);
// Make eXecutable Readable
BF_FIELD(MXR, 19, 1);
// permit Supervisor User Memory access
BF_FIELD(SUM, 18, 1);
// Modify PRiVilege
BF_FIELD(MPRV, 17, 1);
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
// dirty, some clean/Some dirty
BF_FIELD(XS, 15, 2);
// floating-point unit status Off/Initial/Clean/Dirty
BF_FIELD(FS, 13, 2);
// machine previous privilege
BF_FIELD(MPP, 11, 2);
// supervisor previous privilege
BF_FIELD(SPP, 8, 1);
// previous machine interrupt-enable
BF_FIELD(MPIE, 7, 1);
// previous supervisor interrupt-enable
BF_FIELD(SPIE, 5, 1);
// previous user interrupt-enable
BF_FIELD(UPIE, 4, 1);
// machine interrupt-enable
BF_FIELD(MIE, 3, 1);
// supervisor interrupt-enable
BF_FIELD(SIE, 1, 1);
// user interrupt-enable
BF_FIELD(UIE, 0, 1);
END_BF_DECL();
mstatus_t mstatus;
static const T mstatus_reset_val = 0x1800;
};
// specialization 64bit
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> {
public:
BEGIN_BF_DECL(mstatus_t, T);
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR
// XS==11)))
BF_FIELD(SD, 63, 1);
// value of XLEN for S-mode
BF_FIELD(SXL, 34, 2);
// value of XLEN for U-mode
BF_FIELD(UXL, 32, 2);
// Trap SRET
BF_FIELD(TSR, 22, 1);
// Timeout Wait
BF_FIELD(TW, 21, 1);
// Trap Virtual Memory
BF_FIELD(TVM, 20, 1);
// Make eXecutable Readable
BF_FIELD(MXR, 19, 1);
// permit Supervisor User Memory access
BF_FIELD(SUM, 18, 1);
// Modify PRiVilege
BF_FIELD(MPRV, 17, 1);
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
// dirty, some clean/Some dirty
BF_FIELD(XS, 15, 2);
// floating-point unit status Off/Initial/Clean/Dirty
BF_FIELD(FS, 13, 2);
// machine previous privilege
BF_FIELD(MPP, 11, 2);
// supervisor previous privilege
BF_FIELD(SPP, 8, 1);
// previous machine interrupt-enable
BF_FIELD(MPIE, 7, 1);
// previous supervisor interrupt-enable
BF_FIELD(SPIE, 5, 1);
// previous user interrupt-enable
BF_FIELD(UPIE, 4, 1);
// machine interrupt-enable
BF_FIELD(MIE, 3, 1);
// supervisor interrupt-enable
BF_FIELD(SIE, 1, 1);
// user interrupt-enable
BF_FIELD(UIE, 0, 1);
END_BF_DECL();
mstatus_t mstatus;
static const T mstatus_reset_val = 0x1800;
};
} // namespace arch
} // namespace iss
#endif // _MSTATUS_TYPE

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, 2018, 2021 MINRES Technologies GmbH
* Copyright (C) 2017 - 2025 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -35,18 +35,25 @@
#ifndef _RISCV_HART_COMMON
#define _RISCV_HART_COMMON
#include "iss/vm_types.h"
#include <iss/arch/traits.h>
#include <iss/log_categories.h>
#include <iss/mem/memory_if.h>
#include <iss/vm_types.h>
#include "mstatus.h"
#include "util/delegate.h"
#include <array>
#include <cstdint>
#include <elfio/elfio.hpp>
#include <fmt/format.h>
#include <iss/arch_if.h>
#include <iss/log_categories.h>
#include <iss/semihosting/semihosting.h>
#include <limits>
#include <sstream>
#include <string>
#include <unordered_map>
#include <util/logging.h>
#include <util/sparse_array.h>
#if defined(__GNUC__)
#define likely(x) ::__builtin_expect(!!(x), 1)
@ -59,7 +66,7 @@
namespace iss {
namespace arch {
enum features_e { FEAT_NONE, FEAT_PMP = 1, FEAT_EXT_N = 2, FEAT_CLIC = 4, FEAT_DEBUG = 8, FEAT_TCM = 16 };
enum features_e { FEAT_NONE, FEAT_EXT_N = 1, FEAT_DEBUG = 2 };
enum riscv_csr {
/* user-level CSR */
@ -191,23 +198,6 @@ enum riscv_csr {
dscratch1 = 0x7B3
};
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3, PRIV_D = 4 };
enum {
@ -226,25 +216,6 @@ enum {
ISA_U = 1 << 20
};
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
struct feature_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
unsigned clic_num_irq{16};
unsigned clic_num_trigger{0};
uint64_t tcm_base{0x10000000};
uint64_t tcm_size{0x8000};
uint64_t io_address{0xf0000000};
uint64_t io_addr_mask{0xf0000000};
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
@ -271,62 +242,149 @@ public:
: trap_access(15 << 16, badaddr) {}
};
inline void read_reg_uint32(uint64_t offs, uint32_t& reg, uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs & 0x3) {
case 0:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 1 + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 2 + i);
break;
case 3:
*data = *(reg_ptr + 3);
break;
}
}
template <typename WORD_TYPE> struct priv_if {
using rd_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE&)>;
using wr_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE)>;
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;
std::function<void(uint16_t, uint16_t, WORD_TYPE)> raise_trap; // trap_id, cause, fault_data
std::unordered_map<unsigned, rd_csr_f>& csr_rd_cb;
std::unordered_map<unsigned, wr_csr_f>& csr_wr_cb;
hart_state<WORD_TYPE>& state;
uint8_t& PRIV;
WORD_TYPE& PC;
uint64_t& tohost;
uint64_t& fromhost;
unsigned& max_irq;
};
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public mem::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
"Instruction access fault", // 1
"Illegal instruction", // 2
"Breakpoint", // 3
"Load address misaligned", // 4
"Load access fault", // 5
"Store/AMO address misaligned", // 6
"Store/AMO access fault", // 7
"Environment call from U-mode", // 8
"Environment call from S-mode", // 9
"Reserved", // a
"Environment call from M-mode", // b
"Instruction page fault", // c
"Load page fault", // d
"Reserved", // e
"Store/AMO page fault"}};
const std::array<const char*, 12> irq_str = {{"User software interrupt", "Supervisor software interrupt", "Reserved",
"Machine software interrupt", "User timer interrupt", "Supervisor timer interrupt",
"Reserved", "Machine timer interrupt", "User external interrupt",
"Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
constexpr static unsigned MEM = traits<BASE>::MEM;
using core = BASE;
using this_class = riscv_hart_common<BASE, LOGCAT>;
using phys_addr_t = typename core::phys_addr_t;
using reg_t = typename core::reg_t;
using addr_t = typename core::addr_t;
using rd_csr_f = std::function<iss::status(unsigned addr, reg_t&)>;
using wr_csr_f = std::function<iss::status(unsigned addr, reg_t)>;
#define MK_CSR_RD_CB(FCT) [this](unsigned a, reg_t& r) -> iss::status { return this->FCT(a, r); };
#define MK_CSR_WR_CB(FCT) [this](unsigned a, reg_t r) -> iss::status { return this->FCT(a, r); };
riscv_hart_common()
: state()
, instr_if(*this) {
// reset values
csr[misa] = traits<BASE>::MISA_VAL;
csr[mvendorid] = 0x669;
csr[marchid] = traits<BASE>::MARCHID_VAL;
csr[mimpid] = 1;
if(traits<BASE>::FLEN > 0) {
csr_rd_cb[fcsr] = MK_CSR_RD_CB(read_fcsr);
csr_wr_cb[fcsr] = MK_CSR_WR_CB(write_fcsr);
csr_rd_cb[fflags] = MK_CSR_RD_CB(read_fcsr);
csr_wr_cb[fflags] = MK_CSR_WR_CB(write_fcsr);
csr_rd_cb[frm] = MK_CSR_RD_CB(read_fcsr);
csr_wr_cb[frm] = MK_CSR_WR_CB(write_fcsr);
}
for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
}
if(traits<BASE>::XLEN == 32)
for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
}
for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
}
for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
}
if(traits<BASE>::XLEN == 32)
for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
}
// common regs
const std::array<unsigned, 4> roaddrs{{misa, mvendorid, marchid, mimpid}};
for(auto addr : roaddrs) {
csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
csr_wr_cb[addr] = MK_CSR_WR_CB(write_null);
}
// special handling & overrides
csr_rd_cb[time] = MK_CSR_RD_CB(read_time);
if(traits<BASE>::XLEN == 32)
csr_rd_cb[timeh] = MK_CSR_RD_CB(read_time);
csr_rd_cb[cycle] = MK_CSR_RD_CB(read_cycle);
if(traits<BASE>::XLEN == 32)
csr_rd_cb[cycleh] = MK_CSR_RD_CB(read_cycle);
csr_rd_cb[instret] = MK_CSR_RD_CB(read_instret);
if(traits<BASE>::XLEN == 32)
csr_rd_cb[instreth] = MK_CSR_RD_CB(read_instret);
csr_rd_cb[mcycle] = MK_CSR_RD_CB(read_cycle);
csr_wr_cb[mcycle] = MK_CSR_WR_CB(write_cycle);
if(traits<BASE>::XLEN == 32)
csr_rd_cb[mcycleh] = MK_CSR_RD_CB(read_cycle);
if(traits<BASE>::XLEN == 32)
csr_wr_cb[mcycleh] = MK_CSR_WR_CB(write_cycle);
csr_rd_cb[minstret] = MK_CSR_RD_CB(read_instret);
csr_wr_cb[minstret] = MK_CSR_WR_CB(write_instret);
if(traits<BASE>::XLEN == 32)
csr_rd_cb[minstreth] = MK_CSR_RD_CB(read_instret);
if(traits<BASE>::XLEN == 32)
csr_wr_cb[minstreth] = MK_CSR_WR_CB(write_instret);
csr_rd_cb[mhartid] = MK_CSR_RD_CB(read_hartid);
};
inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs & 0x3) {
case 0:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + i) = *(data + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 1 + i) = *(data + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 2 + i) = *(data + i);
break;
case 3:
*(reg_ptr + 3) = *data;
break;
}
}
struct riscv_hart_common {
riscv_hart_common(){};
~riscv_hart_common() {
if(io_buf.str().length()) {
CPPLOG(INFO) << "tohost send '" << io_buf.str() << "'";
}
};
}
std::unordered_map<std::string, uint64_t> symbol_table;
uint64_t entry_address{0};
uint64_t tohost = std::numeric_limits<uint64_t>::max();
uint64_t fromhost = std::numeric_limits<uint64_t>::max();
std::stringstream io_buf;
bool read_elf_file(std::string name, uint8_t expected_elf_class,
std::function<iss::status(uint64_t, uint64_t, const uint8_t* const)> cb) {
void set_semihosting_callback(semihosting_cb_t<reg_t> cb) { semihosting_cb = cb; };
std::pair<uint64_t, bool> load_file(std::string name, int type) {
return std::make_pair(entry_address, read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64));
}
bool read_elf_file(std::string name, uint8_t expected_elf_class) {
// Create elfio reader
ELFIO::elfio reader;
// Load ELF data
@ -343,8 +401,9 @@ struct riscv_hart_common {
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
const auto seg_data = pseg->get_data();
const auto type = pseg->get_type();
if(type == 1 && fsize > 0) {
auto res = cb(pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
if(type == ELFIO::PT_LOAD && fsize > 0) {
auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
if(res != iss::Ok)
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
}
@ -369,19 +428,18 @@ struct riscv_hart_common {
#endif
}
}
try {
tohost = symbol_table.at("tohost");
} catch(std::out_of_range& e) {
}
try {
fromhost = symbol_table.at("fromhost");
} catch(std::out_of_range& e) {
}
auto to_it = symbol_table.find("tohost");
if(to_it != std::end(symbol_table))
tohost = to_it->second;
auto from_it = symbol_table.find("tohost");
if(from_it != std::end(symbol_table))
tohost = from_it->second;
}
return true;
}
return false;
};
iss::status execute_sys_write(arch_if* aif, const std::array<uint64_t, 8>& loaded_payload, unsigned mem_type) {
uint64_t fd = loaded_payload[1];
uint64_t buf_ptr = loaded_payload[2];
@ -409,6 +467,363 @@ struct riscv_hart_common {
}
return iss::Ok;
}
constexpr bool has_compressed() { return traits<BASE>::MISA_VAL & 0b0100; }
constexpr reg_t get_pc_mask() { return has_compressed() ? (reg_t)~1 : (reg_t)~3; }
void disass_output(uint64_t pc, const std::string instr) override {
// NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]", pc, instr, lvl[this->reg.PRIV],
// (reg_t)state.mstatus,
// this->reg.cycle + cycle_offset);
NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x} {:40} [p:{};c:{}]", pc, instr, lvl[this->reg.PRIV],
this->reg.cycle + cycle_offset);
};
void register_csr(unsigned addr, rd_csr_f f) { csr_rd_cb[addr] = f; }
void register_csr(unsigned addr, wr_csr_f f) { csr_wr_cb[addr] = f; }
void register_csr(unsigned addr, rd_csr_f rdf, wr_csr_f wrf) {
csr_rd_cb[addr] = rdf;
csr_wr_cb[addr] = wrf;
}
void unregister_csr_rd(unsigned addr) { csr_rd_cb.erase(addr); }
void unregister_csr_wr(unsigned addr) { csr_wr_cb.erase(addr); }
bool debug_mode_active() { return this->reg.PRIV & 0x4; }
const reg_t& get_mhartid() const { return mhartid_reg; }
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
iss::status read_csr(unsigned addr, reg_t& val) {
if(addr >= csr.size())
return iss::Err;
auto req_priv_lvl = (addr >> 8) & 0x3;
if(this->reg.PRIV < req_priv_lvl) // not having required privileges
throw illegal_instruction_fault(this->fault_data);
auto it = csr_rd_cb.find(addr);
if(it == csr_rd_cb.end() || !it->second) // non existent register
throw illegal_instruction_fault(this->fault_data);
return it->second(addr, val);
}
iss::status write_csr(unsigned addr, reg_t val) {
if(addr >= csr.size())
return iss::Err;
auto req_priv_lvl = (addr >> 8) & 0x3;
if(this->reg.PRIV < req_priv_lvl) // not having required privileges
throw illegal_instruction_fault(this->fault_data);
if((addr & 0xc00) == 0xc00) // writing to read-only region
throw illegal_instruction_fault(this->fault_data);
auto it = csr_wr_cb.find(addr);
if(it == csr_wr_cb.end() || !it->second) // non existent register
throw illegal_instruction_fault(this->fault_data);
return it->second(addr, val);
}
iss::status read_null(unsigned addr, reg_t& val) {
val = 0;
return iss::Ok;
}
iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; }
iss::status read_plain(unsigned addr, reg_t& val) {
val = csr[addr];
return iss::Ok;
}
iss::status write_plain(unsigned addr, reg_t val) {
csr[addr] = val;
return iss::Ok;
}
iss::status read_cycle(unsigned addr, reg_t& val) {
auto cycle_val = this->reg.cycle + cycle_offset;
if(addr == mcycle) {
val = static_cast<reg_t>(cycle_val);
} else if(addr == mcycleh) {
val = static_cast<reg_t>(cycle_val >> 32);
}
return iss::Ok;
}
iss::status write_cycle(unsigned addr, reg_t val) {
if(sizeof(typename traits<BASE>::reg_t) != 4) {
mcycle_csr = static_cast<uint64_t>(val);
} else {
if(addr == mcycle) {
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
} else {
mcycle_csr = (static_cast<uint64_t>(val) << 32) + (mcycle_csr & 0xffffffff);
}
}
cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around
return iss::Ok;
}
iss::status read_instret(unsigned addr, reg_t& val) {
if((addr & 0xff) == (minstret & 0xff)) {
val = static_cast<reg_t>(this->reg.instret);
} else if((addr & 0xff) == (minstreth & 0xff)) {
val = static_cast<reg_t>(this->reg.instret >> 32);
}
return iss::Ok;
}
iss::status write_instret(unsigned addr, reg_t val) {
if(sizeof(typename traits<BASE>::reg_t) != 4) {
this->reg.instret = static_cast<uint64_t>(val);
} else {
if((addr & 0xff) == (minstret & 0xff)) {
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
} else {
this->reg.instret = (static_cast<uint64_t>(val) << 32) + (this->reg.instret & 0xffffffff);
}
}
this->reg.instret--;
return iss::Ok;
}
iss::status read_time(unsigned addr, reg_t& val) {
uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052;
if(addr == time) {
val = static_cast<reg_t>(time_val);
} else if(addr == timeh) {
if(sizeof(typename traits<BASE>::reg_t) != 4)
return iss::Err;
val = static_cast<reg_t>(time_val >> 32);
}
return iss::Ok;
}
iss::status read_tvec(unsigned addr, reg_t& val) {
val = csr[addr] & ~2;
return iss::Ok;
}
iss::status read_hartid(unsigned addr, reg_t& val) {
val = mhartid_reg;
return iss::Ok;
}
iss::status write_epc(unsigned addr, reg_t val) {
csr[addr] = val & get_pc_mask();
return iss::Ok;
}
iss::status write_dcsr(unsigned addr, reg_t val) {
if(!debug_mode_active())
throw illegal_instruction_fault(this->fault_data);
// +-------------- ebreakm
// | +---------- stepi
// | | +++----- cause
// | | ||| +- step
csr[addr] = val & 0b1000100111000100U;
return iss::Ok;
}
iss::status read_debug(unsigned addr, reg_t& val) {
if(!debug_mode_active())
throw illegal_instruction_fault(this->fault_data);
val = csr[addr];
return iss::Ok;
}
iss::status write_dscratch(unsigned addr, reg_t val) {
if(!debug_mode_active())
throw illegal_instruction_fault(this->fault_data);
csr[addr] = val;
return iss::Ok;
}
iss::status read_dpc(unsigned addr, reg_t& val) {
if(!debug_mode_active())
throw illegal_instruction_fault(this->fault_data);
val = this->reg.DPC;
return iss::Ok;
}
iss::status write_dpc(unsigned addr, reg_t val) {
if(!debug_mode_active())
throw illegal_instruction_fault(this->fault_data);
this->reg.DPC = val;
return iss::Ok;
}
iss::status read_fcsr(unsigned addr, reg_t& val) {
switch(addr) {
case 1: // fflags, 4:0
val = bit_sub<0, 5>(this->get_fcsr());
break;
case 2: // frm, 7:5
val = bit_sub<5, 3>(this->get_fcsr());
break;
case 3: // fcsr
val = this->get_fcsr();
break;
default:
return iss::Err;
}
return iss::Ok;
}
iss::status write_fcsr(unsigned addr, reg_t val) {
switch(addr) {
case 1: // fflags, 4:0
this->set_fcsr((this->get_fcsr() & 0xffffffe0) | (val & 0x1f));
break;
case 2: // frm, 7:5
this->set_fcsr((this->get_fcsr() & 0xffffff1f) | ((val & 0x7) << 5));
break;
case 3: // fcsr
this->set_fcsr(val & 0xff);
break;
default:
return iss::Err;
}
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); },
.raise_trap =
[this](uint16_t trap_id, uint16_t cause, reg_t fault_data) {
this->reg.trap_state = 0x80ULL << 24 | (cause << 16) | trap_id;
this->fault_data = fault_data;
},
.csr_rd_cb{this->csr_rd_cb},
.csr_wr_cb{csr_wr_cb},
.state{this->state},
.PRIV{this->reg.PRIV},
.PC{this->reg.PC},
.tohost{this->tohost},
.fromhost{this->fromhost},
.max_irq{mcause_max_irq}};
}
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 ? 0 : (cur_data >> 56) & 0xFF;
// Extract Command (bits 55:48)
uint8_t command = traits<BASE>::XLEN == 32 ? 0 : (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;
}
}
mem::memory_hierarchy memories;
mem::memory_if get_mem_if() override {
assert(false || "This function should never be called");
return mem::memory_if{};
}
void set_next(mem::memory_if mem_if) override { memory = mem_if; };
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
protected:
hart_state<reg_t> state;
static constexpr reg_t get_mstatus_mask_t(unsigned priv_lvl = PRIV_M) {
if(sizeof(reg_t) == 4) {
return priv_lvl == PRIV_U ? 0x80000011UL : // 0b1...0 0001 0001
priv_lvl == PRIV_S ? 0x800de133UL // 0b0...0 0001 1000 1001 1001;
: 0x807ff9ddUL;
} else {
return priv_lvl == PRIV_U ? 0x011ULL : // 0b1...0 0001 0001
priv_lvl == PRIV_S ? 0x000de133ULL
: 0x007ff9ddULL;
}
}
mem::memory_if memory;
struct riscv_instrumentation_if : public iss::instrumentation_if {
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch)
: arch(arch) {}
/**
* get the name of this architecture
*
* @return the name of this architecture
*/
const std::string core_type_name() const override { return traits<BASE>::core_type; }
uint64_t get_pc() override { return arch.reg.PC; }
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; }
uint64_t get_instr_word() override { return arch.reg.instruction; }
uint64_t get_instr_count() override { return arch.reg.icount; }
uint64_t get_pendig_traps() override { return arch.reg.trap_state; }
uint64_t get_total_cycles() override { return arch.reg.cycle + arch.cycle_offset; }
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; }
bool is_branch_taken() override { return arch.reg.last_branch; }
unsigned get_reg_num() override { return traits<BASE>::NUM_REGS; }
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; }
riscv_hart_common<BASE, LOGCAT>& arch;
};
friend struct riscv_instrumentation_if;
riscv_instrumentation_if instr_if;
instrumentation_if* get_instrumentation_if() override { return &instr_if; };
using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
using csr_page_type = typename csr_type::page_type;
csr_type csr;
std::unordered_map<unsigned, rd_csr_f> csr_rd_cb;
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
reg_t mhartid_reg{0x0};
uint64_t mcycle_csr{0};
uint64_t minstret_csr{0};
reg_t fault_data;
int64_t cycle_offset{0};
int64_t instret_offset{0};
semihosting_cb_t<reg_t> semihosting_cb;
unsigned mcause_max_irq{16U};
};
} // namespace arch

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -244,7 +244,6 @@ struct tgc5c: public arch_if {
uint32_t last_branch = 0;
} reg;
#pragma pack(pop)
std::array<address_type, 4> addr_mode;
uint64_t interrupt_sim=0;

View File

@ -1,3 +1,37 @@
/*******************************************************************************
* Copyright (C) 2023 - 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 _ISS_ARCH_TGC_MAPPER_H
#define _ISS_ARCH_TGC_MAPPER_H
@ -23,35 +57,29 @@ using tgc5c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arc
#ifdef CORE_TGC5D
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d.h>
using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N)>;
using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5D_XRB_MAC
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d_xrb_mac.h>
using tgc5d_xrb_mac_plat_type =
iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac,
(iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
using tgc5d_xrb_mac_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac(iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5D_XRB_NN
#include "hwl.h"
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d_xrb_nn.h>
using tgc5d_xrb_nn_plat_type =
iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn,
(iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>;
iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>>;
#endif
#ifdef CORE_TGC5E
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5e.h>
using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N)>;
using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5X
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5x.h>
using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>;
using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#endif

View File

@ -1,3 +1,37 @@
/*******************************************************************************
* 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
******************************************************************************/
#include <array>
// generated from:
// * /scratch/eyck/workarea/Other/riscv-opcodes/csrs.csv

285
src/iss/mem/clic.h Normal file
View File

@ -0,0 +1,285 @@
/*******************************************************************************
* 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
******************************************************************************/
#include "memory_if.h"
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include <util/logging.h>
namespace iss {
namespace mem {
struct clic_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
unsigned clic_num_irq{16};
unsigned clic_num_trigger{0};
bool nmode{false};
};
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 1 + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 2 + i);
break;
case 3:
*data = *(reg_ptr + 3);
break;
}
}
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + i) = *(data + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 1 + i) = *(data + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 2 + i) = *(data + i);
break;
case 3:
*(reg_ptr + 3) = *data;
break;
}
}
template <typename WORD_TYPE> struct clic : public memory_elem {
using this_class = clic<WORD_TYPE>;
using reg_t = WORD_TYPE;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
clic(arch::priv_if<WORD_TYPE> hart_if, clic_config cfg)
: hart_if(hart_if)
, cfg(cfg) {
clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0});
clic_cfg_reg = 0x30;
clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
clic_uact_lvl = clic_uprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
hart_if.csr_rd_cb[arch::mtvt] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[arch::mtvt] = MK_CSR_WR_CB(write_xtvt);
// hart_if.csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);};
// hart_if.csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);};
hart_if.csr_rd_cb[arch::mintstatus] = MK_CSR_RD_CB(read_intstatus);
hart_if.csr_wr_cb[arch::mintstatus] = MK_CSR_WR_CB(write_null);
// hart_if.csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);};
// hart_if.csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);};
// hart_if.csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);};
// hart_if.csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);};
hart_if.csr_rd_cb[arch::mintthresh] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[arch::mintthresh] = MK_CSR_WR_CB(write_intthresh);
if(cfg.nmode) {
hart_if.csr_rd_cb[arch::utvt] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[arch::utvt] = MK_CSR_WR_CB(write_xtvt);
hart_if.csr_rd_cb[arch::uintstatus] = MK_CSR_RD_CB(read_intstatus);
hart_if.csr_wr_cb[arch::uintstatus] = MK_CSR_WR_CB(write_null);
hart_if.csr_rd_cb[arch::uintthresh] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[arch::uintthresh] = MK_CSR_WR_CB(write_intthresh);
}
hart_if.csr[arch::mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
hart_if.csr[arch::uintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
}
~clic() = 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 mem) override { down_stream_mem = mem; }
std::tuple<uint64_t, uint64_t> get_range() override { return {cfg.clic_base, cfg.clic_base + 0x7fff}; }
private:
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000))
return read_clic(addr, length, data);
return down_stream_mem.rd_mem(access, addr, length, data);
}
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000))
return write_clic(addr, length, data);
return down_stream_mem.wr_mem(access, addr, length, data);
}
iss::status read_clic(uint64_t addr, unsigned length, uint8_t* data);
iss::status write_clic(uint64_t addr, unsigned length, uint8_t const* data);
iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; }
iss::status read_plain(unsigned addr, reg_t& val) {
val = hart_if.csr[addr];
return iss::Ok;
}
iss::status write_xtvt(unsigned addr, reg_t val) {
hart_if.csr[addr] = val & ~0x3fULL;
return iss::Ok;
}
iss::status read_cause(unsigned addr, reg_t& val);
iss::status write_cause(unsigned addr, reg_t val);
iss::status read_intstatus(unsigned addr, reg_t& val);
iss::status write_intthresh(unsigned addr, reg_t val);
protected:
arch::priv_if<WORD_TYPE> hart_if;
memory_if down_stream_mem;
clic_config cfg;
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}, clic_uprev_lvl{0};
uint8_t clic_mact_lvl{0}, clic_uact_lvl{0};
};
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::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_with_offset(clic_inttrig_reg[offset], addr & 0x3, 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_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length);
} else {
for(auto i = 0U; i < length; ++i)
*(data + i) = 0;
}
return iss::Ok;
}
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::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_with_offset(clic_inttrig_reg[offset], addr & 0x3, 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_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length);
clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1
}
return iss::Ok;
}
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_cause(unsigned addr, reg_t& val) {
if((hart_if.csr[arch::mtvec] & 0x3) == 3) {
val = hart_if.csr[addr] & (1UL << (sizeof(reg_t) * 8) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16));
auto mode = (addr >> 8) & 0x3;
switch(mode) {
case 0:
val |= clic_uprev_lvl << 16;
val |= hart_if.state.mstatus.UPIE << 27;
break;
default:
val |= clic_mprev_lvl << 16;
val |= hart_if.state.mstatus.MPIE << 27;
val |= hart_if.state.mstatus.MPP << 28;
break;
}
} else
val = hart_if.csr[addr] & ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1));
return iss::Ok;
}
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_cause(unsigned addr, reg_t val) {
if((hart_if.csr[arch::mtvec] & 0x3) == 3) {
auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16));
hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask);
auto mode = (addr >> 8) & 0x3;
switch(mode) {
case 0:
clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
hart_if.state.mstatus.UPIE = (val >> 27) & 0x1;
break;
default:
clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
hart_if.state.mstatus.MPIE = (val >> 27) & 0x1;
hart_if.state.mstatus.MPP = (val >> 28) & 0x3;
break;
}
} else {
auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1));
hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask);
}
return iss::Ok;
}
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_intstatus(unsigned addr, reg_t& val) {
auto mode = (addr >> 8) & 0x3;
val = clic_uact_lvl & 0xff;
if(mode == 0x3)
val += (clic_mact_lvl & 0xff) << 24;
return iss::Ok;
}
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_intthresh(unsigned addr, reg_t val) {
hart_if.csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1;
return iss::Ok;
}
} // namespace mem
} // namespace iss

101
src/iss/mem/memory_if.cpp Normal file
View File

@ -0,0 +1,101 @@
/*******************************************************************************
* 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
******************************************************************************/
#include "memory_if.h"
#include <algorithm>
namespace iss {
namespace mem {
void memory_hierarchy::root(memory_elem& e) {
hierarchy.push_front(&e);
root_set = true;
update_chain();
}
void memory_hierarchy::prepend(memory_elem& e) {
if(root_set)
hierarchy.insert(hierarchy.begin() + 1, &e);
else
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& e) {
auto old = hierarchy.back();
auto it = std::find_if(std::begin(owned_elems), std::end(owned_elems),
[old](std::unique_ptr<memory_elem> const& p) { return p.get() == old; });
hierarchy.pop_back();
if(it != std::end(owned_elems))
owned_elems.erase(it);
hierarchy.push_back(&e);
update_chain();
}
void memory_hierarchy::update_chain() {
bool tail = false;
for(size_t i = 1; i < hierarchy.size(); ++i) {
hierarchy[i - 1]->set_next(hierarchy[i]->get_mem_if());
}
}
void memory_hierarchy::prepend(std::unique_ptr<memory_elem>&& p) {
prepend(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::append(std::unique_ptr<memory_elem>&& p) {
append(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::insert_before(std::unique_ptr<memory_elem>&& p) {
insert_before(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::insert_after(std::unique_ptr<memory_elem>&& p) {
insert_after(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::replace_last(std::unique_ptr<memory_elem>&& p) {
replace_last(*p);
owned_elems.push_back(std::move(p));
}
} // namespace mem
} // namespace iss

86
src/iss/mem/memory_if.h Normal file
View File

@ -0,0 +1,86 @@
/*******************************************************************************
* 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 <limits>
#include <memory>
#include <util/delegate.h>
#include <vector>
namespace iss {
namespace mem {
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_elem() = default;
virtual memory_if get_mem_if() = 0;
virtual void set_next(memory_if) = 0;
virtual std::tuple<uint64_t, uint64_t> get_range() { return {0, std::numeric_limits<uint64_t>::max()}; }
};
struct memory_hierarchy {
void root(memory_elem&);
void prepend(memory_elem&);
void append(memory_elem&);
void insert_before(memory_elem&);
void insert_after(memory_elem&);
void replace_last(memory_elem&);
void prepend(std::unique_ptr<memory_elem>&&);
void append(std::unique_ptr<memory_elem>&&);
void insert_before(std::unique_ptr<memory_elem>&&);
void insert_after(std::unique_ptr<memory_elem>&&);
void replace_last(std::unique_ptr<memory_elem>&&);
protected:
void update_chain();
std::deque<memory_elem*> hierarchy;
std::vector<std::unique_ptr<memory_elem>> owned_elems;
bool root_set{false};
};
} // namespace mem
} // namespace iss
#endif

View File

@ -0,0 +1,90 @@
/*******************************************************************************
* 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_WITH_HTIF_
#define _MEMORY_WITH_HTIF_
#include "memory_if.h"
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include <util/logging.h>
#include <util/sparse_array.h>
namespace iss {
namespace mem {
template <typename WORD_TYPE> struct memory_with_htif : public memory_elem {
using this_class = memory_with_htif<WORD_TYPE>;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
memory_with_htif(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {}
~memory_with_htif() = 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 && addr == hart_if.tohost) {
return hart_if.exec_htif(data);
}
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 mem
} // namespace iss
#endif // _MEMORY_WITH_HTIF_

353
src/iss/mem/mmu.h Normal file
View File

@ -0,0 +1,353 @@
/*******************************************************************************
* 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
******************************************************************************/
#include "memory_if.h"
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include <util/logging.h>
namespace iss {
namespace mem {
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 1 + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 2 + i);
break;
case 3:
*data = *(reg_ptr + 3);
break;
}
}
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + i) = *(data + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 1 + i) = *(data + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 2 + i) = *(data + i);
break;
case 3:
*(reg_ptr + 3) = *data;
break;
}
}
// TODO: update vminfo on trap enter and leave as well as mstatus write, reset
template <typename WORD_TYPE> struct mmu : public memory_elem {
using this_class = mmu<WORD_TYPE>;
using reg_t = WORD_TYPE;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
constexpr static reg_t PGSIZE = 1 << PGSHIFT;
constexpr static reg_t PGMASK = PGSIZE - 1;
mmu(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {
hart_if.csr_rd_cb[satp] = MK_CSR_RD_CB(read_satp);
hart_if.csr_wr_cb[satp] = MK_CSR_WR_CB(write_satp);
}
virtual ~mmu() = 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 mem) override { down_stream_mem = mem; }
private:
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(hart_if.PRIV, satp);
if(vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = down_stream_mem.rd_mem(access, addr, len1, data);
if(res == iss::Ok)
res = down_stream_mem.rd_mem(access, split_addr, length - len1, data + len1);
return res;
}
}
return down_stream_mem.rd_mem(access, addr, length, data);
}
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(hart_if.PRIV, satp);
if(vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = down_stream_mem.wr_mem(access, addr, len1, data);
if(res == iss::Ok)
res = down_stream_mem.wr_mem(access, split_addr, length - len1, data + len1);
return res;
}
}
return down_stream_mem.wr_mem(access, virt2phys(access, addr), length, data);
}
void update_vm_info();
iss::status read_plain(unsigned addr, reg_t& val) {
val = hart_if.csr[addr];
return iss::Ok;
}
iss::status write_plain(unsigned addr, reg_t const& val) {
hart_if.csr[addr] = val;
return iss::Ok;
}
iss::status read_satp(unsigned addr, reg_t& val) {
auto tvm = bit_sub<20, 1>(hart_if.state.mstatus());
if(hart_if.PRIV == arch::PRIV_S & tvm != 0) {
hart_if.raise_trap(2, 0, hart_if.PC);
// hart_if.reg.trap_state = (1 << 31) | (2 << 16);
// hart_if.fault_data = hart_if.reg.PC;
return iss::Err;
}
val = satp;
return iss::Ok;
}
iss::status write_satp(unsigned addr, reg_t val) {
reg_t tvm = hart_if.state.mstatus.TVM;
if(hart_if.PRIV == arch::PRIV_S & tvm != 0) {
hart_if.raise_trap(2, 0, hart_if.PC);
// hart_if.reg.trap_state = (1 << 31) | (2 << 16);
// hart_if.fault_data = hart_if.reg.PC;
return iss::Err;
}
satp = val;
update_vm_info();
return iss::Ok;
}
uint64_t virt2phys(iss::access_type access, uint64_t addr);
static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) {
if(state == arch::PRIV_M)
return {0, 0, 0, 0};
if(state <= arch::PRIV_S)
switch(bit_sub<31, 1>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 1:
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
static inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr) {
if(state == arch::PRIV_M)
return {0, 0, 0, 0};
if(state <= arch::PRIV_S)
switch(bit_sub<60, 4>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 8:
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
case 9:
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
case 10:
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
case 11:
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
protected:
reg_t satp;
std::unordered_map<reg_t, uint64_t> ptw;
std::array<vm_info, 2> vmt;
std::array<address_type, 4> addr_mode;
arch::priv_if<WORD_TYPE> hart_if;
memory_if down_stream_mem;
};
template <typename WORD_TYPE> uint64_t mmu<WORD_TYPE>::virt2phys(iss::access_type access, uint64_t addr) {
const auto type = access & iss::access_type::FUNC;
auto it = ptw.find(addr >> PGSHIFT);
if(it != ptw.end()) {
const reg_t pte = it->second;
const reg_t ad = PTE_A | (type == iss::access_type::WRITE) * PTE_D;
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
*(uint32_t*)ppte |= ad;
return {addr.getAccessType(), addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)};
#else
// take exception if access or possibly dirty bit is not set.
if((pte & ad) == ad)
return {(pte & (~PGMASK)) | (addr & PGMASK)};
else
ptw.erase(it); // throw an exception
#endif
} else {
uint32_t mode = type != iss::access_type::FETCH && hart_if.state.mstatus.MPRV ? // MPRV
hart_if.state.mstatus.MPP
: hart_if.PRIV;
const vm_info& vm = vmt[static_cast<uint16_t>(type) / 2];
const bool s_mode = mode == arch::PRIV_S;
const bool sum = hart_if.state.mstatus.SUM;
const bool mxr = hart_if.state.mstatus.MXR;
// verify bits xlen-1:va_bits-1 are all equal
const int va_bits = PGSHIFT + vm.levels * vm.idxbits;
const reg_t mask = (reg_t(1) << (sizeof(reg_t) * 8 - (va_bits - 1))) - 1;
const reg_t masked_msbs = (addr >> (va_bits - 1)) & mask;
const int levels = (masked_msbs != 0 && masked_msbs != mask) ? 0 : vm.levels;
reg_t base = vm.ptbase;
for(int i = levels - 1; i >= 0; i--) {
const int ptshift = i * vm.idxbits;
const reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
// check that physical address of PTE is legal
reg_t pte = 0;
const uint8_t res = down_stream_mem.rd_mem(iss::access_type::READ, base + idx * vm.ptesize, vm.ptesize, (uint8_t*)&pte);
if(res != 0)
throw arch::trap_load_access_fault(addr);
const reg_t ppn = pte >> PTE_PPN_SHIFT;
if(PTE_TABLE(pte)) { // next level of page table
base = ppn << PGSHIFT;
} else if((pte & PTE_U) ? s_mode && (type == iss::access_type::FETCH || !sum) : !s_mode) {
break;
} else if(!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break;
} else if(type == (type == iss::access_type::FETCH ? !(pte & PTE_X)
: type == iss::access_type::READ ? !(pte & PTE_R) && !(mxr && (pte & PTE_X))
: !((pte & PTE_R) && (pte & PTE_W)))) {
break;
} else if((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
break;
} else {
const reg_t ad = PTE_A | ((type == iss::access_type::WRITE) * PTE_D);
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
*(uint32_t*)ppte |= ad;
#else
// take exception if access or possibly dirty bit is not set.
if((pte & ad) != ad)
break;
#endif
// for superpage mappings, make a fake leaf PTE for the TLB's benefit.
const reg_t vpn = addr >> PGSHIFT;
const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
const reg_t offset = addr & PGMASK;
ptw[vpn] = value | (pte & 0xff);
return value | offset;
}
}
}
switch(type) {
case access_type::FETCH:
hart_if.raise_trap(12, 0, addr);
throw arch::trap_instruction_page_fault(addr);
case access_type::READ:
hart_if.raise_trap(13, 0, addr);
throw arch::trap_load_page_fault(addr);
case access_type::WRITE:
hart_if.raise_trap(15, 0, addr);
throw arch::trap_store_page_fault(addr);
default:
abort();
}
}
template <typename WORD_TYPE> inline void mmu<WORD_TYPE>::update_vm_info() {
vmt[1] = decode_vm_info(hart_if.PRIV, satp);
addr_mode[3] = addr_mode[2] = vmt[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
if(hart_if.state.mstatus.MPRV)
vmt[0] = decode_vm_info(hart_if.state.mstatus.MPP, satp);
else
vmt[0] = vmt[1];
addr_mode[1] = addr_mode[0] = vmt[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
ptw.clear();
}
} // namespace mem
} // namespace iss

244
src/iss/mem/pmp.h Normal file
View File

@ -0,0 +1,244 @@
/*******************************************************************************
* 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
******************************************************************************/
#include "memory_if.h"
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include <util/logging.h>
namespace iss {
namespace mem {
struct clic_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
unsigned clic_num_irq{16};
unsigned clic_num_trigger{0};
bool nmode{false};
};
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 1 + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 2 + i);
break;
case 3:
*data = *(reg_ptr + 3);
break;
}
}
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + i) = *(data + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 1 + i) = *(data + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 2 + i) = *(data + i);
break;
case 3:
*(reg_ptr + 3) = *data;
break;
}
}
template <typename WORD_TYPE> struct pmp : public memory_elem {
using this_class = pmp<WORD_TYPE>;
using reg_t = WORD_TYPE;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
pmp(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {
for(size_t i = arch::pmpaddr0; i <= arch::pmpaddr15; ++i) {
hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_plain);
}
for(size_t i = arch::pmpcfg0; i < arch::pmpcfg0 + 16 / sizeof(reg_t); ++i) {
hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_pmpcfg);
}
}
virtual ~pmp() = 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 mem) override { down_stream_mem = mem; }
private:
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
if(!pmp_check(access, addr, length) && !is_debug(access)) {
hart_if.fault_data = addr;
if(is_debug(access))
throw trap_access(0, addr);
hart_if.reg.trap_state = (1UL << 31) | ((access == access_type::FETCH ? 1 : 5) << 16); // issue trap 1
return iss::Err;
}
return down_stream_mem.rd_mem(access, addr, length, data);
}
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
if(!pmp_check(access, addr, length) && !is_debug(access)) {
hart_if.fault_data = addr;
if(is_debug(access))
throw trap_access(0, addr);
hart_if.reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 1
return iss::Err;
}
return down_stream_mem.wr_mem(access, addr, length, data);
}
iss::status read_plain(unsigned addr, reg_t& val) {
val = hart_if.csr[addr];
return iss::Ok;
}
iss::status write_plain(unsigned addr, reg_t const& val) {
hart_if.csr[addr] = val;
return iss::Ok;
}
iss::status write_pmpcfg(unsigned addr, reg_t val) {
hart_if.csr[addr] = val & 0x9f9f9f9f;
return iss::Ok;
}
bool pmp_check(const access_type type, const uint64_t addr, const unsigned len);
protected:
arch::priv_if<WORD_TYPE> hart_if;
memory_if down_stream_mem;
};
template <typename WORD_TYPE> bool pmp<WORD_TYPE>::pmp_check(const access_type type, const uint64_t addr, const unsigned len) {
constexpr auto PMP_SHIFT = 2U;
constexpr auto PMP_R = 0x1U;
constexpr auto PMP_W = 0x2U;
constexpr auto PMP_X = 0x4U;
constexpr auto PMP_A = 0x18U;
constexpr auto PMP_L = 0x80U;
constexpr auto PMP_TOR = 0x1U;
constexpr auto PMP_NA4 = 0x2U;
constexpr auto PMP_NAPOT = 0x3U;
reg_t base = 0;
auto any_active = false;
auto const cfg_reg_size = sizeof(reg_t);
for(size_t i = 0; i < 16; i++) {
reg_t tor = hart_if.csr[arch::pmpaddr0 + i] << PMP_SHIFT;
uint8_t cfg = hart_if.csr[arch::pmpcfg0 + (i / cfg_reg_size)] >> (i % cfg_reg_size);
if(cfg & PMP_A) {
any_active = true;
auto pmp_a = (cfg & PMP_A) >> 3;
auto is_tor = pmp_a == PMP_TOR;
auto is_na4 = pmp_a == PMP_NA4;
reg_t mask = (hart_if.csr[arch::pmpaddr0 + i] << 1) | (!is_na4);
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
// Check each 4-byte sector of the access
auto any_match = false;
auto all_match = true;
for(reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
reg_t cur_addr = addr + offset;
auto napot_match = ((cur_addr ^ tor) & mask) == 0;
auto tor_match = base <= (cur_addr + len - 1) && cur_addr < tor;
auto match = is_tor ? tor_match : napot_match;
any_match |= match;
all_match &= match;
}
if(any_match) {
// If the PMP matches only a strict subset of the access, fail it
if(!all_match)
return false;
return (hart_if.reg.PRIV == arch::PRIV_M && !(cfg & PMP_L)) || (type == access_type::READ && (cfg & PMP_R)) ||
(type == access_type::WRITE && (cfg & PMP_W)) || (type == access_type::FETCH && (cfg & PMP_X));
}
}
base = tor;
}
// constexpr auto pmp_num_regs = 16;
// reg_t tor_base = 0;
// auto any_active = false;
// auto lower_addr = addr >>2;
// auto upper_addr = (addr+len-1)>>2;
// for (size_t i = 0; i < pmp_num_regs; i++) {
// uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4);
// uint8_t cfg_next = i==(pmp_num_regs-1)? 0 : csr[pmpcfg0+((i+1)/4)]>>((i+1)%4);
// auto pmpaddr = csr[pmpaddr0+i];
// if (cfg & PMP_A) {
// any_active=true;
// auto is_tor = bit_sub<3, 2>(cfg) == PMP_TOR;
// auto is_napot = bit_sub<4, 1>(cfg) && bit_sub<3, 2>(cfg_next)!= PMP_TOR;
// if(is_napot) {
// reg_t mask = bit_sub<3, 1>(cfg)?~( pmpaddr & ~(pmpaddr + 1)): 0x3fffffff;
// auto mpmpaddr = pmpaddr & mask;
// if((lower_addr&mask) == mpmpaddr && (upper_addr&mask)==mpmpaddr)
// return (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
// (type == access_type::READ && (cfg & PMP_R)) ||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
// (type == access_type::FETCH && (cfg & PMP_X));
// } else if(is_tor) {
// if(lower_addr>=tor_base && upper_addr<=pmpaddr)
// return (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
// (type == access_type::READ && (cfg & PMP_R)) ||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
// (type == access_type::FETCH && (cfg & PMP_X));
// }
// }
// tor_base = pmpaddr;
// }
return !any_active || hart_if.reg.PRIV == arch::PRIV_M;
}
} // namespace mem
} // namespace iss

View File

@ -1,3 +1,37 @@
/*******************************************************************************
* 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
******************************************************************************/
#include "semihosting.h"
#include <chrono>
#include <cstdint>

View File

@ -1,3 +1,37 @@
/*******************************************************************************
* 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 _SEMIHOSTING_H_
#define _SEMIHOSTING_H_
#include <chrono>
@ -58,4 +92,4 @@ template <typename T> struct semihosting_callback {
};
template <typename T> using semihosting_cb_t = std::function<void(iss::arch_if*, T*, T*)>;
#endif
#endif

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* Copyright (C) 2017 - 2025 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -387,7 +387,7 @@ template <unsigned int BUSWIDTH> void core_complex<BUSWIDTH>::run() {
quantum_keeper.reset();
cpu->set_interrupt_execution(false);
cpu->start(dump_ir);
} while(cpu->get_interrupt_execution());
} while(!cpu->get_interrupt_execution());
sc_stop();
}
@ -419,7 +419,7 @@ template <unsigned int BUSWIDTH> bool core_complex<BUSWIDTH>::read_mem(uint64_t
gp.set_extension(preExt);
}
auto pre_delay = delay;
dbus->b_transport(gp, delay);
sckt->b_transport(gp, delay);
if(pre_delay > delay) {
quantum_keeper.reset();
} else {

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2017-2021 MINRES Technologies GmbH
* Copyright (C) 2017 - 2025 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,36 @@
/*
* sc_core_adapter.h
/*******************************************************************************
* Copyright (C) 2023 - 2025 MINRES Technologies GmbH
* All rights reserved.
*
* Created on: Jul 5, 2023
* Author: eyck
*/
* 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 _SYSC_SC_CORE_ADAPTER_H_
#define _SYSC_SC_CORE_ADAPTER_H_
@ -11,6 +38,7 @@
#include "sc_core_adapter_if.h"
#include <iostream>
#include <iss/iss.h>
#include <iss/mem/memory_if.h>
#include <iss/vm_types.h>
#include <scc/report.h>
#include <util/ities.h>
@ -18,11 +46,16 @@
namespace sysc {
template <typename PLAT> class sc_core_adapter : public PLAT, public sc_core_adapter_if {
public:
using this_class = sc_core_adapter<PLAT>;
using reg_t = typename iss::arch::traits<typename PLAT::core>::reg_t;
using phys_addr_t = typename iss::arch::traits<typename PLAT::core>::phys_addr_t;
using heart_state_t = typename PLAT::hart_state_type;
sc_core_adapter(sysc::tgfs::core_complex_if* owner)
: owner(owner) {}
: owner(owner) {
this->csr_rd_cb[iss::arch::time] = MK_CSR_RD_CB(read_time);
if(sizeof(reg_t) == 4)
this->csr_rd_cb[iss::arch::timeh] = MK_CSR_RD_CB(read_time);
this->memories.replace_last(*this);
}
iss::arch_if* get_arch_if() override { return this; }
@ -60,79 +93,90 @@ public:
}
};
iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t* const data) override {
if(addr.access && iss::access_type::DEBUG)
return owner->read_mem_dbg(addr.val, length, data) ? iss::Ok : iss::Err;
iss::mem::memory_if get_mem_if() override {
return iss::mem::memory_if{.rd_mem{util::delegate<iss::mem::rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
.wr_mem{util::delegate<iss::mem::wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}};
}
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
if(access && iss::access_type::DEBUG)
return owner->read_mem_dbg(addr, length, data) ? iss::Ok : iss::Err;
else {
return owner->read_mem(addr.val, length, data, is_fetch(addr.access)) ? iss::Ok : iss::Err;
return owner->read_mem(addr, length, data, is_fetch(access)) ? iss::Ok : iss::Err;
}
}
iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data) override {
if(addr.access && iss::access_type::DEBUG)
return owner->write_mem_dbg(addr.val, length, data) ? iss::Ok : iss::Err;
else {
auto tohost_upper = (sizeof(reg_t) == 4 && addr.val == (this->tohost + 4)) || (sizeof(reg_t) == 8 && addr.val == this->tohost);
auto tohost_lower = (sizeof(reg_t) == 4 && addr.val == this->tohost) || (sizeof(reg_t) == 64 && addr.val == this->tohost);
if(tohost_lower || tohost_upper) {
if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
switch(hostvar >> 48) {
case 0:
if(hostvar != 0x1) {
SCCINFO(owner->hier_name())
<< "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar << "), stopping simulation";
} else {
SCCINFO(owner->hier_name())
<< "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar << "), stopping simulation";
}
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = hostvar;
#ifndef WITH_TCC
throw(iss::simulation_stopped(hostvar));
#endif
break;
default:
break;
}
} else if(tohost_lower)
to_host_wr_cnt++;
return iss::Ok;
} else {
auto res = owner->write_mem(addr.val, length, data) ? iss::Ok : iss::Err;
// clear MTIP on mtimecmp write
if(addr.val == 0x2004000) {
reg_t val;
this->read_csr(iss::arch::mip, val);
if(val & (1ULL << 7))
this->write_csr(iss::arch::mip, val & ~(1ULL << 7));
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
if(access && iss::access_type::DEBUG)
return owner->write_mem_dbg(addr, length, data) ? iss::Ok : iss::Err;
if(addr == this->tohost) {
reg_t cur_data = *reinterpret_cast<const reg_t*>(data);
// Extract Device (bits 63:56)
uint8_t device = sizeof(reg_t) == 4 ? 0 : (cur_data >> 56) & 0xFF;
// Extract Command (bits 55:48)
uint8_t command = sizeof(reg_t) == 4 ? 0 : (cur_data >> 48) & 0xFF;
// Extract payload (bits 47:0)
uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; // 24bits
if(payload_addr & 1) {
if(payload_addr != 0x1) {
SCCERR(owner->hier_name()) << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr
<< "), stopping simulation";
} else {
SCCINFO(owner->hier_name())
<< "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr << "), stopping simulation";
}
return res;
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = payload_addr;
#ifndef WITH_TCC
throw(iss::simulation_stopped(payload_addr));
#endif
return iss::Ok;
}
if(device == 0 && command == 0) {
std::array<uint64_t, 8> loaded_payload;
auto res = owner->read_mem(payload_addr, 8 * sizeof(uint64_t), reinterpret_cast<uint8_t*>(loaded_payload.data()), false)
? iss::Ok
: iss::Err;
if(res == iss::Err) {
SCCERR(owner->hier_name()) << "Syscall read went wrong";
return iss::Ok;
}
uint64_t syscall_num = loaded_payload.at(0);
if(syscall_num == 64) // SYS_WRITE
return this->execute_sys_write(this, loaded_payload, PLAT::MEM);
SCCERR(owner->hier_name()) << "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;
}
SCCERR(owner->hier_name()) << "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;
}
auto res = owner->write_mem(addr, length, data) ? iss::Ok : iss::Err;
return res;
}
iss::status read_csr(unsigned addr, reg_t& val) override {
if((addr == iss::arch::time || addr == iss::arch::timeh)) {
uint64_t time_val = owner->mtime_i.get_interface() ? owner->mtime_i.read() : 0;
if(addr == iss::arch::time) {
val = static_cast<reg_t>(time_val);
} else if(addr == iss::arch::timeh) {
if(sizeof(reg_t) != 4)
return iss::Err;
val = static_cast<reg_t>(time_val >> 32);
}
return iss::Ok;
} else {
return PLAT::read_csr(addr, val);
iss::status read_time(unsigned addr, reg_t& val) {
uint64_t time_val = owner->mtime_i.get_interface() ? owner->mtime_i.read() : 0;
if(addr == iss::arch::time) {
val = static_cast<reg_t>(time_val);
} else if(addr == iss::arch::timeh) {
if(sizeof(reg_t) != 4)
return iss::Err;
val = static_cast<reg_t>(time_val >> 32);
}
return iss::Ok;
}
void wait_until(uint64_t flags) override {
SCCDEBUG(owner->hier_name()) << "Sleeping until interrupt";
PLAT::wait_until(flags);
while(this->reg.pending_trap == 0 && (this->csr[iss::arch::mip] & this->csr[iss::arch::mie]) == 0) {
sc_core::wait(wfi_evt);
}
PLAT::wait_until(flags);
}
void local_irq(short id, bool value) override {
@ -165,7 +209,6 @@ public:
private:
sysc::tgfs::core_complex_if* const owner{nullptr};
sc_core::sc_event wfi_evt;
uint64_t hostvar{std::numeric_limits<uint64_t>::max()};
unsigned to_host_wr_cnt = 0;
bool first{true};
};

View File

@ -1,9 +1,36 @@
/*
* sc_core_adapter.h
/*******************************************************************************
* Copyright (C) 2023 MINRES Technologies GmbH
* All rights reserved.
*
* Created on: Jul 5, 2023
* Author: eyck
*/
* 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 _SYSC_SC_CORE_ADAPTER_IF_H_
#define _SYSC_SC_CORE_ADAPTER_IF_H_

View File

@ -4785,8 +4785,6 @@ continuation_e vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, jit_hold
code_word_t instr = 0;
phys_addr_t paddr(pc);
auto *const data = (uint8_t *)&instr;
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, data);
if (res != iss::Ok)
return ILLEGAL_FETCH;
@ -4926,4 +4924,4 @@ volatile std::array<bool, 2> dummy = {
};
}
}
// clang-format on
// clang-format on

View File

@ -251,22 +251,8 @@ private:
decoder instr_decoder;
iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
if(this->core.has_mmu()) {
auto phys_pc = this->core.virt2phys(pc);
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok)
// return iss::Err;
// } else {
if (this->core.read(phys_pc, 4, data) != iss::Ok)
return iss::Err;
// }
} else {
if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok)
return iss::Err;
}
if (this->core.read(iss::address_type::PHYSICAL, pc.access, pc.space, pc.val, 4, data) != iss::Ok)
return iss::Err;
return iss::Ok;
}
};
@ -2695,11 +2681,12 @@ std::unique_ptr<vm_if> create<arch::tgc5c>(arch::tgc5c *core, unsigned short por
} // namespace iss
#include <iss/arch/riscv_hart_m_p.h>
#include <iss/arch/riscv_hart_msu_vp.h>
#include <iss/arch/riscv_hart_mu_p.h>
#include <iss/factory.h>
namespace iss {
namespace {
volatile std::array<bool, 2> dummy = {
volatile std::array<bool, 3> dummy = {
core_factory::instance().register_creator("tgc5c|m_p|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{
auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::tgc5c>();
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
@ -2719,8 +2706,18 @@ volatile std::array<bool, 2> dummy = {
cpu->set_semihosting_callback(*cb);
}
return {cpu_ptr{cpu}, vm_ptr{vm}};
}),
core_factory::instance().register_creator("tgc5c|mus_vp|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{
auto* cpu = new iss::arch::riscv_hart_msu_vp<iss::arch::tgc5c>();
auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false);
if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
if(init_data){
auto* cb = reinterpret_cast<semihosting_cb_t<arch::traits<arch::tgc5c>::reg_t>*>(init_data);
cpu->set_semihosting_callback(*cb);
}
return {cpu_ptr{cpu}, vm_ptr{vm}};
})
};
}
}
// clang-format on
// clang-format on

View File

@ -4944,8 +4944,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, BasicBlock *this_block)
// const typename traits::addr_t upper_bits = ~traits::PGMASK;
phys_addr_t paddr(pc);
auto *const data = (uint8_t *)&instr;
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, data);
if (res != iss::Ok)
return std::make_tuple(ILLEGAL_FETCH, nullptr);

View File

@ -3743,8 +3743,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, tu_builder& tu) {
enum {TRAP_ID=1<<16};
code_word_t instr = 0;
phys_addr_t paddr(pc);
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr));
if (res != iss::Ok)
return ILLEGAL_FETCH;
@ -3833,4 +3831,4 @@ volatile std::array<bool, 2> dummy = {
};
}
}
// clang-format on
// clang-format on