diff --git a/CMakeLists.txt b/CMakeLists.txt index e951e94..b9300bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ add_subdirectory(softfloat) set(LIB_SOURCES src/iss/plugin/instruction_count.cpp src/iss/arch/tgc5c.cpp - src/iss/memory/memory_if.cpp + src/iss/mmio/memory_if.cpp src/vm/interp/vm_tgc5c.cpp src/vm/fp_functions.cpp src/iss/debugger/csr_names.cpp diff --git a/src/iss/arch/mstatus.h b/src/iss/arch/mstatus.h new file mode 100644 index 0000000..4f89a04 --- /dev/null +++ b/src/iss/arch/mstatus.h @@ -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 +#include +#include +#include + +namespace iss { +namespace arch { + +template struct status {}; +// specialization 32bit +template struct status::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 struct status::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 struct hart_state {}; +// specialization 32bit +template class hart_state::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 class hart_state::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 \ No newline at end of file diff --git a/src/iss/arch/riscv_hart_common.h b/src/iss/arch/riscv_hart_common.h index e678196..e445e62 100644 --- a/src/iss/arch/riscv_hart_common.h +++ b/src/iss/arch/riscv_hart_common.h @@ -37,8 +37,9 @@ #include "iss/arch/traits.h" #include "iss/log_categories.h" -#include "iss/memory/memory_if.h" +#include "iss/mmio/memory_if.h" #include "iss/vm_types.h" +#include "mstatus.h" #include "util/delegate.h" #include #include @@ -51,7 +52,6 @@ #include #include #include -#include #include #include @@ -66,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 */ @@ -242,10 +242,6 @@ struct vm_info { }; 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}; @@ -278,56 +274,22 @@ 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(®); - 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; - } -} - -inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const data, unsigned length) { - auto reg_ptr = reinterpret_cast(®); - 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; - } -} template struct priv_if { + using rd_csr_f = std::function; + using wr_csr_f = std::function; + std::function read_csr; std::function write_csr; std::function exec_htif; + std::unordered_map& csr_rd_cb; + std::unordered_map& csr_wr_cb; + hart_state& mstatus; uint64_t& tohost; uint64_t& fromhost; + unsigned& mcause_max_irq; }; -template struct riscv_hart_common : public BASE, public memory::memory_elem { +template struct riscv_hart_common : public BASE, public mmio::memory_elem { const std::array lvl = {{'U', 'S', 'H', 'M'}}; const std::array trap_str = {{"" "Instruction address misaligned", // 0 @@ -365,7 +327,8 @@ template struct riscv_hart_co #define MK_CSR_WR_CB(FCT) [this](unsigned a, reg_t r) -> iss::status { return this->FCT(a, r); }; riscv_hart_common() - : instr_if(*this) { + : state() + , instr_if(*this) { // reset values csr[misa] = traits::MISA_VAL; csr[mvendorid] = 0x669; @@ -748,17 +711,16 @@ template struct riscv_hart_co return iss::Ok; } - iss::status write_xtvt(unsigned addr, reg_t val) { - csr[addr] = val & ~0x3fULL; - return iss::Ok; - } - priv_if get_priv_if() { return priv_if{.read_csr = [this](unsigned addr, reg_t& val) -> iss::status { return read_csr(addr, val); }, .write_csr = [this](unsigned addr, reg_t val) -> iss::status { return write_csr(addr, val); }, .exec_htif = [this](uint8_t const* data) -> iss::status { return execute_htif(data); }, + .csr_rd_cb{this->csr_rd_cb}, + .csr_wr_cb{csr_wr_cb}, + .mstatus{this->state}, .tohost{this->tohost}, - .fromhost{this->fromhost}}; + .fromhost{this->fromhost}, + .mcause_max_irq{mcause_max_irq}}; } iss::status execute_htif(uint8_t const* data) { @@ -798,17 +760,33 @@ template struct riscv_hart_co } } - memory::memory_hierarchy memories; + mmio::memory_hierarchy memories; - virtual memory::memory_if get_mem_if() override { + virtual mmio::memory_if get_mem_if() override { assert(false || "This function should nevver be called"); - return memory::memory_if{}; + return mmio::memory_if{}; } - virtual void set_next(memory::memory_if mem_if) { memory = mem_if; }; + virtual void set_next(mmio::memory_if mem_if) { memory = mem_if; }; + + void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } protected: - memory::memory_if memory; + hart_state 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; + } + } + + mmio::memory_if memory; struct riscv_instrumentation_if : public iss::instrumentation_if { riscv_instrumentation_if(riscv_hart_common& arch) @@ -864,6 +842,7 @@ protected: int64_t instret_offset{0}; semihosting_cb_t semihosting_cb; std::array vm; + unsigned mcause_max_irq{16U}; }; } // namespace arch diff --git a/src/iss/arch/riscv_hart_m_p.h b/src/iss/arch/riscv_hart_m_p.h index 1e9405a..6980c2e 100644 --- a/src/iss/arch/riscv_hart_m_p.h +++ b/src/iss/arch/riscv_hart_m_p.h @@ -35,7 +35,6 @@ #ifndef _RISCV_HART_M_P_H #define _RISCV_HART_M_P_H -#include "iss/instrumentation_if.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -43,21 +42,15 @@ #include #include #include -#include -#include #ifndef FMT_HEADER_ONLY #define FMT_HEADER_ONLY #endif #include #include #include -#include -#include -#include -#include -#include +#include #include -#include + namespace iss { namespace arch { @@ -71,61 +64,8 @@ public: using reg_t = typename core::reg_t; using addr_t = typename core::addr_t; - // primary template - template struct hart_state {}; - // specialization 32bit - template class hart_state::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 reg_t mstatus_reset_val = 0x1800; - - void write_mstatus(T val) { - auto mask = get_mask() & 0xff; // MPP is hardcode as 0x3 - auto new_val = (mstatus.backing.val & ~mask) | (val & mask); - mstatus = new_val; - } - - static constexpr uint32_t get_mask() { + static constexpr reg_t get_mstatus_mask() { + if(sizeof(reg_t) == 4) // return 0x807ff988UL; // 0b1000 0000 0111 1111 1111 1000 1000 1000 // only machine mode is supported // +-SD // | +-TSR @@ -141,66 +81,7 @@ public: // | |||||| | | | |+-MPIE // | ||||||/|/|/| || +-MIE return 0b00000000000000000001100010001000; - } - }; - - // specialization 64bit - template class hart_state::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 reg_t mstatus_reset_val = 0x1800; - - void write_mstatus(T val) { - auto mask = get_mask() & 0xff; // MPP is hardcode as 0x3 - auto new_val = (mstatus.backing.val & ~mask) | (val & mask); - mstatus = new_val; - } - - static constexpr T get_mask() { + else if(sizeof(reg_t) == 8) // return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 // // +-TSR @@ -216,10 +97,15 @@ public: // |||||| | | | |+-MPIE // ||||||/|/|/| || +-MIE return 0b00000000000000000001100010001000; - } - }; + else + assert(false && "Unsupported XLEN value"); + } - using hart_state_type = hart_state; + void write_mstatus(reg_t val) { + auto mask = get_mstatus_mask() & 0xff; // MPP is hardcoded as 0x3 + auto new_val = (this->state.mstatus() & ~mask) | (val & mask); + this->state.mstatus = new_val; + } constexpr reg_t get_irq_mask() { return 0b100010001000; // only machine mode is supported @@ -242,11 +128,15 @@ public: void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; } - void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } + void set_num_of_irq(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); } protected: - hart_state_type state; + using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const); + using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const); + hart_state state; + + std::unordered_map ptw; std::unordered_map atomic_reservation; iss::status read_status(unsigned addr, reg_t& val); @@ -265,20 +155,13 @@ protected: void check_interrupt(); feature_config cfg; - unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast(traits::CLIC_NUM_IRQ)) : 16U}; - memory::functional_memory default_mem; + mmio::memory_with_htif default_mem; }; template riscv_hart_m_p::riscv_hart_m_p(feature_config cfg) -: state() -, cfg(cfg) +: cfg(cfg) , default_mem(base::get_priv_if()) { - this->rd_func = util::delegate::from(this); - this->wr_func = util::delegate::from(this); - this->memories.prepend(*this); - this->memories.append(default_mem); - const std::array rwaddrs{{mepc, mtvec, mscratch, mtval}}; for(auto addr : rwaddrs) { this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain); @@ -310,6 +193,10 @@ riscv_hart_m_p::riscv_hart_m_p(feature_config cfg) this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr); this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug); } + this->rd_func = util::delegate::from(this); + this->wr_func = util::delegate::from(this); + this->memories.prepend(*this); + this->memories.append(default_mem); } template @@ -318,7 +205,7 @@ iss::status riscv_hart_m_p::read(const address_type type, co #ifndef NDEBUG if(access && iss::access_type::DEBUG) { CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr; - } else if(access && iss::access_type::FETCH) { + } else if(is_fetch(access)) { CPPLOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr; } else { CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr; @@ -464,39 +351,27 @@ iss::status riscv_hart_m_p::write(const address_type type, c template iss::status riscv_hart_m_p::read_status(unsigned addr, reg_t& val) { - val = state.mstatus & hart_state_type::get_mask(); + val = this->state.mstatus & get_mstatus_mask(); return iss::Ok; } template iss::status riscv_hart_m_p::write_status(unsigned addr, reg_t val) { - state.write_mstatus(val); + write_mstatus(val); check_interrupt(); return iss::Ok; } template iss::status riscv_hart_m_p::read_cause(unsigned addr, reg_t& val) { - if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { - val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - val |= state.mstatus.MPIE << 27; - val |= state.mstatus.MPP << 28; - } else - val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1)); + val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); return iss::Ok; } template iss::status riscv_hart_m_p::write_cause(unsigned addr, reg_t val) { - if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); - state.mstatus.MPIE = (val >> 27) & 0x1; - state.mstatus.MPP = (val >> 28) & 0x3; - } else { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1)); - this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); - } + auto mask = ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); + this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); return iss::Ok; } @@ -524,7 +399,7 @@ iss::status riscv_hart_m_p::read_ip(unsigned addr, reg_t& va template inline void riscv_hart_m_p::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state_type::mstatus_reset_val; + this->state.mstatus = hart_state::mstatus_reset_val; } template void riscv_hart_m_p::check_interrupt() { @@ -536,7 +411,7 @@ template void riscv_hart_m_pcsr[mip] & this->csr[mie]; - bool mstatus_mie = state.mstatus.MIE; + bool mstatus_mie = this->state.mstatus.MIE; auto m_enabled = this->reg.PRIV < PRIV_M || mstatus_mie; auto enabled_interrupts = m_enabled ? ena_irq : 0; @@ -630,16 +505,17 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t // the trap; and the x IE field of mstatus // is cleared // store the actual privilege level in yPP and store interrupt enable flags - state.mstatus.MPP = PRIV_M; - state.mstatus.MPIE = state.mstatus.MIE; - state.mstatus.MIE = false; + this->state.mstatus.MPP = PRIV_M; + this->state.mstatus.MPIE = this->state.mstatus.MIE; + this->state.mstatus.MIE = false; // get trap vector auto xtvec = this->csr[mtvec]; // calculate adds// set NEXT_PC to trap addressess to jump to based on MODE - if((FEAT & features_e::FEAT_CLIC) && trap_id != 0 && (xtvec & 0x3UL) == 3UL) { + if(trap_id != 0 && (xtvec & 0x3UL) == 3UL) { reg_t data; - auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast(&data)); + auto ret = + this->memory.rd_mem(iss::access_type::DEBUG_READ, this->csr[arch::mtvt], sizeof(reg_t), reinterpret_cast(&data)); if(ret == iss::Err) return this->reg.PC; this->reg.NEXT_PC = data; @@ -666,8 +542,8 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t } template uint64_t riscv_hart_m_p::leave_trap(uint64_t flags) { - state.mstatus.MIE = state.mstatus.MPIE; - state.mstatus.MPIE = 1; + this->state.mstatus.MIE = this->state.mstatus.MPIE; + this->state.mstatus.MPIE = 1; // sets the pc to the value stored in the x epc register. this->reg.NEXT_PC = this->csr[mepc] & this->get_pc_mask(); NSCLOG(INFO, LOGCAT) << "Executing xRET"; diff --git a/src/iss/arch/riscv_hart_msu_vp.h b/src/iss/arch/riscv_hart_msu_vp.h index a6b9437..f975377 100644 --- a/src/iss/arch/riscv_hart_msu_vp.h +++ b/src/iss/arch/riscv_hart_msu_vp.h @@ -35,7 +35,6 @@ #ifndef _RISCV_HART_MSU_VP_H #define _RISCV_HART_MSU_VP_H -#include "iss/instrumentation_if.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -44,16 +43,12 @@ #include #include #include -#include #ifndef FMT_HEADER_ONLY #define FMT_HEADER_ONLY #endif #include #include #include -#include -#include -#include #include #include #include @@ -70,65 +65,12 @@ public: using reg_t = typename core::reg_t; using addr_t = typename core::addr_t; - // primary template - template struct hart_state {}; - // specialization 32bit - template class hart_state::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(); + static constexpr reg_t get_misa() { + return (sizeof(reg_t) == 4 ? (1UL << 30) : (2ULL << 62)) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; + } - mstatus_t mstatus; - - static const reg_t mstatus_reset_val = 0x1800; - - void write_mstatus(T val, unsigned priv_lvl) { - auto mask = get_mask(priv_lvl); - auto new_val = (static_cast(mstatus) & ~mask) | (val & mask); - mstatus = new_val; - } - - T satp; - - static constexpr T get_misa() { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; } - - static constexpr uint32_t get_mask(unsigned priv_lvl) { + static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) { + if(sizeof(reg_t) == 4) { #if __cplusplus < 201402L return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL; #else @@ -141,130 +83,79 @@ public: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 } #endif - } - - static inline vm_info decode_vm_info(uint32_t state, T sptbr) { - if(state == PRIV_M) - return {0, 0, 0, 0}; - if(state <= 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 - } - }; - // specialization 64bit - template class hart_state::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 reg_t mstatus_reset_val = 0xa00000000; - - void write_mstatus(T val, unsigned priv_lvl) { - T old_val = mstatus; - auto mask = get_mask(priv_lvl); - auto new_val = (old_val & ~mask) | (val & mask); - if((new_val & mstatus.SXL.Mask) == 0) { - new_val |= old_val & mstatus.SXL.Mask; - } - if((new_val & mstatus.UXL.Mask) == 0) { - new_val |= old_val & mstatus.UXL.Mask; - } - mstatus = new_val; - } - - T satp; - - static constexpr T get_misa() { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; } - - static constexpr T get_mask(unsigned priv_lvl) { - uint64_t ret; + } else if(sizeof(reg_t) == 8) { switch(priv_lvl) { case PRIV_U: - ret = 0x8000000f00000011ULL; - break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 + return 0x8000000f00000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 case PRIV_S: - ret = 0x8000000f000de133ULL; - break; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011 + return 0x8000000f000de133ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011 default: - ret = 0x8000000f007ff9ddULL; - break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 + return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 } - return ret; - } + } else + assert(false && "Unsupported XLEN value"); + } - static inline vm_info decode_vm_info(uint32_t state, T sptbr) { - if(state == PRIV_M) - return {0, 0, 0, 0}; - if(state <= 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 + template ::value>* = nullptr> + void write_mstatus(T_ val, unsigned priv_lvl) { + reg_t old_val = mstatus; + auto mask = get_mstatus_mask(priv_lvl); + auto new_val = (old_val & ~mask) | (val & mask); + this->state.mstatus = new_val; + } + + template ::value>* = nullptr> + void write_mstatus(T_ val, unsigned priv_lvl) { + reg_t old_val = mstatus; + auto mask = get_mstatus_mask(priv_lvl); + auto new_val = (old_val & ~mask) | (val & mask); + if((new_val & this->state.mstatus.SXL.Mask) == 0) { + new_val |= old_val & this->state.mstatus.SXL.Mask; } - }; - using hart_state_type = hart_state; + if((new_val & this->state.mstatus.UXL.Mask) == 0) { + new_val |= old_val & this->state.mstatus.UXL.Mask; + } + this->state.mstatus = new_val; + } + reg_t satp; + + static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) { + if(state == PRIV_M) + return {0, 0, 0, 0}; + if(state <= 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 == PRIV_M) + return {0, 0, 0, 0}; + if(state <= 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 + } const typename core::reg_t PGSIZE = 1 << PGSHIFT; const typename core::reg_t PGMASK = PGSIZE - 1; @@ -305,7 +196,7 @@ protected: virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t* const data); virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data); - hart_state_type state; + hart_state state; using mem_type = util::sparse_array; mem_type mem; @@ -415,7 +306,7 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ return iss::Err; } if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary - vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + vm_info vm = decode_vm_info(this->reg.PRIV, satp); if(vm.levels != 0) { // VM is active auto split_addr = (addr + length) & ~PGMASK; auto len1 = split_addr - addr; @@ -521,7 +412,7 @@ iss::status riscv_hart_msu_vp::write(const address_type type, const access try { // TODO: There is no check for alignment if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary - vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + vm_info vm = decode_vm_info(this->reg.PRIV, satp); if(vm.levels != 0) { // VM is active auto split_addr = (addr + length) & ~PGMASK; auto len1 = split_addr - addr; @@ -583,13 +474,13 @@ iss::status riscv_hart_msu_vp::write(const address_type type, const access template iss::status riscv_hart_msu_vp::read_status(unsigned addr, reg_t& val) { auto req_priv_lvl = (addr >> 8) & 0x3; - val = state.mstatus & hart_state_type::get_mask(req_priv_lvl); + val = state.mstatus & get_mstatus_mask(req_priv_lvl); return iss::Ok; } template iss::status riscv_hart_msu_vp::write_status(unsigned addr, reg_t val) { auto req_priv_lvl = (addr >> 8) & 0x3; - state.write_mstatus(val, req_priv_lvl); + write_mstatus(val, req_priv_lvl); check_interrupt(); update_vm_info(); return iss::Ok; @@ -633,7 +524,7 @@ template iss::status riscv_hart_msu_vp::read_satp(unsigned this->fault_data = this->reg.PC; return iss::Err; } - val = state.satp; + val = satp; return iss::Ok; } @@ -644,7 +535,7 @@ template iss::status riscv_hart_msu_vp::write_satp(unsigne this->fault_data = this->reg.PC; return iss::Err; } - state.satp = val; + satp = val; update_vm_info(); return iss::Ok; } @@ -715,15 +606,15 @@ template iss::status riscv_hart_msu_vp::write_mem(phys_add template inline void riscv_hart_msu_vp::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state_type::mstatus_reset_val; + state.mstatus = hart_state::mstatus_reset_val; update_vm_info(); } template inline void riscv_hart_msu_vp::update_vm_info() { - this->vm[1] = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + this->vm[1] = decode_vm_info(this->reg.PRIV, satp); BASE::addr_mode[3] = BASE::addr_mode[2] = this->vm[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; if(state.mstatus.MPRV) - this->vm[0] = hart_state_type::decode_vm_info(state.mstatus.MPP, state.satp); + this->vm[0] = decode_vm_info(state.mstatus.MPP, satp); else this->vm[0] = this->vm[1]; BASE::addr_mode[1] = BASE::addr_mode[0] = this->vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; diff --git a/src/iss/arch/riscv_hart_mu_p.h b/src/iss/arch/riscv_hart_mu_p.h index 6d35d7a..f5e8527 100644 --- a/src/iss/arch/riscv_hart_mu_p.h +++ b/src/iss/arch/riscv_hart_mu_p.h @@ -35,7 +35,6 @@ #ifndef _RISCV_HART_MU_P_H #define _RISCV_HART_MU_P_H -#include "iss/instrumentation_if.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -43,20 +42,14 @@ #include #include #include -#include -#include #ifndef FMT_HEADER_ONLY #define FMT_HEADER_ONLY #endif #include #include #include -#include -#include -#include -#include +#include #include -#include namespace iss { namespace arch { @@ -71,67 +64,14 @@ public: using reg_t = typename core::reg_t; using addr_t = typename core::addr_t; - // primary template - template struct hart_state {}; - // specialization 32bit - template class hart_state::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 reg_t mstatus_reset_val = 0x1800; // MPP set to 1 - - void write_mstatus(T val, unsigned priv_lvl) { - auto mask = get_mask(priv_lvl); - auto new_val = (mstatus.backing.val & ~mask) | (val & mask); - mstatus = new_val; - } - - static constexpr uint32_t get_mask(unsigned priv_lvl) { + static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) { + if(sizeof(reg_t) == 4) { #if __cplusplus < 201402L return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL; #else switch(priv_lvl) { case PRIV_U: - return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001 + return 0x00000011UL; // 0b1...0 0001 0001 default: // +-SD // | +-TSR @@ -151,72 +91,13 @@ public: return 0b00000000000000000001100010011001; } #endif - } - }; - - // specialization 64bit - template class hart_state::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 reg_t mstatus_reset_val = 0x1800; - - void write_mstatus(T val, unsigned priv_lvl) { - auto mask = get_mask(priv_lvl); - auto new_val = (mstatus.backing.val & ~mask) | (val & mask); - mstatus = new_val; - } - - static constexpr uint64_t get_mask(unsigned priv_lvl) { + } else if(sizeof(reg_t) == 8) { #if __cplusplus < 201402L return priv_lvl == PRIV_U ? 0x011ULL : priv_lvl == PRIV_S ? 0x000de133ULL : 0x007ff9ddULL; #else switch(priv_lvl) { case PRIV_U: - return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001 + return 0x00000011UL; // 0b1...0 0001 0001 default: // +-SD // | +-TSR @@ -236,10 +117,15 @@ public: return 0b00000000000000000001100010011001; } #endif - } - }; + } else + assert(false && "Unsupported XLEN value"); + } - using hart_state_type = hart_state; + void write_mstatus(reg_t val, unsigned priv_lvl) { + auto mask = get_mstatus_mask(priv_lvl); + auto new_val = (this->state.mstatus() & ~mask) | (val & mask); + this->state.mstatus = new_val; + } constexpr reg_t get_irq_mask(size_t mode) { std::array m = {{ @@ -268,40 +154,14 @@ public: void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; } - void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - protected: - virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t* const data); - virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data); - - iss::status read_clic(uint64_t addr, unsigned length, uint8_t* const data); - iss::status write_clic(uint64_t addr, unsigned length, const uint8_t* const data); - using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const); using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const); - hart_state_type state; + hart_state state; - using mem_type = util::sparse_array; - mem_type mem; std::unordered_map ptw; std::unordered_map atomic_reservation; - uint8_t clic_cfg_reg{0}; - std::array clic_inttrig_reg; - union clic_int_reg_t { - struct { - uint8_t ip; - uint8_t ie; - uint8_t attr; - uint8_t ctl; - }; - uint32_t raw; - }; - std::vector clic_int_reg; - uint8_t clic_mprev_lvl{0}, clic_uprev_lvl{0}; - uint8_t clic_mact_lvl{0}, clic_uact_lvl{0}; - - std::vector tcm; iss::status read_status(unsigned addr, reg_t& val); iss::status write_status(unsigned addr, reg_t val); @@ -310,8 +170,6 @@ protected: iss::status read_ie(unsigned addr, reg_t& val); iss::status write_ie(unsigned addr, reg_t val); iss::status read_ip(unsigned addr, reg_t& val); - iss::status read_intstatus(unsigned addr, reg_t& val); - iss::status write_intthresh(unsigned addr, reg_t val); iss::status write_xtvt(unsigned addr, reg_t val); iss::status write_dcsr(unsigned addr, reg_t val); iss::status read_debug(unsigned addr, reg_t& val); @@ -320,35 +178,17 @@ protected: iss::status write_dpc(unsigned addr, reg_t val); iss::status write_ideleg(unsigned addr, reg_t val); iss::status write_edeleg(unsigned addr, reg_t val); - iss::status write_pmpcfg(unsigned addr, reg_t val); void check_interrupt(); - bool pmp_check(const access_type type, const uint64_t addr, const unsigned len); - std::vector> memfn_range; - std::vector> memfn_read; - std::vector> memfn_write; - void insert_mem_range(uint64_t, uint64_t, std::function, std::function); feature_config cfg; - unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast(traits::CLIC_NUM_IRQ)) : 16U}; - - std::pair, std::function> replace_mem_access(std::function rd, - std::function wr) { - std::pair, std::function> ret{hart_mem_rd_delegate, hart_mem_wr_delegate}; - hart_mem_rd_delegate = rd; - hart_mem_wr_delegate = wr; - return ret; - } - std::function hart_mem_rd_delegate; - std::function hart_mem_wr_delegate; + mmio::memory_with_htif default_mem; }; template riscv_hart_mu_p::riscv_hart_mu_p(feature_config cfg) : state() -, cfg(cfg) { - this->rd_func = util::delegate::from(this); - this->rd_func = util::delegate::from(this); - +, cfg(cfg) +, default_mem(base::get_priv_if()) { const std::array rwaddrs{{ mepc, mtvec, @@ -380,16 +220,6 @@ riscv_hart_mu_p::riscv_hart_mu_p(feature_config cfg) this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null); this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null); - if(FEAT & FEAT_PMP) { - for(size_t i = pmpaddr0; i <= pmpaddr15; ++i) { - this->csr_rd_cb[i] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[i] = MK_CSR_WR_CB(write_plain); - } - for(size_t i = pmpcfg0; i < pmpcfg0 + 16 / sizeof(reg_t); ++i) { - this->csr_rd_cb[i] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[i] = MK_CSR_WR_CB(write_pmpcfg); - } - } if(FEAT & FEAT_EXT_N) { this->csr_rd_cb[mideleg] = MK_CSR_RD_CB(read_plain); this->csr_wr_cb[mideleg] = MK_CSR_WR_CB(write_ideleg); @@ -406,52 +236,6 @@ riscv_hart_mu_p::riscv_hart_mu_p(feature_config cfg) this->csr_wr_cb[ucause] = MK_CSR_WR_CB(write_cause); this->csr_rd_cb[utvec] = MK_CSR_RD_CB(read_tvec); } - if(FEAT & FEAT_CLIC) { - this->csr_rd_cb[mtvt] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[mtvt] = MK_CSR_WR_CB(write_xtvt); - // this->csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);}; - this->csr_rd_cb[mintstatus] = MK_CSR_RD_CB(read_intstatus); - this->csr_wr_cb[mintstatus] = MK_CSR_WR_CB(write_null); - // this->csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);}; - // this->csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);}; - // this->csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);}; - this->csr_rd_cb[mintthresh] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[mintthresh] = MK_CSR_WR_CB(write_intthresh); - if(FEAT & FEAT_EXT_N) { - this->csr_rd_cb[utvt] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[utvt] = MK_CSR_WR_CB(write_xtvt); - this->csr_rd_cb[uintstatus] = MK_CSR_RD_CB(read_intstatus); - this->csr_wr_cb[uintstatus] = MK_CSR_WR_CB(write_null); - this->csr_rd_cb[uintthresh] = MK_CSR_RD_CB(read_plain); - this->csr_wr_cb[uintthresh] = MK_CSR_WR_CB(write_intthresh); - } - 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; - this->csr[mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; - this->csr[uintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; - insert_mem_range( - cfg.clic_base, 0x5000UL, - [this](phys_addr_t addr, unsigned length, uint8_t* const data) { return read_clic(addr.val, length, data); }, - [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { return write_clic(addr.val, length, data); }); - } - if(FEAT & FEAT_TCM) { - tcm.resize(cfg.tcm_size); - std::function read_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(tcm.data() + offset, tcm.data() + offset + length, data); - return iss::Ok; - }; - std::function write_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(data, data + length, tcm.data() + offset); - return iss::Ok; - }; - insert_mem_range(cfg.tcm_base, cfg.tcm_size, read_clic_cb, write_clic_cb); - } if(FEAT & FEAT_DEBUG) { this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch); this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug); @@ -462,108 +246,10 @@ riscv_hart_mu_p::riscv_hart_mu_p(feature_config cfg) this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr); this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug); } - hart_mem_rd_delegate = [this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status { return this->read_mem(a, l, d); }; - hart_mem_wr_delegate = [this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status { return this->write_mem(a, l, d); }; -} - -template -inline void riscv_hart_mu_p::insert_mem_range(uint64_t base, uint64_t size, std::function rd_f, - std::function wr_fn) { - std::tuple entry{base, size}; - auto it = std::upper_bound( - memfn_range.begin(), memfn_range.end(), entry, - [](std::tuple const& a, std::tuple const& b) { return std::get<0>(a) < std::get<0>(b); }); - auto idx = std::distance(memfn_range.begin(), it); - memfn_range.insert(it, entry); - memfn_read.insert(std::begin(memfn_read) + idx, rd_f); - memfn_write.insert(std::begin(memfn_write) + idx, wr_fn); -} - -template -inline iss::status riscv_hart_mu_p::write_pmpcfg(unsigned addr, reg_t val) { - this->csr[addr] = val & 0x9f9f9f9f; - return iss::Ok; -} - -template -bool riscv_hart_mu_p::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 = this->csr[pmpaddr0 + i] << PMP_SHIFT; - uint8_t cfg = this->csr[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 = (this->csr[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 (this->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)); - } - } - 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 (this->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 (this->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 || this->reg.PRIV == PRIV_M; + this->rd_func = util::delegate::from(this); + this->wr_func = util::delegate::from(this); + this->memories.prepend(*this); + this->memories.append(default_mem); } template @@ -581,15 +267,6 @@ iss::status riscv_hart_mu_p::read(const address_type type, c try { switch(space) { case traits::MEM: { - if(FEAT & FEAT_PMP) { - if(!pmp_check(access, addr, length) && !is_debug(access)) { - this->fault_data = addr; - if(is_debug(access)) - throw trap_access(0, addr); - this->reg.trap_state = (1UL << 31) | ((access == access_type::FETCH ? 1 : 5) << 16); // issue trap 1 - return iss::Err; - } - } auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) { this->fault_data = addr; @@ -604,21 +281,7 @@ iss::status riscv_hart_mu_p::read(const address_type type, c this->fault_data = addr; return iss::Err; } - phys_addr_t phys_addr{access, space, addr}; - auto res = iss::Err; - if(!is_fetch(access) && memfn_range.size()) { - auto it = - std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple const& a) { - return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val; - }); - if(it != std::end(memfn_range)) { - auto idx = std::distance(std::begin(memfn_range), it); - res = memfn_read[idx](phys_addr, length, data); - } else - res = hart_mem_rd_delegate(phys_addr, length, data); - } else { - res = hart_mem_rd_delegate(phys_addr, length, data); - } + auto res = this->memory.rd_mem(access, addr, length, data); if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { this->reg.trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault this->fault_data = addr; @@ -638,8 +301,6 @@ iss::status riscv_hart_mu_p::read(const address_type type, c return this->read_csr(addr, *reinterpret_cast(data)); } break; case traits::FENCE: { - if((addr + length) > mem.size()) - return iss::Err; return iss::Ok; } break; case traits::RES: { @@ -692,15 +353,6 @@ iss::status riscv_hart_mu_p::write(const address_type type, try { switch(space) { case traits::MEM: { - if(FEAT & FEAT_PMP) { - if(!pmp_check(access, addr, length) && (access & access_type::DEBUG) != access_type::DEBUG) { - this->fault_data = addr; - if(access && iss::access_type::DEBUG) - throw trap_access(0, addr); - this->reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 1 - return iss::Err; - } - } if(unlikely(is_fetch(access) && (addr & 0x1) == 1)) { this->fault_data = addr; if(access && iss::access_type::DEBUG) @@ -715,22 +367,8 @@ iss::status riscv_hart_mu_p::write(const address_type type, this->fault_data = addr; return iss::Err; } - phys_addr_t phys_addr{access, space, addr}; - auto res = iss::Err; - if(!is_fetch(access) && memfn_range.size()) { - auto it = - std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple const& a) { - return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val; - }); - if(it != std::end(memfn_range)) { - auto idx = std::distance(std::begin(memfn_range), it); - res = memfn_write[idx](phys_addr, length, data); - } else - res = hart_mem_wr_delegate(phys_addr, length, data); - } else { - res = hart_mem_wr_delegate(phys_addr, length, data); - } - if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { + auto res = this->memory.wr_mem(access, addr, length, data); + if(unlikely(res != iss::Ok && !is_debug(access))) { this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault) this->fault_data = addr; } @@ -747,8 +385,6 @@ iss::status riscv_hart_mu_p::write(const address_type type, return this->write_csr(addr, *reinterpret_cast(data)); } break; case traits::FENCE: { - if((addr + length) > mem.size()) - return iss::Err; switch(addr) { case 2: case 3: { @@ -776,59 +412,27 @@ iss::status riscv_hart_mu_p::write(const address_type type, template iss::status riscv_hart_mu_p::read_status(unsigned addr, reg_t& val) { - val = state.mstatus & hart_state_type::get_mask((addr >> 8) & 0x3); + val = state.mstatus & get_mstatus_mask((addr >> 8) & 0x3); return iss::Ok; } template iss::status riscv_hart_mu_p::write_status(unsigned addr, reg_t val) { - state.write_mstatus(val, (addr >> 8) & 0x3); + write_mstatus(val, (addr >> 8) & 0x3); check_interrupt(); return iss::Ok; } template iss::status riscv_hart_mu_p::read_cause(unsigned addr, reg_t& val) { - if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { - val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - auto mode = (addr >> 8) & 0x3; - switch(mode) { - case 0: - val |= clic_uprev_lvl << 16; - val |= state.mstatus.UPIE << 27; - break; - default: - val |= clic_mprev_lvl << 16; - val |= state.mstatus.MPIE << 27; - val |= state.mstatus.MPP << 28; - break; - } - } else - val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1)); + val = this->csr[addr] & ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); return iss::Ok; } template iss::status riscv_hart_mu_p::write_cause(unsigned addr, reg_t val) { - if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); - auto mode = (addr >> 8) & 0x3; - switch(mode) { - case 0: - clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; - state.mstatus.UPIE = (val >> 27) & 0x1; - break; - default: - clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; - state.mstatus.MPIE = (val >> 27) & 0x1; - state.mstatus.MPP = (val >> 28) & 0x3; - break; - } - } else { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1)); - this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); - } + auto mask = ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); + this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); return iss::Ok; } @@ -872,126 +476,9 @@ iss::status riscv_hart_mu_p::write_edeleg(unsigned addr, reg return iss::Ok; } -template -iss::status riscv_hart_mu_p::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 -iss::status riscv_hart_mu_p::write_intthresh(unsigned addr, reg_t val) { - this->csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) { - switch(paddr.val) { - default: { - for(auto offs = 0U; offs < length; ++offs) { - *(data + offs) = mem[(paddr.val + offs) % mem.size()]; - } - } - } - return iss::Ok; -} -template -iss::status riscv_hart_mu_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) { - mem_type::page_type& p = mem(paddr.val / mem.page_size); - std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask)); - // tohost handling in case of riscv-test - // according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754: - if(paddr.access && iss::access_type::FUNC) { - if(paddr.val == this->tohost) { - reg_t cur_data = *reinterpret_cast(data); - // Extract Device (bits 63:56) - uint8_t device = traits::XLEN == 32 - ? *reinterpret_cast(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 24 - : (cur_data >> 56) & 0xFF; - // Extract Command (bits 55:48) - uint8_t command = traits::XLEN == 32 - ? *reinterpret_cast(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 16 - : (cur_data >> 48) & 0xFF; - // Extract payload (bits 47:0) - uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; - if(payload_addr & 1) { - CPPLOG(FATAL) << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr - << "), stopping simulation"; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } else if(device == 0 && command == 0) { - std::array loaded_payload; - if(read(address_type::PHYSICAL, access_type::DEBUG_READ, traits::MEM, payload_addr, 8 * sizeof(uint64_t), - reinterpret_cast(loaded_payload.data())) == iss::Err) - CPPLOG(ERR) << "Syscall read went wrong"; - uint64_t syscall_num = loaded_payload.at(0); - if(syscall_num == 64) { // SYS_WRITE - return this->execute_sys_write(this, loaded_payload, traits::MEM); - } else { - CPPLOG(ERR) << "tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num - << ") not implemented"; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } - } else { - CPPLOG(ERR) << "tohost functionality not implemented for device " << device << " and command " << command; - this->reg.trap_state = std::numeric_limits::max(); - this->interrupt_sim = payload_addr; - return iss::Ok; - } - } - if((traits::XLEN == 32 && paddr.val == this->fromhost + 4) || (traits::XLEN == 64 && paddr.val == this->fromhost)) { - uint64_t fhostvar = *reinterpret_cast(p.data() + (this->fromhost & mem.page_addr_mask)); - *reinterpret_cast(p.data() + (this->tohost & mem.page_addr_mask)) = fhostvar; - } - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_clic(uint64_t addr, unsigned length, uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - *data = clic_cfg_reg; - for(auto i = 1; i < length; ++i) - *(data + i) = 0; - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - read_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - read_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - } else { - for(auto i = 0U; i < length; ++i) - *(data + i) = 0; - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e); - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - write_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - write_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1 - } - return iss::Ok; -} - template inline void riscv_hart_mu_p::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state_type::mstatus_reset_val; + state.mstatus = hart_state::mstatus_reset_val; } template void riscv_hart_mu_p::check_interrupt() { @@ -1061,9 +548,9 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ phys_addr_t p_addr(access_type::DEBUG_READ, traits::MEM, addr - 4); std::array data; // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 - this->read_mem(p_addr, 4, data.data()); - p_addr.val += 8; - this->read_mem(p_addr, 4, data.data() + 4); + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data()); + addr += 8; + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4); const std::array ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; if(data == ref_data) { @@ -1124,7 +611,7 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ auto xtvec = this->csr[utvec | (new_priv << 8)]; // calculate addr// set NEXT_PC to trap addressess to jump to based on MODE // bits in mtvec - if((FEAT & features_e::FEAT_CLIC) && trap_id != 0 && (xtvec & 0x3UL) == 3UL) { + if(trap_id != 0 && (xtvec & 0x3UL) == 3UL) { reg_t data; auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast(&data)); if(ret == iss::Err) diff --git a/src/iss/mmio/clic.h b/src/iss/mmio/clic.h new file mode 100644 index 0000000..758baa4 --- /dev/null +++ b/src/iss/mmio/clic.h @@ -0,0 +1,252 @@ + +#include "iss/arch/riscv_hart_common.h" +#include "iss/vm_types.h" +#include "memory_if.h" +#include + +namespace iss { +namespace mmio { +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(®); + 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(®); + 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 struct clic : public memory_elem { + using this_class = clic; + using reg_t = WORD_TYPE; + constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; + + clic(arch::priv_if 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::from(this)}, + .wr_mem{util::delegate::from(this)}}; + } + + void set_next(memory_if mem) override { down_stream_mem = mem; } + + std::tuple 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 hart_if; + memory_if down_stream_mem; + clic_config cfg; + uint8_t clic_cfg_reg{0}; + std::array clic_inttrig_reg; + union clic_int_reg_t { + struct { + uint8_t ip; + uint8_t ie; + uint8_t attr; + uint8_t ctl; + }; + uint32_t raw; + }; + std::vector clic_int_reg; + uint8_t clic_mprev_lvl{0}, clic_uprev_lvl{0}; + uint8_t clic_mact_lvl{0}, clic_uact_lvl{0}; +}; + +template iss::status clic::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 iss::status clic::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 iss::status clic::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.mstatus.UPIE << 27; + break; + default: + val |= clic_mprev_lvl << 16; + val |= hart_if.mstatus.MPIE << 27; + val |= hart_if.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 iss::status clic::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.mstatus.UPIE = (val >> 27) & 0x1; + break; + default: + clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; + hart_if.mstatus.MPIE = (val >> 27) & 0x1; + hart_if.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 iss::status clic::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 iss::status clic::write_intthresh(unsigned addr, reg_t val) { + hart_if.csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; + return iss::Ok; +} + +} // namespace mmio +} // namespace iss diff --git a/src/iss/memory/memory_if.cpp b/src/iss/mmio/memory_if.cpp similarity index 78% rename from src/iss/memory/memory_if.cpp rename to src/iss/mmio/memory_if.cpp index 61f7d24..03c0b70 100644 --- a/src/iss/memory/memory_if.cpp +++ b/src/iss/mmio/memory_if.cpp @@ -1,7 +1,7 @@ #include "memory_if.h" namespace iss { -namespace memory { +namespace mmio { void memory_hierarchy::prepend(memory_elem& e) { hierarchy.push_front(e); update_chain(); @@ -15,11 +15,12 @@ void memory_hierarchy::insert_after(memory_elem&) {} void memory_hierarchy::replace_last(memory_elem&) {} void memory_hierarchy::update_chain() { bool tail = false; - if(hierarchy.size() > 1) - for(size_t i = 1; i < hierarchy.size(); ++i) { + for(size_t i = 0; i < hierarchy.size(); ++i) { + hierarchy[i].get().register_csrs(); + if(i) hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if()); - } + } } -} // namespace memory +} // namespace mmio } // namespace iss \ No newline at end of file diff --git a/src/iss/memory/memory_if.h b/src/iss/mmio/memory_if.h similarity index 93% rename from src/iss/memory/memory_if.h rename to src/iss/mmio/memory_if.h index f1454cd..70adac2 100644 --- a/src/iss/memory/memory_if.h +++ b/src/iss/mmio/memory_if.h @@ -38,11 +38,11 @@ #include "iss/vm_types.h" #include #include -#include +#include #include namespace iss { -namespace memory { +namespace mmio { 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*); @@ -55,6 +55,8 @@ struct memory_if { struct memory_elem { virtual memory_if get_mem_if() = 0; virtual void set_next(memory_if) = 0; + virtual void register_csrs() {} + virtual std::tuple get_range() { return {0, std::numeric_limits::max()}; } }; struct memory_hierarchy { @@ -69,6 +71,6 @@ protected: std::deque> hierarchy; }; -} // namespace memory +} // namespace mmio } // namespace iss #endif \ No newline at end of file diff --git a/src/iss/memory/functional_memory.h b/src/iss/mmio/memory_with_htif.h similarity index 85% rename from src/iss/memory/functional_memory.h rename to src/iss/mmio/memory_with_htif.h index ca7e880..f9c3ebe 100644 --- a/src/iss/memory/functional_memory.h +++ b/src/iss/mmio/memory_with_htif.h @@ -1,3 +1,5 @@ +#ifndef _MEMORY_WITH_HTIF_ +#define _MEMORY_WITH_HTIF_ #include "iss/arch/riscv_hart_common.h" #include "iss/vm_types.h" @@ -6,15 +8,15 @@ #include namespace iss { -namespace memory { -template struct functional_memory : public memory_elem { - using this_class = functional_memory; +namespace mmio { +template struct memory_with_htif : public memory_elem { + using this_class = memory_with_htif; constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; - functional_memory(arch::priv_if hart_if) + memory_with_htif(arch::priv_if hart_if) : hart_if(hart_if) {} - ~functional_memory() = default; + ~memory_with_htif() = default; memory_if get_mem_if() override { return memory_if{.rd_mem{util::delegate::from(this)}, @@ -55,5 +57,6 @@ protected: mem_type mem; arch::priv_if hart_if; }; -} // namespace memory +} // namespace mmio } // namespace iss +#endif // _MEMORY_WITH_HTIF_ \ No newline at end of file diff --git a/src/iss/mmio/pmp.h b/src/iss/mmio/pmp.h new file mode 100644 index 0000000..848b7ea --- /dev/null +++ b/src/iss/mmio/pmp.h @@ -0,0 +1,212 @@ + +#include "iss/arch/riscv_hart_common.h" +#include "iss/vm_types.h" +#include "memory_if.h" +#include + +namespace iss { +namespace mmio { +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(®); + 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(®); + 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 struct pmp : public memory_elem { + using this_class = pmp; + using reg_t = WORD_TYPE; + constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; + + pmp(arch::priv_if hart_if, clic_config cfg) + : hart_if(hart_if) + , cfg(cfg) { + 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); + } + } + + ~pmp() = default; + + memory_if get_mem_if() override { + return memory_if{.rd_mem{util::delegate::from(this)}, + .wr_mem{util::delegate::from(this)}}; + } + + void set_next(memory_if 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 hart_if; + memory_if down_stream_mem; +}; + +template bool pmp::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 mmio +} // namespace iss