diff --git a/CMakeLists.txt b/CMakeLists.txt index 285e363..b9300bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(softfloat) set(LIB_SOURCES src/iss/plugin/instruction_count.cpp src/iss/arch/tgc5c.cpp + src/iss/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 c7348d1..74776c5 100644 --- a/src/iss/arch/riscv_hart_common.h +++ b/src/iss/arch/riscv_hart_common.h @@ -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/arch/traits.h" +#include "iss/log_categories.h" +#include "iss/mmio/memory_if.h" #include "iss/vm_types.h" +#include "mstatus.h" +#include "util/delegate.h" #include #include #include #include #include #include +#include #include #include #include #include #include +#include #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 */ @@ -235,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}; @@ -271,62 +274,145 @@ 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; - } -} +template struct priv_if { + using rd_csr_f = std::function; + using wr_csr_f = std::function; -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; - } -} -struct riscv_hart_common { - riscv_hart_common(){}; + 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 mmio::memory_elem { + const std::array lvl = {{'U', 'S', 'H', 'M'}}; + const std::array 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 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::MEM; + + using core = BASE; + using this_class = riscv_hart_common; + 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; + using wr_csr_f = std::function; + +#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::MISA_VAL; + csr[mvendorid] = 0x669; + csr[marchid] = traits::MARCHID_VAL; + csr[mimpid] = 1; + + if(traits::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::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::XLEN == 32) + for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) { + csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); + } + // common regs + const std::array 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::XLEN == 32) + csr_rd_cb[timeh] = MK_CSR_RD_CB(read_time); + csr_rd_cb[cycle] = MK_CSR_RD_CB(read_cycle); + if(traits::XLEN == 32) + csr_rd_cb[cycleh] = MK_CSR_RD_CB(read_cycle); + csr_rd_cb[instret] = MK_CSR_RD_CB(read_instret); + if(traits::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::XLEN == 32) + csr_rd_cb[mcycleh] = MK_CSR_RD_CB(read_cycle); + if(traits::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::XLEN == 32) + csr_rd_cb[minstreth] = MK_CSR_RD_CB(read_instret); + if(traits::XLEN == 32) + csr_wr_cb[minstreth] = MK_CSR_WR_CB(write_instret); + csr_rd_cb[mhartid] = MK_CSR_RD_CB(read_hartid); + }; ~riscv_hart_common() { if(io_buf.str().length()) { CPPLOG(INFO) << "tohost send '" << io_buf.str() << "'"; } - }; + } + std::unordered_map symbol_table; uint64_t entry_address{0}; uint64_t tohost = std::numeric_limits::max(); uint64_t fromhost = std::numeric_limits::max(); std::stringstream io_buf; - bool read_elf_file(std::string name, uint8_t expected_elf_class, - std::function cb) { + void set_semihosting_callback(semihosting_cb_t cb) { semihosting_cb = cb; }; + + std::pair 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 @@ -344,7 +430,8 @@ struct riscv_hart_common { const auto seg_data = pseg->get_data(); const auto type = pseg->get_type(); if(type == ELFIO::PT_LOAD && fsize > 0) { - auto res = cb(pseg->get_physical_address(), fsize, reinterpret_cast(seg_data)); + auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits::MEM, + pseg->get_physical_address(), fsize, reinterpret_cast(seg_data)); if(res != iss::Ok) CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); } @@ -382,6 +469,7 @@ struct riscv_hart_common { } return false; }; + iss::status execute_sys_write(arch_if* aif, const std::array& loaded_payload, unsigned mem_type) { uint64_t fd = loaded_payload[1]; uint64_t buf_ptr = loaded_payload[2]; @@ -409,6 +497,357 @@ struct riscv_hart_common { } return iss::Ok; } + + constexpr bool has_compressed() { return traits::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(cycle_val); + } else if(addr == mcycleh) { + val = static_cast(cycle_val >> 32); + } + return iss::Ok; + } + + iss::status write_cycle(unsigned addr, reg_t val) { + if(sizeof(typename traits::reg_t) != 4) { + mcycle_csr = static_cast(val); + } else { + if(addr == mcycle) { + mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val; + } else { + mcycle_csr = (static_cast(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(this->reg.instret); + } else if((addr & 0xff) == (minstreth & 0xff)) { + val = static_cast(this->reg.instret >> 32); + } + return iss::Ok; + } + + iss::status write_instret(unsigned addr, reg_t val) { + if(sizeof(typename traits::reg_t) != 4) { + this->reg.instret = static_cast(val); + } else { + if((addr & 0xff) == (minstret & 0xff)) { + this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val; + } else { + this->reg.instret = (static_cast(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(time_val); + } else if(addr == timeh) { + if(sizeof(typename traits::reg_t) != 4) + return iss::Err; + val = static_cast(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 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}, + .mcause_max_irq{mcause_max_irq}}; + } + + iss::status execute_htif(uint8_t const* data) { + reg_t cur_data = *reinterpret_cast(data); + // Extract Device (bits 63:56) + uint8_t device = traits::XLEN == 32 ? 0 : (cur_data >> 56) & 0xFF; + // Extract Command (bits 55:48) + uint8_t command = traits::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::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } else if(device == 0 && command == 0) { + std::array loaded_payload; + if(memory.rd_mem(access_type::DEBUG_READ, payload_addr, 8 * sizeof(uint64_t), + reinterpret_cast(loaded_payload.data())) == iss::Err) + CPPLOG(ERR) << "Syscall read went wrong"; + uint64_t syscall_num = loaded_payload.at(0); + if(syscall_num == 64) { // SYS_WRITE + return this->execute_sys_write(this, loaded_payload, traits::MEM); + } else { + CPPLOG(ERR) << "this->tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num + << ") not implemented"; + this->reg.trap_state = std::numeric_limits::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } + } else { + CPPLOG(ERR) << "this->tohost functionality not implemented for device " << device << " and command " << command; + this->reg.trap_state = std::numeric_limits::max(); + this->interrupt_sim = payload_addr; + return iss::Ok; + } + } + + mmio::memory_hierarchy memories; + + virtual mmio::memory_if get_mem_if() override { + assert(false || "This function should nevver be called"); + return mmio::memory_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: + 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) + : arch(arch) {} + /** + * get the name of this architecture + * + * @return the name of this architecture + */ + const std::string core_type_name() const override { return traits::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::NUM_REGS; } + + unsigned get_reg_size(unsigned num) override { return traits::reg_bit_widths[num]; } + + std::unordered_map const& get_symbol_table(std::string name) override { return arch.symbol_table; } + + riscv_hart_common& 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::reg_t, 1ULL << 12, 12>; + using csr_page_type = typename csr_type::page_type; + csr_type csr; + + std::unordered_map csr_rd_cb; + std::unordered_map 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 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 3fe71b7..6980c2e 100644 --- a/src/iss/arch/riscv_hart_m_p.h +++ b/src/iss/arch/riscv_hart_m_p.h @@ -35,9 +35,6 @@ #ifndef _RISCV_HART_M_P_H #define _RISCV_HART_M_P_H -#include "iss/arch/traits.h" -#include "iss/instrumentation_if.h" -#include "iss/log_categories.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -45,123 +42,30 @@ #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 -#include - -#include namespace iss { namespace arch { template -class riscv_hart_m_p : public BASE, public riscv_hart_common { -protected: - const std::array lvl = {{'U', 'S', 'H', 'M'}}; - const std::array 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 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"}}; - +class riscv_hart_m_p : public riscv_hart_common { public: using core = BASE; + using base = riscv_hart_common; using this_class = riscv_hart_m_p; 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 = iss::status (this_class::*)(unsigned addr, reg_t&); - using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t); - 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); - - constexpr static unsigned MEM = traits::MEM; - - // 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 @@ -177,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 @@ -252,144 +97,48 @@ 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 } - constexpr bool has_compressed() { return traits::MISA_VAL & 0b0100; } - constexpr reg_t get_pc_mask() { return has_compressed() ? (reg_t)~1 : (reg_t)~3; } - riscv_hart_m_p(feature_config cfg = feature_config{}); + virtual ~riscv_hart_m_p() = default; void reset(uint64_t address) override; - std::pair load_file(std::string name, int type = -1) override; - iss::status read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - uint8_t* const data) override; + uint8_t* const data); iss::status write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - const uint8_t* const data) override; + const uint8_t* const data); - uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, fault_data, fault_data); } + uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, this->fault_data, this->fault_data); } uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override; uint64_t leave_trap(uint64_t flags) override; - const reg_t& get_mhartid() const { return mhartid_reg; } - void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; + void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; } - void disass_output(uint64_t pc, const std::string instr) override { - NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x} {:40} [s:0x{:x};c:{}]", pc, instr, (reg_t)state.mstatus, - this->reg.cycle + cycle_offset); - }; - - iss::instrumentation_if* get_instrumentation_if() override { return &instr_if; } - - void set_csr(unsigned addr, reg_t val) { csr[addr & csr.page_addr_mask] = val; } - - void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - - void set_semihosting_callback(semihosting_cb_t cb) { semihosting_cb = cb; }; + void set_num_of_irq(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); } protected: - struct riscv_instrumentation_if : public iss::instrumentation_if { + 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); - riscv_instrumentation_if(riscv_hart_m_p& 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::core_type; } + hart_state state; - 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::NUM_REGS; } - - unsigned get_reg_size(unsigned num) override { return traits::reg_bit_widths[num]; } - - std::unordered_map const& get_symbol_table(std::string name) override { return arch.symbol_table; } - - riscv_hart_m_p& arch; - }; - - friend struct riscv_instrumentation_if; - - 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); - - virtual iss::status read_csr(unsigned addr, reg_t& val); - virtual iss::status write_csr(unsigned addr, reg_t val); - - hart_state_type state; - int64_t cycle_offset{0}; - uint64_t mcycle_csr{0}; - int64_t instret_offset{0}; - uint64_t minstret_csr{0}; - reg_t fault_data; - riscv_instrumentation_if instr_if; - - semihosting_cb_t semihosting_cb; - - using mem_type = util::sparse_array; - using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; - using csr_page_type = typename csr_type::page_type; - mem_type mem; - csr_type csr; std::unordered_map ptw; std::unordered_map atomic_reservation; - std::unordered_map csr_rd_cb; - std::unordered_map csr_wr_cb; - uint8_t clic_cfg_reg{0}; - std::array clic_inttrig_reg; - union clic_int_reg_t { - struct { - uint8_t ip; - uint8_t ie; - uint8_t attr; - uint8_t ctl; - }; - uint32_t raw; - }; - std::vector clic_int_reg; - uint8_t clic_mprev_lvl{0}; - uint8_t clic_mact_lvl{0}; - std::vector tcm; - - iss::status read_plain(unsigned addr, reg_t& val); - iss::status write_plain(unsigned addr, reg_t val); - iss::status read_null(unsigned addr, reg_t& val); - iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; } - iss::status read_cycle(unsigned addr, reg_t& val); - iss::status write_cycle(unsigned addr, reg_t val); - iss::status read_instret(unsigned addr, reg_t& val); - iss::status write_instret(unsigned addr, reg_t val); - iss::status read_tvec(unsigned addr, reg_t& val); - iss::status read_time(unsigned addr, reg_t& val); iss::status read_status(unsigned addr, reg_t& val); iss::status write_status(unsigned addr, reg_t val); iss::status read_cause(unsigned addr, reg_t& val); @@ -397,205 +146,57 @@ 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_hartid(unsigned addr, reg_t& val); - iss::status write_epc(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); iss::status write_dscratch(unsigned addr, reg_t val); iss::status read_dpc(unsigned addr, reg_t& val); iss::status write_dpc(unsigned addr, reg_t val); - iss::status read_fcsr(unsigned addr, reg_t& val); - iss::status write_fcsr(unsigned addr, reg_t val); - - virtual iss::status read_custom_csr(unsigned addr, reg_t& val) { return iss::status::Err; }; - virtual iss::status write_custom_csr(unsigned addr, reg_t val) { return iss::status::Err; }; - - void register_custom_csr_rd(unsigned addr) { csr_rd_cb[addr] = &this_class::read_custom_csr; } - void register_custom_csr_wr(unsigned addr) { csr_wr_cb[addr] = &this_class::write_custom_csr; } - - reg_t mhartid_reg{0x0}; 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}; - inline bool debug_mode_active() { return this->reg.PRIV & 0x4; } - - 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_m_p::riscv_hart_m_p(feature_config cfg) -: state() -, instr_if(*this) -, cfg(cfg) { - // reset values - csr[misa] = traits::MISA_VAL; - csr[mvendorid] = 0x669; - csr[marchid] = traits::MARCHID_VAL; - csr[mimpid] = 1; - - if(traits::FLEN > 0) { - csr_rd_cb[fcsr] = &this_class::read_fcsr; - csr_wr_cb[fcsr] = &this_class::write_fcsr; - } - for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - if(traits::XLEN == 32) - for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - } - if(traits::XLEN == 32) - for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - } - // common regs - const std::array roaddrs{{misa, mvendorid, marchid, mimpid}}; - for(auto addr : roaddrs) { - csr_rd_cb[addr] = &this_class::read_plain; - csr_wr_cb[addr] = &this_class::write_null; - } +: cfg(cfg) +, default_mem(base::get_priv_if()) { const std::array rwaddrs{{mepc, mtvec, mscratch, mtval}}; for(auto addr : rwaddrs) { - csr_rd_cb[addr] = &this_class::read_plain; - csr_wr_cb[addr] = &this_class::write_plain; + this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain); + // MK_CSR_RD_CB(read_plain(a,r);}; + this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); } - // special handling & overrides - csr_rd_cb[time] = &this_class::read_time; - if(traits::XLEN == 32) - csr_rd_cb[timeh] = &this_class::read_time; - csr_rd_cb[cycle] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_rd_cb[cycleh] = &this_class::read_cycle; - csr_rd_cb[instret] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_rd_cb[instreth] = &this_class::read_instret; + this->csr_rd_cb[mstatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[mstatus] = MK_CSR_WR_CB(write_status); + this->csr_rd_cb[mcause] = MK_CSR_RD_CB(read_cause); + this->csr_wr_cb[mcause] = MK_CSR_WR_CB(write_cause); + this->csr_rd_cb[mtvec] = MK_CSR_RD_CB(read_tvec); + this->csr_wr_cb[mepc] = MK_CSR_WR_CB(write_epc); + this->csr_rd_cb[mip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[mip] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[mie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[mie] = MK_CSR_WR_CB(write_ie); + this->csr_wr_cb[misa] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mvendorid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null); - csr_rd_cb[mcycle] = &this_class::read_cycle; - csr_wr_cb[mcycle] = &this_class::write_cycle; - if(traits::XLEN == 32) - csr_rd_cb[mcycleh] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_wr_cb[mcycleh] = &this_class::write_cycle; - csr_rd_cb[minstret] = &this_class::read_instret; - csr_wr_cb[minstret] = &this_class::write_instret; - if(traits::XLEN == 32) - csr_rd_cb[minstreth] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_wr_cb[minstreth] = &this_class::write_instret; - csr_rd_cb[mstatus] = &this_class::read_status; - csr_wr_cb[mstatus] = &this_class::write_status; - csr_rd_cb[mcause] = &this_class::read_cause; - csr_wr_cb[mcause] = &this_class::write_cause; - csr_rd_cb[mtvec] = &this_class::read_tvec; - csr_wr_cb[mepc] = &this_class::write_epc; - csr_rd_cb[mip] = &this_class::read_ip; - csr_wr_cb[mip] = &this_class::write_null; - csr_rd_cb[mie] = &this_class::read_ie; - csr_wr_cb[mie] = &this_class::write_ie; - csr_rd_cb[mhartid] = &this_class::read_hartid; - csr_wr_cb[misa] = &this_class::write_null; - csr_wr_cb[mvendorid] = &this_class::write_null; - csr_wr_cb[marchid] = &this_class::write_null; - csr_wr_cb[mimpid] = &this_class::write_null; - if(FEAT & FEAT_CLIC) { - csr_rd_cb[mtvt] = &this_class::read_plain; - csr_wr_cb[mtvt] = &this_class::write_xtvt; - // csr_rd_cb[mxnti] = &this_class::read_csr_reg; - // csr_wr_cb[mxnti] = &this_class::write_csr_reg; - csr_rd_cb[mintstatus] = &this_class::read_intstatus; - csr_wr_cb[mintstatus] = &this_class::write_null; - // csr_rd_cb[mscratchcsw] = &this_class::read_csr_reg; - // csr_wr_cb[mscratchcsw] = &this_class::write_csr_reg; - // csr_rd_cb[mscratchcswl] = &this_class::read_csr_reg; - // csr_wr_cb[mscratchcswl] = &this_class::write_csr_reg; - csr_rd_cb[mintthresh] = &this_class::read_plain; - csr_wr_cb[mintthresh] = &this_class::write_intthresh; - clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0}); - clic_cfg_reg = 0x20; - clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1; - csr[mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; - insert_mem_range( - cfg.clic_base, 0x5000UL, - [this](phys_addr_t addr, unsigned length, uint8_t* const data) { return read_clic(addr.val, length, data); }, - [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { return write_clic(addr.val, length, data); }); - } - if(FEAT & FEAT_TCM) { - tcm.resize(cfg.tcm_size); - std::function read_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(tcm.data() + offset, tcm.data() + offset + length, data); - return iss::Ok; - }; - std::function write_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t const* const data) { - auto offset = addr.val - this->cfg.tcm_base; - std::copy(data, data + length, tcm.data() + offset); - return iss::Ok; - }; - insert_mem_range(cfg.tcm_base, cfg.tcm_size, read_clic_cb, write_clic_cb); - } if(FEAT & FEAT_DEBUG) { - csr_wr_cb[dscratch0] = &this_class::write_dscratch; - csr_rd_cb[dscratch0] = &this_class::read_debug; - csr_wr_cb[dscratch1] = &this_class::write_dscratch; - csr_rd_cb[dscratch1] = &this_class::read_debug; - csr_wr_cb[dpc] = &this_class::write_dpc; - csr_rd_cb[dpc] = &this_class::read_dpc; - csr_wr_cb[dcsr] = &this_class::write_dcsr; - csr_rd_cb[dcsr] = &this_class::read_debug; + this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch); + this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug); + this->csr_wr_cb[dscratch1] = MK_CSR_WR_CB(write_dscratch); + this->csr_rd_cb[dscratch1] = MK_CSR_RD_CB(read_debug); + this->csr_wr_cb[dpc] = MK_CSR_WR_CB(write_dpc); + this->csr_rd_cb[dpc] = MK_CSR_RD_CB(read_dpc); + 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 -std::pair riscv_hart_m_p::load_file(std::string name, int type) { - if(read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64, - [this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status { - return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits::MEM, addr, size, - data); - })) { - return std::make_pair(entry_address, true); - } - return std::make_pair(entry_address, false); -} - -template -inline void riscv_hart_m_p::insert_mem_range(uint64_t base, uint64_t size, std::function rd_f, - std::function wr_fn) { - std::tuple entry{base, size}; - auto it = std::upper_bound( - memfn_range.begin(), memfn_range.end(), entry, - [](std::tuple const& a, std::tuple const& b) { return std::get<0>(a) < std::get<0>(b); }); - auto idx = std::distance(memfn_range.begin(), it); - memfn_range.insert(it, entry); - memfn_read.insert(std::begin(memfn_read) + idx, rd_f); - memfn_write.insert(std::begin(memfn_write) + idx, wr_fn); + this->rd_func = util::delegate::from(this); + this->wr_func = util::delegate::from(this); + this->memories.prepend(*this); + this->memories.append(default_mem); } template @@ -604,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; @@ -613,9 +214,9 @@ iss::status riscv_hart_m_p::read(const address_type type, co try { switch(space) { case traits::MEM: { - auto alignment = is_fetch(access) ? (has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); + auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) { - fault_data = addr; + this->fault_data = addr; if(is_debug(access)) throw trap_access(0, addr); this->reg.trap_state = (1UL << 31); // issue trap 0 @@ -624,33 +225,19 @@ iss::status riscv_hart_m_p::read(const address_type type, co try { if(!is_debug(access) && (addr & (alignment - 1))) { this->reg.trap_state = (1UL << 31) | 4 << 16; - fault_data = addr; + 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 - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -658,11 +245,9 @@ iss::status riscv_hart_m_p::read(const address_type type, co case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return read_csr(addr, *reinterpret_cast(data)); + 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: { @@ -680,7 +265,7 @@ iss::status riscv_hart_m_p::read(const address_type type, co } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -716,7 +301,7 @@ iss::status riscv_hart_m_p::write(const address_type type, c switch(space) { case traits::MEM: { if(unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) { - fault_data = addr; + this->fault_data = addr; if(access && iss::access_type::DEBUG) throw trap_access(0, addr); this->reg.trap_state = (1UL << 31); // issue trap 0 @@ -726,52 +311,28 @@ iss::status riscv_hart_m_p::write(const address_type type, c auto alignment = std::min(length, sizeof(reg_t)); if(length > 1 && (addr & (alignment - 1)) && !is_debug(access)) { this->reg.trap_state = (1UL << 31) | 6 << 16; - fault_data = addr; + this->fault_data = addr; return iss::Err; } - phys_addr_t phys_addr{access, space, addr}; - auto res = iss::Err; - if(!is_fetch(access) && memfn_range.size()) { - auto it = - std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple const& a) { - return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val; - }); - if(it != std::end(memfn_range)) { - auto idx = std::distance(std::begin(memfn_range), it); - res = memfn_write[idx](phys_addr, length, data); - } else - res = write_mem(phys_addr, length, data); - } else { - res = write_mem(phys_addr, length, data); - } + auto res = this->memory.wr_mem(access, addr, length, data); if(unlikely(res != iss::Ok && !is_debug(access))) { this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault) - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; return iss::Err; } } break; case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return write_csr(addr, *reinterpret_cast(data)); - } break; - case traits::FENCE: { - if((addr + length) > mem.size()) - return iss::Err; - switch(addr) { - case 2: - case 3: { - ptw.clear(); - auto tvm = state.mstatus.TVM; - return iss::Ok; - } - } + return this->write_csr(addr, *reinterpret_cast(data)); } break; + case traits::FENCE: + break; case traits::RES: { atomic_reservation[addr] = data[0]; } break; @@ -782,185 +343,49 @@ iss::status riscv_hart_m_p::write(const address_type type, c } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } } -template -iss::status riscv_hart_m_p::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 (this->*(it->second))(addr, val); -} - -template -iss::status riscv_hart_m_p::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 (this->*(it->second))(addr, val); -} - -template -iss::status riscv_hart_m_p::read_null(unsigned addr, reg_t& val) { - val = 0; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_plain(unsigned addr, reg_t& val) { - val = csr[addr]; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_plain(unsigned addr, reg_t val) { - csr[addr] = val; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_cycle(unsigned addr, reg_t& val) { - auto cycle_val = this->reg.cycle + cycle_offset; - if(addr == mcycle) { - val = static_cast(cycle_val); - } else if(addr == mcycleh) { - val = static_cast(cycle_val >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_cycle(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - mcycle_csr = static_cast(val); - } else { - if(addr == mcycle) { - mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val; - } else { - mcycle_csr = (static_cast(val) << 32) + (mcycle_csr & 0xffffffff); - } - } - cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_instret(unsigned addr, reg_t& val) { - if((addr & 0xff) == (minstret & 0xff)) { - val = static_cast(this->reg.instret); - } else if((addr & 0xff) == (minstreth & 0xff)) { - val = static_cast(this->reg.instret >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_instret(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - this->reg.instret = static_cast(val); - } else { - if((addr & 0xff) == (minstret & 0xff)) { - this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val; - } else { - this->reg.instret = (static_cast(val) << 32) + (this->reg.instret & 0xffffffff); - } - } - this->reg.instret--; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_time(unsigned addr, reg_t& val) { - uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052; - if(addr == time) { - val = static_cast(time_val); - } else if(addr == timeh) { - if(sizeof(typename traits::reg_t) != 4) - return iss::Err; - val = static_cast(time_val >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_tvec(unsigned addr, reg_t& val) { - val = FEAT & features_e::FEAT_CLIC ? csr[addr] : csr[addr] & ~2; - return iss::Ok; -} - 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) && (csr[mtvec] & 0x3) == 3) { - val = csr[addr] & ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - val |= clic_mprev_lvl << 16; - val |= state.mstatus.MPIE << 27; - val |= state.mstatus.MPP << 28; - } else - val = 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) && (csr[mtvec] & 0x3) == 3) { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - csr[addr] = (val & mask) | (csr[addr] & ~mask); - clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; - state.mstatus.MPIE = (val >> 27) & 0x1; - state.mstatus.MPP = (val >> 28) & 0x3; - } else { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1)); - csr[addr] = (val & mask) | (csr[addr] & ~mask); - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_hartid(unsigned addr, reg_t& val) { - val = mhartid_reg; + auto mask = ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); + this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); return iss::Ok; } template iss::status riscv_hart_m_p::read_ie(unsigned addr, reg_t& val) { auto mask = get_irq_mask(); - val = csr[mie] & mask; + val = this->csr[mie] & mask; return iss::Ok; } template iss::status riscv_hart_m_p::write_ie(unsigned addr, reg_t val) { auto mask = get_irq_mask(); - csr[mie] = (csr[mie] & ~mask) | (val & mask); + this->csr[mie] = (this->csr[mie] & ~mask) | (val & mask); check_interrupt(); return iss::Ok; } @@ -968,194 +393,13 @@ iss::status riscv_hart_m_p::write_ie(unsigned addr, reg_t va template iss::status riscv_hart_m_p::read_ip(unsigned addr, reg_t& val) { auto mask = get_irq_mask(); - val = csr[mip] & mask; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_epc(unsigned addr, reg_t val) { - csr[addr] = val & get_pc_mask(); - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::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; -} - -template -iss::status riscv_hart_m_p::read_debug(unsigned addr, reg_t& val) { - if(!debug_mode_active()) - throw illegal_instruction_fault(this->fault_data); - val = csr[addr]; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_dscratch(unsigned addr, reg_t val) { - if(!debug_mode_active()) - throw illegal_instruction_fault(this->fault_data); - csr[addr] = val; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::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; -} - -template -iss::status riscv_hart_m_p::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; -} - -template -iss::status riscv_hart_m_p::read_intstatus(unsigned addr, reg_t& val) { - val = (clic_mact_lvl & 0xff) << 24; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_fcsr(unsigned addr, reg_t& val) { - val = this->get_fcsr(); - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_fcsr(unsigned addr, reg_t val) { - this->set_fcsr(val); - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_intthresh(unsigned addr, reg_t val) { - csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_xtvt(unsigned addr, reg_t val) { - csr[addr] = val & ~0x3fULL; - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) { - switch(paddr.val) { - default: { - for(auto offs = 0U; offs < length; ++offs) { - *(data + offs) = mem[(paddr.val + offs) % mem.size()]; - } - } - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) { - mem_type::page_type& p = mem(paddr.val / mem.page_size); - std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask)); - // 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 == tohost) { - reg_t cur_data = *reinterpret_cast(data); - // Extract Device (bits 63:56) - uint8_t device = traits::XLEN == 32 ? *reinterpret_cast(p.data() + ((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() + ((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 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 == fromhost + 4) || (traits::XLEN == 64 && paddr.val == fromhost)) { - uint64_t fhostvar = *reinterpret_cast(p.data() + (fromhost & mem.page_addr_mask)); - *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; - } - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::read_clic(uint64_t addr, unsigned length, uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - *data = clic_cfg_reg; - for(auto i = 1; i < length; ++i) - *(data + i) = 0; - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - read_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - read_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - } else { - for(auto i = 0U; i < length; ++i) - *(data + i) = 0; - } - return iss::Ok; -} - -template -iss::status riscv_hart_m_p::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) { - if(addr == cfg.clic_base) { // cliccfg - clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e); - } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig - auto offset = ((addr & 0x7fff) - 0x40) / 4; - write_reg_uint32(addr, clic_inttrig_reg[offset], data, length); - } else if(addr >= (cfg.clic_base + 0x1000) && - (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl - auto offset = ((addr & 0x7fff) - 0x1000) / 4; - write_reg_uint32(addr, clic_int_reg[offset].raw, data, length); - clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1 - } + val = this->csr[mip] & mask; return iss::Ok; } template inline void riscv_hart_m_p::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state_type::mstatus_reset_val; + this->state.mstatus = hart_state::mstatus_reset_val; } template void riscv_hart_m_p::check_interrupt() { @@ -1165,9 +409,9 @@ 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; @@ -1193,7 +437,7 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t if(cause == 11) cause = 0x8 + PRIV_M; // adjust environment call cause // store ret addr in xepc register - csr[mepc] = static_cast(addr) & get_pc_mask(); // store actual address instruction of exception + this->csr[mepc] = static_cast(addr) & this->get_pc_mask(); // store actual address instruction of exception /* * write mtval if new_priv=M_MODE, spec says: * When a hardware breakpoint is triggered, or an instruction-fetch, load, @@ -1203,27 +447,26 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t */ switch(cause) { case 0: - csr[mtval] = static_cast(tval); + this->csr[mtval] = static_cast(tval); break; case 2: - csr[mtval] = (!has_compressed() || (tval & 0x3) == 3) ? tval : tval & 0xffff; + this->csr[mtval] = (!this->has_compressed() || (tval & 0x3) == 3) ? tval : tval & 0xffff; break; case 3: - if((FEAT & FEAT_DEBUG) && (csr[dcsr] & 0x8000)) { + if((FEAT & FEAT_DEBUG) && (this->csr[dcsr] & 0x8000)) { this->reg.DPC = addr; - csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1 << 6) | PRIV_M; // FIXME: cause should not be 4 (stepi) + this->csr[dcsr] = (this->csr[dcsr] & ~0x1c3) | (1 << 6) | PRIV_M; // FIXME: cause should not be 4 (stepi) new_priv = this->reg.PRIV | PRIV_D; } else { - csr[mtval] = addr; + this->csr[mtval] = addr; } - if(semihosting_cb) { + if(this->semihosting_cb) { // Check for semihosting call - phys_addr_t p_addr(access_type::DEBUG_READ, traits::MEM, addr - 4); std::array data; // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 - this->read_mem(p_addr, 4, data.data()); - p_addr.val += 8; - this->read_mem(p_addr, 4, data.data() + 4); + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data()); + addr += 8; + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4); const std::array ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; if(data == ref_data) { @@ -1237,24 +480,24 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t #endif NSCLOG(INFO, LOGCAT) << "Semihosting call at address " << buffer.data() << " occurred "; - semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); + this->semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); return this->reg.NEXT_PC; } } break; case 4: case 6: - csr[mtval] = fault_data; + this->csr[mtval] = this->fault_data; break; default: - csr[mtval] = 0; + this->csr[mtval] = 0; } - fault_data = 0; + this->fault_data = 0; } else { - csr[mepc] = this->reg.NEXT_PC & get_pc_mask(); // store next address if interrupt + this->csr[mepc] = this->reg.NEXT_PC & this->get_pc_mask(); // store next address if interrupt this->reg.pending_trap = 0; } - csr[mcause] = (trap_id << (traits::XLEN - 1)) + cause; + this->csr[mcause] = (trap_id << (traits::XLEN - 1)) + cause; // update mstatus // xPP field of mstatus is written with the active privilege mode at the time // of the trap; the x PIE field of mstatus @@ -1262,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 = csr[mtvec]; + 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, 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; @@ -1291,17 +535,17 @@ uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t sprintf(buffer.data(), "0x%016lx", addr); #endif if((flags & 0xffffffff) != 0xffffffff) - NSCLOG(INFO, LOGCAT) << (trap_id ? "Interrupt" : "Trap") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" - << cause << ")" + NSCLOG(INFO, LOGCAT) << (trap_id ? "Interrupt" : "Trap") << " with cause '" + << (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")" << " at address " << buffer.data() << " occurred"; return this->reg.NEXT_PC; } 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 = csr[mepc] & get_pc_mask(); + this->reg.NEXT_PC = this->csr[mepc] & this->get_pc_mask(); NSCLOG(INFO, LOGCAT) << "Executing xRET"; check_interrupt(); this->reg.trap_state = this->reg.pending_trap; diff --git a/src/iss/arch/riscv_hart_msu_vp.h b/src/iss/arch/riscv_hart_msu_vp.h index ca621c2..f975377 100644 --- a/src/iss/arch/riscv_hart_msu_vp.h +++ b/src/iss/arch/riscv_hart_msu_vp.h @@ -35,9 +35,6 @@ #ifndef _RISCV_HART_MSU_VP_H #define _RISCV_HART_MSU_VP_H -#include "iss/arch/traits.h" -#include "iss/instrumentation_if.h" -#include "iss/log_categories.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -46,124 +43,34 @@ #include #include #include -#include #ifndef FMT_HEADER_ONLY #define FMT_HEADER_ONLY #endif #include #include #include -#include -#include -#include #include #include #include -#include -#include - -#include namespace iss { namespace arch { -template class riscv_hart_msu_vp : public BASE, public riscv_hart_common { -protected: - const std::array lvl = {{'U', 'S', 'H', 'M'}}; - const std::array 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 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"}}; - +template class riscv_hart_msu_vp : public riscv_hart_common { public: using core = BASE; + using base = riscv_hart_common; using this_class = riscv_hart_msu_vp; - using virt_addr_t = typename core::virt_addr_t; 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 = iss::status (this_class::*)(unsigned addr, reg_t&); - using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t); + 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; + } - constexpr static unsigned MEM = traits::MEM; - - // 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, unsigned priv_lvl) { - auto mask = get_mask(priv_lvl); - auto new_val = (mstatus.st.value & ~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 @@ -176,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; @@ -314,119 +170,42 @@ public: return m[mode]; } - riscv_hart_msu_vp(feature_config cfg = feature_config{}); + riscv_hart_msu_vp(); + virtual ~riscv_hart_msu_vp() = default; void reset(uint64_t address) override; - std::pair load_file(std::string name, int type = -1) override; - phys_addr_t virt2phys(const iss::addr_t& addr) override; iss::status read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - uint8_t* const data) override; + uint8_t* const data); iss::status write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - const uint8_t* const data) override; + const uint8_t* const data); - uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, fault_data, fault_data); } + uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, this->fault_data, this->fault_data); } uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override; uint64_t leave_trap(uint64_t flags) override; void wait_until(uint64_t flags) override; - void disass_output(uint64_t pc, const std::string instr) override { - CLOG(INFO, disass) << 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); - }; + void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; } - iss::instrumentation_if* get_instrumentation_if() override { return &instr_if; } - - void set_csr(unsigned addr, reg_t val) { csr[addr & csr.page_addr_mask] = val; } - - void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - - void set_semihosting_callback(std::function& cb) { semihosting_cb = cb; }; + void set_irq_num(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); } protected: - struct riscv_instrumentation_if : public iss::instrumentation_if { - - riscv_instrumentation_if(riscv_hart_msu_vp& 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::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::NUM_REGS; } - - unsigned get_reg_size(unsigned num) override { return traits::reg_bit_widths[num]; } - - std::unordered_map const& get_symbol_table(std::string name) override { return arch.symbol_table; } - - riscv_hart_msu_vp& arch; - }; - - friend struct riscv_instrumentation_if; - addr_t get_pc() { return this->reg.PC; } - addr_t get_next_pc() { return this->reg.NEXT_PC; } - 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); - virtual iss::status read_csr(unsigned addr, reg_t& val); - virtual iss::status write_csr(unsigned addr, reg_t val); - - hart_state_type state; - int64_t cycle_offset{0}; - uint64_t mcycle_csr{0}; - int64_t instret_offset{0}; - uint64_t minstret_csr{0}; - reg_t fault_data; - std::array vm; - riscv_instrumentation_if instr_if; - - std::function semihosting_cb; + hart_state state; using mem_type = util::sparse_array; - using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; - using csr_page_type = typename csr_type::page_type; mem_type mem; - csr_type csr; void update_vm_info(); std::unordered_map ptw; std::unordered_map atomic_reservation; - std::unordered_map csr_rd_cb; - std::unordered_map csr_wr_cb; std::vector tcm; - iss::status read_csr_reg(unsigned addr, reg_t& val); - iss::status write_csr_reg(unsigned addr, reg_t val); - iss::status read_null(unsigned addr, reg_t& val); - iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; } - iss::status read_cycle(unsigned addr, reg_t& val); - iss::status write_cycle(unsigned addr, reg_t val); - iss::status read_instret(unsigned addr, reg_t& val); - iss::status write_instret(unsigned addr, reg_t val); - iss::status read_tvec(unsigned addr, reg_t& val); - iss::status read_time(unsigned addr, reg_t& val); iss::status read_status(unsigned addr, reg_t& val); iss::status write_status(unsigned addr, reg_t val); iss::status write_cause(unsigned addr, reg_t val); @@ -435,18 +214,14 @@ protected: iss::status read_ip(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 read_hartid(unsigned addr, reg_t& val); - iss::status write_epc(unsigned addr, reg_t val); iss::status read_satp(unsigned addr, reg_t& val); iss::status write_satp(unsigned addr, reg_t val); - iss::status read_fcsr(unsigned addr, reg_t& val); - iss::status write_fcsr(unsigned addr, reg_t val); virtual iss::status read_custom_csr_reg(unsigned addr, reg_t& val) { return iss::status::Err; }; virtual iss::status write_custom_csr_reg(unsigned addr, reg_t val) { return iss::status::Err; }; - void register_custom_csr_rd(unsigned addr) { csr_rd_cb[addr] = &this_class::read_custom_csr_reg; } - void register_custom_csr_wr(unsigned addr) { csr_wr_cb[addr] = &this_class::write_custom_csr_reg; } + void register_custom_csr_rd(unsigned addr) { this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_custom_csr_reg); } + void register_custom_csr_wr(unsigned addr) { this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_custom_csr_reg); } reg_t mhartid_reg{0x0}; @@ -455,118 +230,50 @@ protected: template riscv_hart_msu_vp::riscv_hart_msu_vp() -: state() -, instr_if(*this) { - this->_has_mmu = true; - // reset values - csr[misa] = traits::MISA_VAL; - csr[mvendorid] = 0x669; - csr[marchid] = traits::MARCHID_VAL; - csr[mimpid] = 1; - - for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_csr_reg; - } - for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_csr_reg; - } - for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_csr_reg; - } - for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - } - for(unsigned addr = cycleh; addr <= hpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - // csr_wr_cb[addr] = &this_class::write_csr_reg; - } +: state() { + this->mmu = true; + this->rd_func = util::delegate::from(this); + this->rd_func = util::delegate::from(this); // common regs - const std::array addrs{{misa, mvendorid, marchid, mimpid, mepc, mtvec, mscratch, mcause, - mtval, mscratch, sepc, stvec, sscratch, scause, stval, sscratch, - uepc, utvec, uscratch, ucause, utval, uscratch}}; - for(auto addr : addrs) { - csr_rd_cb[addr] = &this_class::read_csr_reg; - csr_wr_cb[addr] = &this_class::write_csr_reg; + const std::array rwaddrs{{mepc, mtvec, mscratch, mtval, mscratch, sepc, stvec, sscratch, scause, stval, sscratch, uepc, + utvec, uscratch, ucause, utval, uscratch}}; + for(auto addr : rwaddrs) { + this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain); + this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); } // special handling & overrides - csr_rd_cb[time] = &this_class::read_time; - if(traits::XLEN == 32) - csr_rd_cb[timeh] = &this_class::read_time; - csr_rd_cb[cycle] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_rd_cb[cycleh] = &this_class::read_cycle; - csr_rd_cb[instret] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_rd_cb[instreth] = &this_class::read_instret; - - csr_rd_cb[mcycle] = &this_class::read_cycle; - csr_wr_cb[mcycle] = &this_class::write_cycle; - if(traits::XLEN == 32) - csr_rd_cb[mcycleh] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_wr_cb[mcycleh] = &this_class::write_cycle; - csr_rd_cb[minstret] = &this_class::read_instret; - csr_wr_cb[minstret] = &this_class::write_instret; - if(traits::XLEN == 32) - csr_rd_cb[minstreth] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_wr_cb[minstreth] = &this_class::write_instret; - csr_rd_cb[mstatus] = &this_class::read_status; - csr_wr_cb[mstatus] = &this_class::write_status; - csr_wr_cb[mcause] = &this_class::write_cause; - csr_rd_cb[sstatus] = &this_class::read_status; - csr_wr_cb[sstatus] = &this_class::write_status; - csr_wr_cb[scause] = &this_class::write_cause; - csr_rd_cb[ustatus] = &this_class::read_status; - csr_wr_cb[ustatus] = &this_class::write_status; - csr_wr_cb[ucause] = &this_class::write_cause; - csr_rd_cb[mtvec] = &this_class::read_tvec; - csr_rd_cb[stvec] = &this_class::read_tvec; - csr_rd_cb[utvec] = &this_class::read_tvec; - csr_wr_cb[mepc] = &this_class::write_epc; - csr_wr_cb[sepc] = &this_class::write_epc; - csr_wr_cb[uepc] = &this_class::write_epc; - csr_rd_cb[mip] = &this_class::read_ip; - csr_wr_cb[mip] = &this_class::write_null; - csr_rd_cb[sip] = &this_class::read_ip; - csr_wr_cb[sip] = &this_class::write_null; - csr_rd_cb[uip] = &this_class::read_ip; - csr_wr_cb[uip] = &this_class::write_null; - csr_rd_cb[mie] = &this_class::read_ie; - csr_wr_cb[mie] = &this_class::write_ie; - csr_rd_cb[sie] = &this_class::read_ie; - csr_wr_cb[sie] = &this_class::write_ie; - csr_rd_cb[uie] = &this_class::read_ie; - csr_wr_cb[uie] = &this_class::write_ie; - csr_rd_cb[mhartid] = &this_class::read_hartid; - csr_rd_cb[mcounteren] = &this_class::read_null; - csr_wr_cb[mcounteren] = &this_class::write_null; - csr_wr_cb[misa] = &this_class::write_null; - csr_wr_cb[mvendorid] = &this_class::write_null; - csr_wr_cb[marchid] = &this_class::write_null; - csr_wr_cb[mimpid] = &this_class::write_null; - csr_rd_cb[satp] = &this_class::read_satp; - csr_wr_cb[satp] = &this_class::write_satp; - csr_rd_cb[fcsr] = &this_class::read_fcsr; - csr_wr_cb[fcsr] = &this_class::write_fcsr; - csr_rd_cb[fflags] = &this_class::read_fcsr; - csr_wr_cb[fflags] = &this_class::write_fcsr; - csr_rd_cb[frm] = &this_class::read_fcsr; - csr_wr_cb[frm] = &this_class::write_fcsr; -} - -template std::pair riscv_hart_msu_vp::load_file(std::string name, int type) { - if(read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64, - [this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status { - return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits::MEM, addr, size, - data); - })) { - return std::make_pair(entry_address, true); - } - return std::make_pair(entry_address, false); + this->csr_rd_cb[mstatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[mstatus] = MK_CSR_WR_CB(write_status); + this->csr_wr_cb[mcause] = MK_CSR_WR_CB(write_cause); + this->csr_rd_cb[sstatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[sstatus] = MK_CSR_WR_CB(write_status); + this->csr_wr_cb[scause] = MK_CSR_WR_CB(write_cause); + this->csr_rd_cb[ustatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[ustatus] = MK_CSR_WR_CB(write_status); + this->csr_wr_cb[ucause] = MK_CSR_WR_CB(write_cause); + this->csr_rd_cb[mtvec] = MK_CSR_RD_CB(read_tvec); + this->csr_rd_cb[stvec] = MK_CSR_RD_CB(read_tvec); + this->csr_rd_cb[utvec] = MK_CSR_RD_CB(read_tvec); + this->csr_rd_cb[mip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[mip] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[sip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[sip] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[uip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[uip] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[mie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[mie] = MK_CSR_WR_CB(write_ie); + this->csr_rd_cb[sie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[sie] = MK_CSR_WR_CB(write_ie); + this->csr_rd_cb[uie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[uie] = MK_CSR_WR_CB(write_ie); + this->csr_rd_cb[mcounteren] = MK_CSR_RD_CB(read_null); + this->csr_wr_cb[mcounteren] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[misa] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mvendorid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[satp] = MK_CSR_RD_CB(read_satp); + this->csr_wr_cb[satp] = MK_CSR_WR_CB(write_satp); } template @@ -584,9 +291,9 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ try { switch(space) { case traits::MEM: { - auto alignment = is_fetch(access) ? (has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); + auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) { - fault_data = addr; + this->fault_data = addr; if(access && iss::access_type::DEBUG) throw trap_access(0, addr); this->reg.trap_state = (1 << 31); // issue trap 0 @@ -595,11 +302,11 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ try { if(!is_debug(access) && (addr & (alignment - 1))) { this->reg.trap_state = 1 << 31 | 4 << 16; - fault_data = addr; + this->fault_data = addr; 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; @@ -609,16 +316,16 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ return res; } } - auto res = read_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); + auto res = read_mem(virt2phys(iss::addr_t{access, type, space, addr}), length, data); if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -626,7 +333,7 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return read_csr(addr, *reinterpret_cast(data)); + return this->read_csr(addr, *reinterpret_cast(data)); } break; case traits::FENCE: { if((addr + length) > mem.size()) @@ -659,7 +366,7 @@ iss::status riscv_hart_msu_vp::read(const address_type type, const access_ } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -695,17 +402,17 @@ iss::status riscv_hart_msu_vp::write(const address_type type, const access switch(space) { case traits::MEM: { if(unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) { - fault_data = addr; + this->fault_data = addr; if(access && iss::access_type::DEBUG) throw trap_access(0, addr); this->reg.trap_state = (1 << 31); // issue trap 0 return iss::Err; } - phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr}); + phys_addr_t paddr = virt2phys(iss::addr_t{access, type, space, addr}); 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; @@ -718,19 +425,19 @@ iss::status riscv_hart_msu_vp::write(const address_type type, const access auto res = write_mem(paddr, length, data); if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault) - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; return iss::Err; } } break; case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return write_csr(addr, *reinterpret_cast(data)); + return this->write_csr(addr, *reinterpret_cast(data)); } break; case traits::FENCE: { if((addr + length) > mem.size()) @@ -759,171 +466,54 @@ iss::status riscv_hart_msu_vp::write(const address_type type, const access } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } } -template iss::status riscv_hart_msu_vp::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 (this->*(it->second))(addr, val); -} - -template iss::status riscv_hart_msu_vp::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 (this->*(it->second))(addr, val); -} - -template iss::status riscv_hart_msu_vp::read_reg(unsigned addr, reg_t& val) { - val = csr[addr]; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_null(unsigned addr, reg_t& val) { - val = 0; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::write_reg(unsigned addr, reg_t val) { - csr[addr] = val; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_cycle(unsigned addr, reg_t& val) { - auto cycle_val = this->reg.cycle + cycle_offset; - if(addr == mcycle) { - val = static_cast(cycle_val); - } else if(addr == mcycleh) { - if(sizeof(typename traits::reg_t) != 4) - return iss::Err; - val = static_cast(cycle_val >> 32); - } - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::write_cycle(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - mcycle_csr = static_cast(val); - } else { - if(addr == mcycle) { - mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val; - } else { - mcycle_csr = (static_cast(val) << 32) + (mcycle_csr & 0xffffffff); - } - } - cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_instret(unsigned addr, reg_t& val) { - if((addr & 0xff) == (minstret & 0xff)) { - val = static_cast(this->reg.instret); - } else if((addr & 0xff) == (minstreth & 0xff)) { - val = static_cast(this->reg.instret >> 32); - } - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::write_instret(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - this->reg.instret = static_cast(val); - } else { - if((addr & 0xff) == (minstret & 0xff)) { - this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val; - } else { - this->reg.instret = (static_cast(val) << 32) + (this->reg.instret & 0xffffffff); - } - } - this->reg.instret--; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_time(unsigned addr, reg_t& val) { - uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052; - if(addr == time) { - val = static_cast(time_val); - } else if(addr == timeh) { - if(sizeof(typename traits::reg_t) != 4) - return iss::Err; - val = static_cast(time_val >> 32); - } - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_tvec(unsigned addr, reg_t& val) { - val = csr[addr] & ~2; - return iss::Ok; -} - 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; } template iss::status riscv_hart_msu_vp::write_cause(unsigned addr, reg_t val) { - csr[addr] = val & ((1UL << (traits::XLEN - 1)) | 0xf); // TODO: make exception code size configurable + this->csr[addr] = val & ((1UL << (traits::XLEN - 1)) | 0xf); // TODO: make exception code size configurable return iss::Ok; } template iss::status riscv_hart_msu_vp::read_ie(unsigned addr, reg_t& val) { - val = csr[mie]; + val = this->csr[mie]; if(addr < mie) - val &= csr[mideleg]; + val &= this->csr[mideleg]; if(addr < sie) - val &= csr[sideleg]; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::read_hartid(unsigned addr, reg_t& val) { - val = mhartid_reg; + val &= this->csr[sideleg]; return iss::Ok; } template iss::status riscv_hart_msu_vp::write_ie(unsigned addr, reg_t val) { auto req_priv_lvl = (addr >> 8) & 0x3; auto mask = get_irq_mask(req_priv_lvl); - csr[mie] = (csr[mie] & ~mask) | (val & mask); + this->csr[mie] = (this->csr[mie] & ~mask) | (val & mask); check_interrupt(); return iss::Ok; } template iss::status riscv_hart_msu_vp::read_ip(unsigned addr, reg_t& val) { - val = csr[mip]; + val = this->csr[mip]; if(addr < mip) - val &= csr[mideleg]; + val &= this->csr[mideleg]; if(addr < sip) - val &= csr[sideleg]; - return iss::Ok; -} - -template iss::status riscv_hart_msu_vp::write_epc(unsigned addr, reg_t val) { - csr[addr] = val & get_pc_mask(); + val &= this->csr[sideleg]; return iss::Ok; } @@ -934,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; } @@ -945,44 +535,10 @@ 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; } -template iss::status riscv_hart_msu_vp::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; -} - -template iss::status riscv_hart_msu_vp::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; -} - template iss::status riscv_hart_msu_vp::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) { switch(paddr.val) { default: { @@ -1000,14 +556,16 @@ template iss::status riscv_hart_msu_vp::write_mem(phys_add // 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 == tohost) { + 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() + ((tohost + 4) & mem.page_addr_mask)) >> 24 - : (cur_data >> 56) & 0xFF; + 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() + ((tohost + 4) & mem.page_addr_mask)) >> 16 - : (cur_data >> 48) & 0xFF; + 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) { @@ -1023,7 +581,7 @@ template iss::status riscv_hart_msu_vp::write_mem(phys_add CPPLOG(ERR) << "Syscall read went wrong"; uint64_t syscall_num = loaded_payload.at(0); if(syscall_num == 64) { // SYS_WRITE - return execute_sys_write(this, loaded_payload, traits::MEM); + 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"; @@ -1038,9 +596,9 @@ template iss::status riscv_hart_msu_vp::write_mem(phys_add return iss::Ok; } } - if((traits::XLEN == 32 && paddr.val == fromhost + 4) || (traits::XLEN == 64 && paddr.val == fromhost)) { - uint64_t fhostvar = *reinterpret_cast(p.data() + (fromhost & mem.page_addr_mask)); - *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; + 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; @@ -1048,26 +606,26 @@ 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() { - vm[1] = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); - BASE::addr_mode[3] = BASE::addr_mode[2] = vm[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; + 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) - vm[0] = hart_state_type::decode_vm_info(state.mstatus.MPP, state.satp); + this->vm[0] = decode_vm_info(state.mstatus.MPP, satp); else - vm[0] = vm[1]; - BASE::addr_mode[1] = BASE::addr_mode[0] = vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; + 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; ptw.clear(); } template void riscv_hart_msu_vp::check_interrupt() { auto status = state.mstatus; - auto ip = csr[mip]; - auto ie = csr[mie]; - auto ideleg = csr[mideleg]; + auto ip = this->csr[mip]; + auto ie = this->csr[mie]; + auto ideleg = this->csr[mideleg]; // Multiple simultaneous interrupts and traps at the same privilege level are // handled in the following decreasing priority order: // external interrupts, software interrupts, timer interrupts, then finally @@ -1144,7 +702,7 @@ template typename riscv_hart_msu_vp::phys_addr_t riscv_har break; } else if(!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { break; - } else if(type == (iss::access_type::FETCH ? !(pte & PTE_X) + } 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; @@ -1197,10 +755,10 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f // calculate effective privilege level auto new_priv = PRIV_M; if(trap_id == 0) { // exception - if(cur_priv != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0) - new_priv = (csr[sedeleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; + if(cur_priv != PRIV_M && ((this->csr[medeleg] >> cause) & 0x1) != 0) + new_priv = (this->csr[sedeleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; // store ret addr in xepc register - csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception + this->csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception /* * write mtval if new_priv=M_MODE, spec says: * When a hardware breakpoint is triggered, or an instruction-fetch, load, @@ -1210,17 +768,17 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f */ switch(cause) { case 0: - csr[utval | (new_priv << 8)] = static_cast(addr); + this->csr[utval | (new_priv << 8)] = static_cast(addr); break; case 2: - csr[utval | (new_priv << 8)] = (instr & 0x3) == 3 ? instr : instr & 0xffff; + this->csr[utval | (new_priv << 8)] = (instr & 0x3) == 3 ? instr : instr & 0xffff; break; case 3: // TODO: implement debug mode behavior // csr[dpc] = addr; // csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi) - csr[utval | (new_priv << 8)] = addr; - if(semihosting_cb) { + this->csr[utval | (new_priv << 8)] = addr; + if(this->semihosting_cb) { // Check for semihosting call phys_addr_t p_addr(access_type::DEBUG_READ, traits::MEM, addr - 4); std::array data; @@ -1241,7 +799,7 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f #endif CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; - semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/); + this->semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); return this->reg.NEXT_PC; } } @@ -1249,20 +807,20 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f case 4: case 6: case 7: - csr[utval | (new_priv << 8)] = fault_data; + this->csr[utval | (new_priv << 8)] = this->fault_data; break; default: - csr[utval | (new_priv << 8)] = 0; + this->csr[utval | (new_priv << 8)] = 0; } - fault_data = 0; + this->fault_data = 0; } else { - if(cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0) - new_priv = (csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; - csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt + if(cur_priv != PRIV_M && ((this->csr[mideleg] >> cause) & 0x1) != 0) + new_priv = (this->csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; + this->csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt this->reg.pending_trap = 0; } size_t adr = ucause | (new_priv << 8); - csr[adr] = (trap_id << (traits::XLEN - 1)) + cause; + this->csr[adr] = (trap_id << (traits::XLEN - 1)) + cause; // update mstatus // xPP field of mstatus is written with the active privilege mode at the time // of the trap; the x PIE field of mstatus @@ -1290,7 +848,7 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f } // get trap vector - auto ivec = csr[utvec | (new_priv << 8)]; + auto ivec = this->csr[utvec | (new_priv << 8)]; // calculate addr// set NEXT_PC to trap addressess to jump to based on MODE // bits in mtvec this->reg.NEXT_PC = ivec & ~0x3UL; @@ -1299,10 +857,10 @@ template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t f std::array buffer; sprintf(buffer.data(), "0x%016lx", addr); if((flags & 0xffffffff) != 0xffffffff) - CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" - << cause << ")" - << " at address " << buffer.data() << " occurred, changing privilege level from " << lvl[cur_priv] << " to " - << lvl[new_priv]; + CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" + << (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")" + << " at address " << buffer.data() << " occurred, changing privilege level from " << this->lvl[cur_priv] + << " to " << this->lvl[new_priv]; // reset trap state this->reg.PRIV = new_priv; this->reg.trap_state = 0; @@ -1344,8 +902,8 @@ template uint64_t riscv_hart_msu_vp::leave_trap(uint64_t f break; } // sets the pc to the value stored in the x epc register. - this->reg.NEXT_PC = csr[uepc | inst_priv << 8]; - CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[this->reg.PRIV]; + this->reg.NEXT_PC = this->csr[uepc | inst_priv << 8]; + CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to " << this->lvl[this->reg.PRIV]; update_vm_info(); check_interrupt(); return this->reg.NEXT_PC; diff --git a/src/iss/arch/riscv_hart_mu_p.h b/src/iss/arch/riscv_hart_mu_p.h index e70e27f..f5e8527 100644 --- a/src/iss/arch/riscv_hart_mu_p.h +++ b/src/iss/arch/riscv_hart_mu_p.h @@ -35,9 +35,6 @@ #ifndef _RISCV_HART_MU_P_H #define _RISCV_HART_MU_P_H -#include "iss/arch/traits.h" -#include "iss/instrumentation_if.h" -#include "iss/log_categories.h" #include "iss/vm_if.h" #include "iss/vm_types.h" #include "riscv_hart_common.h" @@ -45,129 +42,36 @@ #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 -#include - -#include namespace iss { namespace arch { template -class riscv_hart_mu_p : public BASE, public riscv_hart_common { -protected: - const std::array lvl = {{'U', 'S', 'H', 'M'}}; - const std::array 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 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"}}; - +class riscv_hart_mu_p : public riscv_hart_common { public: using core = BASE; + using base = riscv_hart_common; using this_class = riscv_hart_mu_p; 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 = iss::status (this_class::*)(unsigned addr, reg_t&); - using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t); - 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); - - constexpr static unsigned MEM = traits::MEM; - - // 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 @@ -187,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 @@ -272,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 = {{ @@ -287,136 +137,32 @@ public: return m[mode]; } - constexpr bool has_compressed() { return traits::MISA_VAL & 0b0100; } - constexpr reg_t get_pc_mask() { return has_compressed() ? ~1 : ~3; } - riscv_hart_mu_p(feature_config cfg = feature_config{}); virtual ~riscv_hart_mu_p() = default; void reset(uint64_t address) override; - std::pair load_file(std::string name, int type = -1) override; - iss::status read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - uint8_t* const data) override; + uint8_t* const data); iss::status write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, - const uint8_t* const data) override; + const uint8_t* const data); - uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, fault_data, fault_data); } + uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, this->fault_data, this->fault_data); } uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override; uint64_t leave_trap(uint64_t flags) override; - const reg_t& get_mhartid() const { return mhartid_reg; } - void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; - - 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); - }; - - iss::instrumentation_if* get_instrumentation_if() override { return &instr_if; } - - void set_csr(unsigned addr, reg_t val) { csr[addr & csr.page_addr_mask] = val; } - - void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - - void set_semihosting_callback(semihosting_cb_t cb) { semihosting_cb = cb; }; + void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; } protected: - struct riscv_instrumentation_if : public iss::instrumentation_if { + 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); - riscv_instrumentation_if(riscv_hart_mu_p& 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::core_type; } + hart_state state; - 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::NUM_REGS; } - - unsigned get_reg_size(unsigned num) override { return traits::reg_bit_widths[num]; } - - std::unordered_map const& get_symbol_table(std::string name) override { return arch.symbol_table; } - - riscv_hart_mu_p& arch; - }; - - friend struct riscv_instrumentation_if; - - 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); - - virtual iss::status read_csr(unsigned addr, reg_t& val); - virtual iss::status write_csr(unsigned addr, reg_t val); - - hart_state_type state; - int64_t cycle_offset{0}; - uint64_t mcycle_csr{0}; - int64_t instret_offset{0}; - uint64_t minstret_csr{0}; - reg_t fault_data; - riscv_instrumentation_if instr_if; - - semihosting_cb_t semihosting_cb; - - using mem_type = util::sparse_array; - using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; - using csr_page_type = typename csr_type::page_type; - mem_type mem; - csr_type csr; std::unordered_map ptw; std::unordered_map atomic_reservation; - std::unordered_map csr_rd_cb; - std::unordered_map csr_wr_cb; - 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_plain(unsigned addr, reg_t& val); - iss::status write_plain(unsigned addr, reg_t val); - iss::status read_null(unsigned addr, reg_t& val); - iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; } - iss::status read_cycle(unsigned addr, reg_t& val); - iss::status write_cycle(unsigned addr, reg_t val); - iss::status read_instret(unsigned addr, reg_t& val); - iss::status write_instret(unsigned addr, reg_t val); - iss::status read_tvec(unsigned addr, reg_t& val); - iss::status read_time(unsigned addr, reg_t& val); iss::status read_status(unsigned addr, reg_t& val); iss::status write_status(unsigned addr, reg_t val); iss::status read_cause(unsigned addr, reg_t& val); @@ -424,92 +170,25 @@ 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 write_ideleg(unsigned addr, reg_t val); - iss::status write_edeleg(unsigned addr, reg_t val); - iss::status read_hartid(unsigned addr, reg_t& val); - iss::status write_epc(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); iss::status write_dscratch(unsigned addr, reg_t val); iss::status read_dpc(unsigned addr, reg_t& val); iss::status write_dpc(unsigned addr, reg_t val); - iss::status read_fcsr(unsigned addr, reg_t& val); - iss::status write_fcsr(unsigned addr, reg_t val); - iss::status write_pmpcfg(unsigned addr, reg_t val); - - virtual iss::status read_custom_csr(unsigned addr, reg_t& val) { return iss::status::Err; }; - virtual iss::status write_custom_csr(unsigned addr, reg_t val) { return iss::status::Err; }; - - void register_custom_csr_rd(unsigned addr) { csr_rd_cb[addr] = &this_class::read_custom_csr_reg; } - void register_custom_csr_wr(unsigned addr) { csr_wr_cb[addr] = &this_class::write_custom_csr_reg; } - - reg_t mhartid_reg{0x0}; + iss::status write_ideleg(unsigned addr, reg_t val); + iss::status write_edeleg(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}; - inline bool debug_mode_active() { return this->reg.PRIV & 0x4; } - - 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() -, instr_if(*this) -, cfg(cfg) { - // reset values - csr[misa] = traits::MISA_VAL; - csr[mvendorid] = 0x669; - csr[marchid] = traits::MARCHID_VAL; - csr[mimpid] = 1; - - if(traits::FLEN > 0) { - csr_rd_cb[fcsr] = &this_class::read_fcsr; - csr_wr_cb[fcsr] = &this_class::write_fcsr; - } - for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - if(traits::XLEN == 32) - for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - csr_wr_cb[addr] = &this_class::write_plain; - } - for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - } - if(traits::XLEN == 32) - for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) { - csr_rd_cb[addr] = &this_class::read_null; - } - // common regs - const std::array roaddrs{{misa, mvendorid, marchid, mimpid}}; - for(auto addr : roaddrs) { - csr_rd_cb[addr] = &this_class::read_plain; - csr_wr_cb[addr] = &this_class::write_null; - } +, cfg(cfg) +, default_mem(base::get_priv_if()) { const std::array rwaddrs{{ mepc, mtvec, @@ -521,246 +200,56 @@ riscv_hart_mu_p::riscv_hart_mu_p(feature_config cfg) utval, }}; for(auto addr : rwaddrs) { - csr_rd_cb[addr] = &this_class::read_plain; - csr_wr_cb[addr] = &this_class::write_plain; + this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain); + this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); } - // special handling & overrides - csr_rd_cb[time] = &this_class::read_time; - if(traits::XLEN == 32) - csr_rd_cb[timeh] = &this_class::read_time; - csr_rd_cb[cycle] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_rd_cb[cycleh] = &this_class::read_cycle; - csr_rd_cb[instret] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_rd_cb[instreth] = &this_class::read_instret; + this->csr_rd_cb[mstatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[mstatus] = MK_CSR_WR_CB(write_status); + this->csr_rd_cb[mcause] = MK_CSR_RD_CB(read_cause); + this->csr_wr_cb[mcause] = MK_CSR_WR_CB(write_cause); + this->csr_rd_cb[mtvec] = MK_CSR_RD_CB(read_tvec); + this->csr_wr_cb[mepc] = MK_CSR_WR_CB(write_epc); + this->csr_rd_cb[mip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[mip] = MK_CSR_WR_CB(write_null); + this->csr_rd_cb[mie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[mie] = MK_CSR_WR_CB(write_ie); + this->csr_rd_cb[mcounteren] = MK_CSR_RD_CB(read_null); + this->csr_wr_cb[mcounteren] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[misa] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mvendorid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null); - csr_rd_cb[mcycle] = &this_class::read_cycle; - csr_wr_cb[mcycle] = &this_class::write_cycle; - if(traits::XLEN == 32) - csr_rd_cb[mcycleh] = &this_class::read_cycle; - if(traits::XLEN == 32) - csr_wr_cb[mcycleh] = &this_class::write_cycle; - csr_rd_cb[minstret] = &this_class::read_instret; - csr_wr_cb[minstret] = &this_class::write_instret; - if(traits::XLEN == 32) - csr_rd_cb[minstreth] = &this_class::read_instret; - if(traits::XLEN == 32) - csr_wr_cb[minstreth] = &this_class::write_instret; - csr_rd_cb[mstatus] = &this_class::read_status; - csr_wr_cb[mstatus] = &this_class::write_status; - csr_rd_cb[mcause] = &this_class::read_cause; - csr_wr_cb[mcause] = &this_class::write_cause; - csr_rd_cb[mtvec] = &this_class::read_tvec; - csr_wr_cb[mepc] = &this_class::write_epc; - csr_rd_cb[mip] = &this_class::read_ip; - csr_wr_cb[mip] = &this_class::write_null; - csr_rd_cb[mie] = &this_class::read_ie; - csr_wr_cb[mie] = &this_class::write_ie; - csr_rd_cb[mhartid] = &this_class::read_hartid; - csr_rd_cb[mcounteren] = &this_class::read_null; - csr_wr_cb[mcounteren] = &this_class::write_null; - csr_wr_cb[misa] = &this_class::write_null; - csr_wr_cb[mvendorid] = &this_class::write_null; - csr_wr_cb[marchid] = &this_class::write_null; - csr_wr_cb[mimpid] = &this_class::write_null; - - if(FEAT & FEAT_PMP) { - for(size_t i = pmpaddr0; i <= pmpaddr15; ++i) { - csr_rd_cb[i] = &this_class::read_plain; - csr_wr_cb[i] = &this_class::write_plain; - } - for(size_t i = pmpcfg0; i < pmpcfg0 + 16 / sizeof(reg_t); ++i) { - csr_rd_cb[i] = &this_class::read_plain; - csr_wr_cb[i] = &this_class::write_pmpcfg; - } - } if(FEAT & FEAT_EXT_N) { - csr_rd_cb[mideleg] = &this_class::read_plain; - csr_wr_cb[mideleg] = &this_class::write_ideleg; - csr_rd_cb[medeleg] = &this_class::read_plain; - csr_wr_cb[medeleg] = &this_class::write_edeleg; - csr_rd_cb[uie] = &this_class::read_ie; - csr_wr_cb[uie] = &this_class::write_ie; - csr_rd_cb[uip] = &this_class::read_ip; - csr_wr_cb[uip] = &this_class::write_null; - csr_wr_cb[uepc] = &this_class::write_epc; - csr_rd_cb[ustatus] = &this_class::read_status; - csr_wr_cb[ustatus] = &this_class::write_status; - csr_rd_cb[ucause] = &this_class::read_cause; - csr_wr_cb[ucause] = &this_class::write_cause; - csr_rd_cb[utvec] = &this_class::read_tvec; - } - if(FEAT & FEAT_CLIC) { - csr_rd_cb[mtvt] = &this_class::read_plain; - csr_wr_cb[mtvt] = &this_class::write_xtvt; - // csr_rd_cb[mxnti] = &this_class::read_csr_reg; - // csr_wr_cb[mxnti] = &this_class::write_csr_reg; - csr_rd_cb[mintstatus] = &this_class::read_intstatus; - csr_wr_cb[mintstatus] = &this_class::write_null; - // csr_rd_cb[mscratchcsw] = &this_class::read_csr_reg; - // csr_wr_cb[mscratchcsw] = &this_class::write_csr_reg; - // csr_rd_cb[mscratchcswl] = &this_class::read_csr_reg; - // csr_wr_cb[mscratchcswl] = &this_class::write_csr_reg; - csr_rd_cb[mintthresh] = &this_class::read_plain; - csr_wr_cb[mintthresh] = &this_class::write_intthresh; - if(FEAT & FEAT_EXT_N) { - csr_rd_cb[utvt] = &this_class::read_plain; - csr_wr_cb[utvt] = &this_class::write_xtvt; - csr_rd_cb[uintstatus] = &this_class::read_intstatus; - csr_wr_cb[uintstatus] = &this_class::write_null; - csr_rd_cb[uintthresh] = &this_class::read_plain; - csr_wr_cb[uintthresh] = &this_class::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; - csr[mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; - 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); + this->csr_rd_cb[mideleg] = MK_CSR_RD_CB(read_plain); + this->csr_wr_cb[mideleg] = MK_CSR_WR_CB(write_ideleg); + this->csr_rd_cb[medeleg] = MK_CSR_RD_CB(read_plain); + this->csr_wr_cb[medeleg] = MK_CSR_WR_CB(write_edeleg); + this->csr_rd_cb[uie] = MK_CSR_RD_CB(read_ie); + this->csr_wr_cb[uie] = MK_CSR_WR_CB(write_ie); + this->csr_rd_cb[uip] = MK_CSR_RD_CB(read_ip); + this->csr_wr_cb[uip] = MK_CSR_WR_CB(write_null); + this->csr_wr_cb[uepc] = MK_CSR_WR_CB(write_epc); + this->csr_rd_cb[ustatus] = MK_CSR_RD_CB(read_status); + this->csr_wr_cb[ustatus] = MK_CSR_WR_CB(write_status); + this->csr_rd_cb[ucause] = MK_CSR_RD_CB(read_cause); + 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_DEBUG) { - csr_wr_cb[dscratch0] = &this_class::write_dscratch; - csr_rd_cb[dscratch0] = &this_class::read_debug; - csr_wr_cb[dscratch1] = &this_class::write_dscratch; - csr_rd_cb[dscratch1] = &this_class::read_debug; - csr_wr_cb[dpc] = &this_class::write_dpc; - csr_rd_cb[dpc] = &this_class::read_dpc; - csr_wr_cb[dcsr] = &this_class::write_dcsr; - csr_rd_cb[dcsr] = &this_class::read_debug; + this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch); + this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug); + this->csr_wr_cb[dscratch1] = MK_CSR_WR_CB(write_dscratch); + this->csr_rd_cb[dscratch1] = MK_CSR_RD_CB(read_debug); + this->csr_wr_cb[dpc] = MK_CSR_WR_CB(write_dpc); + this->csr_rd_cb[dpc] = MK_CSR_RD_CB(read_dpc); + 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 -std::pair riscv_hart_mu_p::load_file(std::string name, int type) { - if(read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64, - [this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status { - return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits::MEM, addr, size, - data); - })) { - return std::make_pair(entry_address, true); - } - return std::make_pair(entry_address, false); -} - -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) { - 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 = csr[pmpaddr0 + i] << PMP_SHIFT; - uint8_t cfg = 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 = (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 @@ -778,18 +267,9 @@ 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)) { - 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) ? (has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); + auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min(length, sizeof(reg_t)); if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) { - fault_data = addr; + this->fault_data = addr; if(is_debug(access)) throw trap_access(0, addr); this->reg.trap_state = (1UL << 31); // issue trap 0 @@ -798,33 +278,19 @@ iss::status riscv_hart_mu_p::read(const address_type type, c try { if(!is_debug(access) && (addr & (alignment - 1))) { this->reg.trap_state = (1UL << 31) | 4 << 16; - fault_data = addr; + 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 - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -832,11 +298,9 @@ iss::status riscv_hart_mu_p::read(const address_type type, c case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return read_csr(addr, *reinterpret_cast(data)); + 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: { @@ -854,7 +318,7 @@ iss::status riscv_hart_mu_p::read(const address_type type, c } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } @@ -883,23 +347,14 @@ iss::status riscv_hart_mu_p::write(const address_type type, << std::hex << addr; break; default: - CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; + CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr 0x" << std::hex << addr; } #endif try { switch(space) { case traits::MEM: { - if(FEAT & FEAT_PMP) { - if(!pmp_check(access, addr, length) && (access & access_type::DEBUG) != access_type::DEBUG) { - 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)) { - fault_data = addr; + this->fault_data = addr; if(access && iss::access_type::DEBUG) throw trap_access(0, addr); this->reg.trap_state = (1UL << 31); // issue trap 0 @@ -909,43 +364,27 @@ iss::status riscv_hart_mu_p::write(const address_type type, auto alignment = std::min(length, sizeof(reg_t)); if(length > 1 && (addr & (alignment - 1)) && !is_debug(access)) { this->reg.trap_state = (1UL << 31) | 6 << 16; - fault_data = addr; + 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) - fault_data = addr; + this->fault_data = addr; } return res; } catch(trap_access& ta) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; return iss::Err; } } break; case traits::CSR: { if(length != sizeof(reg_t)) return iss::Err; - return write_csr(addr, *reinterpret_cast(data)); + 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: { @@ -965,205 +404,51 @@ iss::status riscv_hart_mu_p::write(const address_type type, } catch(trap_access& ta) { if((access & access_type::DEBUG) == 0) { this->reg.trap_state = (1UL << 31) | ta.id; - fault_data = ta.addr; + this->fault_data = ta.addr; } return iss::Err; } } -template -iss::status riscv_hart_mu_p::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 (this->*(it->second))(addr, val); -} - -template -iss::status riscv_hart_mu_p::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 (this->*(it->second))(addr, val); -} - -template -iss::status riscv_hart_mu_p::read_null(unsigned addr, reg_t& val) { - val = 0; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_plain(unsigned addr, reg_t& val) { - val = csr[addr]; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_plain(unsigned addr, reg_t val) { - csr[addr] = val; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_cycle(unsigned addr, reg_t& val) { - auto cycle_val = this->reg.cycle + cycle_offset; - if(addr == mcycle) { - val = static_cast(cycle_val); - } else if(addr == mcycleh) { - val = static_cast(cycle_val >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_cycle(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - mcycle_csr = static_cast(val); - } else { - if(addr == mcycle) { - mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val; - } else { - mcycle_csr = (static_cast(val) << 32) + (mcycle_csr & 0xffffffff); - } - } - cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_instret(unsigned addr, reg_t& val) { - if((addr & 0xff) == (minstret & 0xff)) { - val = static_cast(this->reg.instret); - } else if((addr & 0xff) == (minstreth & 0xff)) { - val = static_cast(this->reg.instret >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_instret(unsigned addr, reg_t val) { - if(sizeof(typename traits::reg_t) != 4) { - this->reg.instret = static_cast(val); - } else { - if((addr & 0xff) == (minstret & 0xff)) { - this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val; - } else { - this->reg.instret = (static_cast(val) << 32) + (this->reg.instret & 0xffffffff); - } - } - this->reg.instret--; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_time(unsigned addr, reg_t& val) { - uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052; - if(addr == time) { - val = static_cast(time_val); - } else if(addr == timeh) { - if(sizeof(typename traits::reg_t) != 4) - return iss::Err; - val = static_cast(time_val >> 32); - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_tvec(unsigned addr, reg_t& val) { - val = FEAT & features_e::FEAT_CLIC ? csr[addr] : csr[addr] & ~2; - return iss::Ok; -} - 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) && (csr[mtvec] & 0x3) == 3) { - val = 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 = 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) && (csr[mtvec] & 0x3) == 3) { - auto mask = ((1UL << (traits::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16)); - csr[addr] = (val & mask) | (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)); - csr[addr] = (val & mask) | (csr[addr] & ~mask); - } - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::read_hartid(unsigned addr, reg_t& val) { - val = mhartid_reg; + auto mask = ((1UL << (traits::XLEN - 1)) | (this->mcause_max_irq - 1)); + this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask); return iss::Ok; } template iss::status riscv_hart_mu_p::read_ie(unsigned addr, reg_t& val) { auto mask = get_irq_mask((addr >> 8) & 0x3); - val = csr[mie] & mask; + val = this->csr[mie] & mask; if(this->reg.PRIV != 3) - val &= csr[mideleg]; + val &= this->csr[mideleg]; return iss::Ok; } template iss::status riscv_hart_mu_p::write_ie(unsigned addr, reg_t val) { auto mask = get_irq_mask((addr >> 8) & 0x3); - csr[mie] = (csr[mie] & ~mask) | (val & mask); + this->csr[mie] = (this->csr[mie] & ~mask) | (val & mask); check_interrupt(); return iss::Ok; } @@ -1171,222 +456,39 @@ iss::status riscv_hart_mu_p::write_ie(unsigned addr, reg_t v template iss::status riscv_hart_mu_p::read_ip(unsigned addr, reg_t& val) { auto mask = get_irq_mask((addr >> 8) & 0x3); - val = csr[mip] & mask; + val = this->csr[mip] & mask; if(this->reg.PRIV != 3) - val &= csr[mideleg]; + val &= this->csr[mideleg]; return iss::Ok; } template iss::status riscv_hart_mu_p::write_ideleg(unsigned addr, reg_t val) { auto mask = 0b000100010001; // only U mode supported - csr[mideleg] = (csr[mideleg] & ~mask) | (val & mask); + this->csr[mideleg] = (this->csr[mideleg] & ~mask) | (val & mask); return iss::Ok; } template iss::status riscv_hart_mu_p::write_edeleg(unsigned addr, reg_t val) { auto mask = 0b1011001111110111; // bit 14/10 (reserved), bit 11 (Env call), and 3 (break) are hardwired to 0 - csr[medeleg] = (csr[medeleg] & ~mask) | (val & mask); - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_epc(unsigned addr, reg_t val) { - csr[addr] = val & get_pc_mask(); - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::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; -} - -template -iss::status riscv_hart_mu_p::read_debug(unsigned addr, reg_t& val) { - if(!debug_mode_active()) - throw illegal_instruction_fault(this->fault_data); - val = csr[addr]; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_dscratch(unsigned addr, reg_t val) { - if(!debug_mode_active()) - throw illegal_instruction_fault(this->fault_data); - csr[addr] = val; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::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; -} - -template -iss::status riscv_hart_mu_p::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; -} - -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::read_fcsr(unsigned addr, reg_t& val) { - val = this->get_fcsr(); - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_fcsr(unsigned addr, reg_t val) { - this->set_fcsr(val); - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_intthresh(unsigned addr, reg_t val) { - csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; - return iss::Ok; -} - -template -iss::status riscv_hart_mu_p::write_xtvt(unsigned addr, reg_t val) { - csr[addr] = val & ~0x3fULL; - 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 == tohost) { - reg_t cur_data = *reinterpret_cast(data); - // Extract Device (bits 63:56) - uint8_t device = traits::XLEN == 32 ? *reinterpret_cast(p.data() + ((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() + ((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 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 == fromhost + 4) || (traits::XLEN == 64 && paddr.val == fromhost)) { - uint64_t fhostvar = *reinterpret_cast(p.data() + (fromhost & mem.page_addr_mask)); - *reinterpret_cast(p.data() + (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 - } + this->csr[medeleg] = (this->csr[medeleg] & ~mask) | (val & mask); 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() { // TODO: Implement CLIC functionality - auto ideleg = csr[mideleg]; + auto ideleg = this->csr[mideleg]; // Multiple simultaneous interrupts and traps at the same privilege level are // handled in the following decreasing priority order: // external interrupts, software interrupts, timer interrupts, then finally // any synchronous traps. - auto ena_irq = csr[mip] & csr[mie]; + auto ena_irq = this->csr[mip] & this->csr[mie]; bool mstatus_mie = state.mstatus.MIE; auto m_enabled = this->reg.PRIV < PRIV_M || mstatus_mie; @@ -1415,10 +517,10 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ // calculate effective privilege level unsigned new_priv = PRIV_M; if(trap_id == 0) { // exception - if(this->reg.PRIV != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0) + if(this->reg.PRIV != PRIV_M && ((this->csr[medeleg] >> cause) & 0x1) != 0) new_priv = PRIV_U; // store ret addr in xepc register - csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception + this->csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception /* * write mtval if new_priv=M_MODE, spec says: * When a hardware breakpoint is triggered, or an instruction-fetch, load, @@ -1428,27 +530,27 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ */ switch(cause) { case 0: - csr[utval | (new_priv << 8)] = static_cast(addr); + this->csr[utval | (new_priv << 8)] = static_cast(addr); break; case 2: - csr[utval | (new_priv << 8)] = (!has_compressed() || (instr & 0x3) == 3) ? instr : instr & 0xffff; + this->csr[utval | (new_priv << 8)] = (!this->has_compressed() || (instr & 0x3) == 3) ? instr : instr & 0xffff; break; case 3: - if((FEAT & FEAT_DEBUG) && (csr[dcsr] & 0x8000)) { + if((FEAT & FEAT_DEBUG) && (this->csr[dcsr] & 0x8000)) { this->reg.DPC = addr; - csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1 << 6) | PRIV_M; // FIXME: cause should not be 4 (stepi) + this->csr[dcsr] = (this->csr[dcsr] & ~0x1c3) | (1 << 6) | PRIV_M; // FIXME: cause should not be 4 (stepi) new_priv = this->reg.PRIV | PRIV_D; } else { - csr[utval | (new_priv << 8)] = addr; + this->csr[utval | (new_priv << 8)] = addr; } - if(semihosting_cb) { + if(this->semihosting_cb) { // Check for semihosting call phys_addr_t p_addr(access_type::DEBUG_READ, traits::MEM, addr - 4); std::array data; // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 - this->read_mem(p_addr, 4, data.data()); - p_addr.val += 8; - this->read_mem(p_addr, 4, data.data() + 4); + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data()); + addr += 8; + this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4); const std::array ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; if(data == ref_data) { @@ -1462,7 +564,7 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ #endif CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; - semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); + this->semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); return this->reg.NEXT_PC; } } @@ -1470,20 +572,20 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ case 4: case 6: case 7: - csr[utval | (new_priv << 8)] = fault_data; + this->csr[utval | (new_priv << 8)] = this->fault_data; break; default: - csr[utval | (new_priv << 8)] = 0; + this->csr[utval | (new_priv << 8)] = 0; } - fault_data = 0; + this->fault_data = 0; } else { - if(this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0) + if(this->reg.PRIV != PRIV_M && ((this->csr[mideleg] >> cause) & 0x1) != 0) new_priv = PRIV_U; - csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt + this->csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt this->reg.pending_trap = 0; } size_t adr = ucause | (new_priv << 8); - csr[adr] = (trap_id << (traits::XLEN - 1)) + cause; + this->csr[adr] = (trap_id << (traits::XLEN - 1)) + cause; // update mstatus // xPP field of mstatus is written with the active privilege mode at the time // of the trap; the x PIE field of mstatus @@ -1506,12 +608,12 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ } // get trap vector - auto xtvec = csr[utvec | (new_priv << 8)]; + 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, csr[mtvt], sizeof(reg_t), reinterpret_cast(&data)); + auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast(&data)); if(ret == iss::Err) return this->reg.PC; this->reg.NEXT_PC = data; @@ -1527,10 +629,10 @@ uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_ sprintf(buffer.data(), "0x%016lx", addr); #endif if((flags & 0xffffffff) != 0xffffffff) - CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" - << cause << ")" - << " at address " << buffer.data() << " occurred, changing privilege level from " << lvl[this->reg.PRIV] - << " to " << lvl[new_priv]; + CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" + << (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")" + << " at address " << buffer.data() << " occurred, changing privilege level from " << this->lvl[this->reg.PRIV] + << " to " << this->lvl[new_priv]; // reset trap state this->reg.PRIV = new_priv; this->reg.trap_state = 0; @@ -1562,8 +664,9 @@ template uint64_t riscv_hart_m break; } // sets the pc to the value stored in the x epc register. - this->reg.NEXT_PC = csr[uepc | inst_priv << 8]; - CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[this->reg.PRIV]; + this->reg.NEXT_PC = this->csr[uepc | inst_priv << 8]; + CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to " + << this->lvl[this->reg.PRIV]; check_interrupt(); } return this->reg.NEXT_PC; 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/mmio/memory_if.cpp b/src/iss/mmio/memory_if.cpp new file mode 100644 index 0000000..03c0b70 --- /dev/null +++ b/src/iss/mmio/memory_if.cpp @@ -0,0 +1,26 @@ +#include "memory_if.h" + +namespace iss { +namespace mmio { +void memory_hierarchy::prepend(memory_elem& e) { + hierarchy.push_front(e); + update_chain(); +} +void memory_hierarchy::append(memory_elem& e) { + hierarchy.push_back(e); + update_chain(); +} +void memory_hierarchy::insert_before(memory_elem&) {} +void memory_hierarchy::insert_after(memory_elem&) {} +void memory_hierarchy::replace_last(memory_elem&) {} +void memory_hierarchy::update_chain() { + bool tail = false; + 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 mmio +} // namespace iss \ No newline at end of file diff --git a/src/iss/mmio/memory_if.h b/src/iss/mmio/memory_if.h new file mode 100644 index 0000000..70adac2 --- /dev/null +++ b/src/iss/mmio/memory_if.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (C) 2025 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial implementation + ******************************************************************************/ + +#ifndef _MEMORY_MEMORY_IF_ +#define _MEMORY_MEMORY_IF_ + +#include "iss/vm_types.h" +#include +#include +#include +#include + +namespace iss { +namespace 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*); + +struct memory_if { + util::delegate rd_mem; + util::delegate wr_mem; +}; + +struct memory_elem { + virtual memory_if get_mem_if() = 0; + virtual void set_next(memory_if) = 0; + virtual void register_csrs() {} + virtual std::tuple get_range() { return {0, std::numeric_limits::max()}; } +}; + +struct memory_hierarchy { + void prepend(memory_elem&); + void append(memory_elem&); + void insert_before(memory_elem&); + void insert_after(memory_elem&); + void replace_last(memory_elem&); + +protected: + void update_chain(); + std::deque> hierarchy; +}; + +} // namespace mmio +} // namespace iss +#endif \ No newline at end of file diff --git a/src/iss/mmio/memory_with_htif.h b/src/iss/mmio/memory_with_htif.h new file mode 100644 index 0000000..f9c3ebe --- /dev/null +++ b/src/iss/mmio/memory_with_htif.h @@ -0,0 +1,62 @@ +#ifndef _MEMORY_WITH_HTIF_ +#define _MEMORY_WITH_HTIF_ + +#include "iss/arch/riscv_hart_common.h" +#include "iss/vm_types.h" +#include "memory_if.h" +#include +#include + +namespace iss { +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; + + memory_with_htif(arch::priv_if hart_if) + : hart_if(hart_if) {} + + ~memory_with_htif() = default; + + memory_if get_mem_if() override { + return memory_if{.rd_mem{util::delegate::from(this)}, + .wr_mem{util::delegate::from(this)}}; + } + + void set_next(memory_if) override { + // intenrionally left empty, leaf element + } + +private: + iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { + for(auto offs = 0U; offs < length; ++offs) { + *(data + offs) = mem[(addr + offs) % mem.size()]; + } + return iss::Ok; + } + + iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { + mem_type::page_type& p = mem(addr / mem.page_size); + std::copy(data, data + length, p.data() + (addr & mem.page_addr_mask)); + // this->tohost handling in case of riscv-test + // according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754: + if(access && iss::access_type::FUNC) { + if(addr == hart_if.tohost) { + return hart_if.exec_htif(data); + } + if((WORD_LEN == 32 && addr == hart_if.fromhost + 4) || (WORD_LEN == 64 && addr == hart_if.fromhost)) { + uint64_t fhostvar = *reinterpret_cast(p.data() + (hart_if.fromhost & mem.page_addr_mask)); + *reinterpret_cast(p.data() + (hart_if.tohost & mem.page_addr_mask)) = fhostvar; + } + } + return iss::Ok; + } + +protected: + using mem_type = util::sparse_array; + mem_type mem; + arch::priv_if hart_if; +}; +} // namespace 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 diff --git a/src/vm/interp/vm_tgc5c.cpp b/src/vm/interp/vm_tgc5c.cpp index 3284e85..8f2b515 100644 --- a/src/vm/interp/vm_tgc5c.cpp +++ b/src/vm/interp/vm_tgc5c.cpp @@ -263,7 +263,7 @@ private: return iss::Err; // } } else { - if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok) + if (this->core.read(iss::address_type::PHYSICAL, pc.access, pc.space, pc.val, 4, data) != iss::Ok) return iss::Err; } @@ -2695,11 +2695,12 @@ std::unique_ptr create(arch::tgc5c *core, unsigned short por } // namespace iss #include +#include #include #include namespace iss { namespace { -volatile std::array dummy = { +volatile std::array dummy = { core_factory::instance().register_creator("tgc5c|m_p|interp", [](unsigned port, void* init_data) -> std::tuple{ auto* cpu = new iss::arch::riscv_hart_m_p(); auto vm = new interp::tgc5c::vm_impl(*cpu, false); @@ -2719,6 +2720,16 @@ volatile std::array 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{ + auto* cpu = new iss::arch::riscv_hart_msu_vp(); + auto vm = new interp::tgc5c::vm_impl(*cpu, false); + if (port != 0) debugger::server::run_server(vm, port); + if(init_data){ + auto* cb = reinterpret_cast::reg_t>*>(init_data); + cpu->set_semihosting_callback(*cb); + } + return {cpu_ptr{cpu}, vm_ptr{vm}}; }) }; }