Compare commits
	
		
			13 Commits
		
	
	
		
			feature/ht
			...
			54233b448d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 54233b448d | |||
| e238369e18 | |||
| cfc980a069 | |||
| 502f3e8df9 | |||
| 88475bfa55 | |||
| 71260a3ef4 | |||
| 23842742a6 | |||
| a13b7ac6d3 | |||
| fb0f6255e9 | |||
| 57d5ea92be | |||
| 383d762abc | |||
| 03cbd305c6 | |||
| 9f5326c110 | 
| @@ -18,6 +18,7 @@ add_subdirectory(softfloat) | ||||
| set(LIB_SOURCES | ||||
|     src/iss/plugin/instruction_count.cpp | ||||
|     src/iss/arch/tgc5c.cpp | ||||
|     src/iss/mem/memory_if.cpp | ||||
|     src/vm/interp/vm_tgc5c.cpp | ||||
|     src/vm/fp_functions.cpp | ||||
|     src/iss/debugger/csr_names.cpp | ||||
|   | ||||
							
								
								
									
										233
									
								
								src/iss/arch/mstatus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/iss/arch/mstatus.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,233 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2025 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||
|  *    may be used to endorse or promote products derived from this software | ||||
|  *    without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
|  * POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  * Contributors: | ||||
|  *       eyck@minres.com - initial implementation | ||||
|  ******************************************************************************/ | ||||
| #ifndef _MSTATUS_TYPE | ||||
| #define _MSTATUS_TYPE | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <type_traits> | ||||
| #include <util/bit_field.h> | ||||
| #include <util/ities.h> | ||||
|  | ||||
| namespace iss { | ||||
| namespace arch { | ||||
|  | ||||
| template <class T, class Enable = void> struct status {}; | ||||
| // specialization 32bit | ||||
| template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> { | ||||
|     static inline unsigned SD(T v) { return bit_sub<63, 1>(v); } | ||||
|     // value of XLEN for S-mode | ||||
|     static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); }; | ||||
|     // value of XLEN for U-mode | ||||
|     static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); }; | ||||
|     // Trap SRET | ||||
|     static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); }; | ||||
|     // Timeout Wait | ||||
|     static inline unsigned TW(T v) { return bit_sub<21, 1>(v); }; | ||||
|     // Trap Virtual Memory | ||||
|     static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); }; | ||||
|     // Make eXecutable Readable | ||||
|     static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); }; | ||||
|     // permit Supervisor User Memory access | ||||
|     static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); }; | ||||
|     // Modify PRiVilege | ||||
|     static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); }; | ||||
|     // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None | ||||
|     // dirty, some clean/Some dirty | ||||
|     static inline unsigned XS(T v) { return bit_sub<15, 2>(v); }; | ||||
|     // floating-point unit status Off/Initial/Clean/Dirty | ||||
|     static inline unsigned FS(T v) { return bit_sub<13, 2>(v); }; | ||||
|     // machine previous privilege | ||||
|     static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); }; | ||||
|     // supervisor previous privilege | ||||
|     static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); }; | ||||
|     // previous machine interrupt-enable | ||||
|     static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); }; | ||||
|     // previous supervisor interrupt-enable | ||||
|     static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); }; | ||||
|     // previous user interrupt-enable | ||||
|     static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); }; | ||||
|     // machine interrupt-enable | ||||
|     static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); }; | ||||
|     // supervisor interrupt-enable | ||||
|     static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); }; | ||||
|     // user interrupt-enable | ||||
|     static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); }; | ||||
| }; | ||||
|  | ||||
| template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> { | ||||
| public: | ||||
|     // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR | ||||
|     // XS==11))) | ||||
|     static inline unsigned SD(T v) { return bit_sub<63, 1>(v); }; | ||||
|     // value of XLEN for S-mode | ||||
|     static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); }; | ||||
|     // value of XLEN for U-mode | ||||
|     static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); }; | ||||
|     // Trap SRET | ||||
|     static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); }; | ||||
|     // Timeout Wait | ||||
|     static inline unsigned TW(T v) { return bit_sub<21, 1>(v); }; | ||||
|     // Trap Virtual Memory | ||||
|     static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); }; | ||||
|     // Make eXecutable Readable | ||||
|     static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); }; | ||||
|     // permit Supervisor User Memory access | ||||
|     static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); }; | ||||
|     // Modify PRiVilege | ||||
|     static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); }; | ||||
|     // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None | ||||
|     // dirty, some clean/Some dirty | ||||
|     static inline unsigned XS(T v) { return bit_sub<15, 2>(v); }; | ||||
|     // floating-point unit status Off/Initial/Clean/Dirty | ||||
|     static inline unsigned FS(T v) { return bit_sub<13, 2>(v); }; | ||||
|     // machine previous privilege | ||||
|     static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); }; | ||||
|     // supervisor previous privilege | ||||
|     static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); }; | ||||
|     // previous machine interrupt-enable | ||||
|     static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); }; | ||||
|     // previous supervisor interrupt-enable | ||||
|     static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); }; | ||||
|     // previous user interrupt-enable | ||||
|     static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); }; | ||||
|     // machine interrupt-enable | ||||
|     static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); }; | ||||
|     // supervisor interrupt-enable | ||||
|     static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); }; | ||||
|     // user interrupt-enable | ||||
|     static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); }; | ||||
| }; | ||||
|  | ||||
| // primary template | ||||
| template <class T, class Enable = void> struct hart_state {}; | ||||
| // specialization 32bit | ||||
| template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> { | ||||
| public: | ||||
|     BEGIN_BF_DECL(mstatus_t, T); | ||||
|     // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR | ||||
|     // XS==11))) | ||||
|     BF_FIELD(SD, 31, 1); | ||||
|     // Trap SRET | ||||
|     BF_FIELD(TSR, 22, 1); | ||||
|     // Timeout Wait | ||||
|     BF_FIELD(TW, 21, 1); | ||||
|     // Trap Virtual Memory | ||||
|     BF_FIELD(TVM, 20, 1); | ||||
|     // Make eXecutable Readable | ||||
|     BF_FIELD(MXR, 19, 1); | ||||
|     // permit Supervisor User Memory access | ||||
|     BF_FIELD(SUM, 18, 1); | ||||
|     // Modify PRiVilege | ||||
|     BF_FIELD(MPRV, 17, 1); | ||||
|     // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None | ||||
|     // dirty, some clean/Some dirty | ||||
|     BF_FIELD(XS, 15, 2); | ||||
|     // floating-point unit status Off/Initial/Clean/Dirty | ||||
|     BF_FIELD(FS, 13, 2); | ||||
|     // machine previous privilege | ||||
|     BF_FIELD(MPP, 11, 2); | ||||
|     // supervisor previous privilege | ||||
|     BF_FIELD(SPP, 8, 1); | ||||
|     // previous machine interrupt-enable | ||||
|     BF_FIELD(MPIE, 7, 1); | ||||
|     // previous supervisor interrupt-enable | ||||
|     BF_FIELD(SPIE, 5, 1); | ||||
|     // previous user interrupt-enable | ||||
|     BF_FIELD(UPIE, 4, 1); | ||||
|     // machine interrupt-enable | ||||
|     BF_FIELD(MIE, 3, 1); | ||||
|     // supervisor interrupt-enable | ||||
|     BF_FIELD(SIE, 1, 1); | ||||
|     // user interrupt-enable | ||||
|     BF_FIELD(UIE, 0, 1); | ||||
|     END_BF_DECL(); | ||||
|  | ||||
|     mstatus_t mstatus; | ||||
|  | ||||
|     static const T mstatus_reset_val = 0x1800; | ||||
| }; | ||||
|  | ||||
| // specialization 64bit | ||||
| template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> { | ||||
| public: | ||||
|     BEGIN_BF_DECL(mstatus_t, T); | ||||
|     // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR | ||||
|     // XS==11))) | ||||
|     BF_FIELD(SD, 63, 1); | ||||
|     // value of XLEN for S-mode | ||||
|     BF_FIELD(SXL, 34, 2); | ||||
|     // value of XLEN for U-mode | ||||
|     BF_FIELD(UXL, 32, 2); | ||||
|     // Trap SRET | ||||
|     BF_FIELD(TSR, 22, 1); | ||||
|     // Timeout Wait | ||||
|     BF_FIELD(TW, 21, 1); | ||||
|     // Trap Virtual Memory | ||||
|     BF_FIELD(TVM, 20, 1); | ||||
|     // Make eXecutable Readable | ||||
|     BF_FIELD(MXR, 19, 1); | ||||
|     // permit Supervisor User Memory access | ||||
|     BF_FIELD(SUM, 18, 1); | ||||
|     // Modify PRiVilege | ||||
|     BF_FIELD(MPRV, 17, 1); | ||||
|     // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None | ||||
|     // dirty, some clean/Some dirty | ||||
|     BF_FIELD(XS, 15, 2); | ||||
|     // floating-point unit status Off/Initial/Clean/Dirty | ||||
|     BF_FIELD(FS, 13, 2); | ||||
|     // machine previous privilege | ||||
|     BF_FIELD(MPP, 11, 2); | ||||
|     // supervisor previous privilege | ||||
|     BF_FIELD(SPP, 8, 1); | ||||
|     // previous machine interrupt-enable | ||||
|     BF_FIELD(MPIE, 7, 1); | ||||
|     // previous supervisor interrupt-enable | ||||
|     BF_FIELD(SPIE, 5, 1); | ||||
|     // previous user interrupt-enable | ||||
|     BF_FIELD(UPIE, 4, 1); | ||||
|     // machine interrupt-enable | ||||
|     BF_FIELD(MIE, 3, 1); | ||||
|     // supervisor interrupt-enable | ||||
|     BF_FIELD(SIE, 1, 1); | ||||
|     // user interrupt-enable | ||||
|     BF_FIELD(UIE, 0, 1); | ||||
|     END_BF_DECL(); | ||||
|  | ||||
|     mstatus_t mstatus; | ||||
|  | ||||
|     static const T mstatus_reset_val = 0x1800; | ||||
| }; | ||||
| } // namespace arch | ||||
| } // namespace iss | ||||
| #endif // _MSTATUS_TYPE | ||||
| @@ -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/vm_types.h" | ||||
| #include "mstatus.h" | ||||
| #include "util/delegate.h" | ||||
| #include <array> | ||||
| #include <cstdint> | ||||
| #include <elfio/elfio.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include <iss/arch_if.h> | ||||
| #include <iss/log_categories.h> | ||||
| #include <iss/semihosting/semihosting.h> | ||||
| #include <limits> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <util/logging.h> | ||||
| #include <util/sparse_array.h> | ||||
| #include "../mem/memory_if.h" | ||||
|  | ||||
| #if defined(__GNUC__) | ||||
| #define likely(x) ::__builtin_expect(!!(x), 1) | ||||
| @@ -59,7 +66,7 @@ | ||||
| namespace iss { | ||||
| namespace arch { | ||||
|  | ||||
| enum features_e { FEAT_NONE, FEAT_PMP = 1, FEAT_EXT_N = 2, FEAT_CLIC = 4, FEAT_DEBUG = 8, FEAT_TCM = 16 }; | ||||
| enum features_e { FEAT_NONE, FEAT_EXT_N = 1, FEAT_DEBUG = 2 }; | ||||
|  | ||||
| enum riscv_csr { | ||||
|     /* user-level CSR */ | ||||
| @@ -191,23 +198,6 @@ enum riscv_csr { | ||||
|     dscratch1 = 0x7B3 | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     PGSHIFT = 12, | ||||
|     PTE_PPN_SHIFT = 10, | ||||
|     // page table entry (PTE) fields | ||||
|     PTE_V = 0x001,   // Valid | ||||
|     PTE_R = 0x002,   // Read | ||||
|     PTE_W = 0x004,   // Write | ||||
|     PTE_X = 0x008,   // Execute | ||||
|     PTE_U = 0x010,   // User | ||||
|     PTE_G = 0x020,   // Global | ||||
|     PTE_A = 0x040,   // Accessed | ||||
|     PTE_D = 0x080,   // Dirty | ||||
|     PTE_SOFT = 0x300 // Reserved for Software | ||||
| }; | ||||
|  | ||||
| template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); } | ||||
|  | ||||
| enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3, PRIV_D = 4 }; | ||||
|  | ||||
| enum { | ||||
| @@ -226,25 +216,6 @@ enum { | ||||
|     ISA_U = 1 << 20 | ||||
| }; | ||||
|  | ||||
| struct vm_info { | ||||
|     int levels; | ||||
|     int idxbits; | ||||
|     int ptesize; | ||||
|     uint64_t ptbase; | ||||
|     bool is_active() { return levels; } | ||||
| }; | ||||
|  | ||||
| struct feature_config { | ||||
|     uint64_t clic_base{0xc0000000}; | ||||
|     unsigned clic_int_ctl_bits{4}; | ||||
|     unsigned clic_num_irq{16}; | ||||
|     unsigned clic_num_trigger{0}; | ||||
|     uint64_t tcm_base{0x10000000}; | ||||
|     uint64_t tcm_size{0x8000}; | ||||
|     uint64_t io_address{0xf0000000}; | ||||
|     uint64_t io_addr_mask{0xf0000000}; | ||||
| }; | ||||
|  | ||||
| class trap_load_access_fault : public trap_access { | ||||
| public: | ||||
|     trap_load_access_fault(uint64_t badaddr) | ||||
| @@ -271,62 +242,148 @@ public: | ||||
|     : trap_access(15 << 16, badaddr) {} | ||||
| }; | ||||
|  | ||||
| inline void read_reg_uint32(uint64_t offs, uint32_t& reg, uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs & 0x3) { | ||||
|     case 0: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 1 + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 2 + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *data = *(reg_ptr + 3); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| template <typename WORD_TYPE> struct priv_if { | ||||
|     using rd_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE&)>; | ||||
|     using wr_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE)>; | ||||
|  | ||||
| inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     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<iss::status(unsigned, WORD_TYPE&)> read_csr; | ||||
|     std::function<iss::status(unsigned, WORD_TYPE)> write_csr; | ||||
|     std::function<iss::status(uint8_t const*)> exec_htif; | ||||
|     std::function<void(uint16_t,uint16_t,WORD_TYPE)> raise_trap; // trap_id, cause, fault_data | ||||
|     std::unordered_map<unsigned, rd_csr_f>& csr_rd_cb; | ||||
|     std::unordered_map<unsigned, wr_csr_f>& csr_wr_cb; | ||||
|     hart_state<WORD_TYPE>& state; | ||||
|     uint8_t& PRIV; | ||||
|     WORD_TYPE& PC; | ||||
|     uint64_t& tohost; | ||||
|     uint64_t& fromhost; | ||||
|     unsigned& max_irq; | ||||
| }; | ||||
|  | ||||
| template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public mem::memory_elem { | ||||
|     const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}}; | ||||
|     const std::array<const char*, 16> trap_str = {{"" | ||||
|                                                    "Instruction address misaligned", // 0 | ||||
|                                                    "Instruction access fault",       // 1 | ||||
|                                                    "Illegal instruction",            // 2 | ||||
|                                                    "Breakpoint",                     // 3 | ||||
|                                                    "Load address misaligned",        // 4 | ||||
|                                                    "Load access fault",              // 5 | ||||
|                                                    "Store/AMO address misaligned",   // 6 | ||||
|                                                    "Store/AMO access fault",         // 7 | ||||
|                                                    "Environment call from U-mode",   // 8 | ||||
|                                                    "Environment call from S-mode",   // 9 | ||||
|                                                    "Reserved",                       // a | ||||
|                                                    "Environment call from M-mode",   // b | ||||
|                                                    "Instruction page fault",         // c | ||||
|                                                    "Load page fault",                // d | ||||
|                                                    "Reserved",                       // e | ||||
|                                                    "Store/AMO page fault"}}; | ||||
|     const std::array<const char*, 12> irq_str = {{"User software interrupt", "Supervisor software interrupt", "Reserved", | ||||
|                                                   "Machine software interrupt", "User timer interrupt", "Supervisor timer interrupt", | ||||
|                                                   "Reserved", "Machine timer interrupt", "User external interrupt", | ||||
|                                                   "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; | ||||
|     constexpr static unsigned MEM = traits<BASE>::MEM; | ||||
|  | ||||
|     using core = BASE; | ||||
|     using this_class = riscv_hart_common<BASE, LOGCAT>; | ||||
|     using phys_addr_t = typename core::phys_addr_t; | ||||
|     using reg_t = typename core::reg_t; | ||||
|     using addr_t = typename core::addr_t; | ||||
|  | ||||
|     using rd_csr_f = std::function<iss::status(unsigned addr, reg_t&)>; | ||||
|     using wr_csr_f = std::function<iss::status(unsigned addr, reg_t)>; | ||||
|  | ||||
| #define MK_CSR_RD_CB(FCT) [this](unsigned a, reg_t& r) -> iss::status { return this->FCT(a, r); }; | ||||
| #define MK_CSR_WR_CB(FCT) [this](unsigned a, reg_t r) -> iss::status { return this->FCT(a, r); }; | ||||
|  | ||||
|     riscv_hart_common() | ||||
|     : state() | ||||
|     , instr_if(*this) { | ||||
|         // reset values | ||||
|         csr[misa] = traits<BASE>::MISA_VAL; | ||||
|         csr[mvendorid] = 0x669; | ||||
|         csr[marchid] = traits<BASE>::MARCHID_VAL; | ||||
|         csr[mimpid] = 1; | ||||
|  | ||||
|         if(traits<BASE>::FLEN > 0) { | ||||
|             csr_rd_cb[fcsr] = MK_CSR_RD_CB(read_fcsr); | ||||
|             csr_wr_cb[fcsr] = MK_CSR_WR_CB(write_fcsr); | ||||
|             csr_rd_cb[fflags] = MK_CSR_RD_CB(read_fcsr); | ||||
|             csr_wr_cb[fflags] = MK_CSR_WR_CB(write_fcsr); | ||||
|             csr_rd_cb[frm] = MK_CSR_RD_CB(read_fcsr); | ||||
|             csr_wr_cb[frm] = MK_CSR_WR_CB(write_fcsr); | ||||
|         } | ||||
|         for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) { | ||||
|             csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); | ||||
|             csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); | ||||
|         } | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) { | ||||
|                 csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); | ||||
|                 csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); | ||||
|             } | ||||
|         for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) { | ||||
|             csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); | ||||
|             csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain); | ||||
|         } | ||||
|         for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) { | ||||
|             csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); | ||||
|         } | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) { | ||||
|                 csr_rd_cb[addr] = MK_CSR_RD_CB(read_null); | ||||
|             } | ||||
|         // common regs | ||||
|         const std::array<unsigned, 4> roaddrs{{misa, mvendorid, marchid, mimpid}}; | ||||
|         for(auto addr : roaddrs) { | ||||
|             csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain); | ||||
|             csr_wr_cb[addr] = MK_CSR_WR_CB(write_null); | ||||
|         } | ||||
|         // special handling & overrides | ||||
|         csr_rd_cb[time] = MK_CSR_RD_CB(read_time); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_rd_cb[timeh] = MK_CSR_RD_CB(read_time); | ||||
|         csr_rd_cb[cycle] = MK_CSR_RD_CB(read_cycle); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_rd_cb[cycleh] = MK_CSR_RD_CB(read_cycle); | ||||
|         csr_rd_cb[instret] = MK_CSR_RD_CB(read_instret); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_rd_cb[instreth] = MK_CSR_RD_CB(read_instret); | ||||
|  | ||||
|         csr_rd_cb[mcycle] = MK_CSR_RD_CB(read_cycle); | ||||
|         csr_wr_cb[mcycle] = MK_CSR_WR_CB(write_cycle); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_rd_cb[mcycleh] = MK_CSR_RD_CB(read_cycle); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_wr_cb[mcycleh] = MK_CSR_WR_CB(write_cycle); | ||||
|         csr_rd_cb[minstret] = MK_CSR_RD_CB(read_instret); | ||||
|         csr_wr_cb[minstret] = MK_CSR_WR_CB(write_instret); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_rd_cb[minstreth] = MK_CSR_RD_CB(read_instret); | ||||
|         if(traits<BASE>::XLEN == 32) | ||||
|             csr_wr_cb[minstreth] = MK_CSR_WR_CB(write_instret); | ||||
|         csr_rd_cb[mhartid] = MK_CSR_RD_CB(read_hartid); | ||||
|     }; | ||||
|     ~riscv_hart_common() { | ||||
|         if(io_buf.str().length()) { | ||||
|             CPPLOG(INFO) << "tohost send '" << io_buf.str() << "'"; | ||||
|         } | ||||
|     }; | ||||
|     } | ||||
|  | ||||
|     std::unordered_map<std::string, uint64_t> symbol_table; | ||||
|     uint64_t entry_address{0}; | ||||
|     uint64_t tohost = std::numeric_limits<uint64_t>::max(); | ||||
|     uint64_t fromhost = std::numeric_limits<uint64_t>::max(); | ||||
|     std::stringstream io_buf; | ||||
|  | ||||
|     bool read_elf_file(std::string name, uint8_t expected_elf_class, | ||||
|                        std::function<iss::status(uint64_t, uint64_t, const uint8_t* const)> cb) { | ||||
|     void set_semihosting_callback(semihosting_cb_t<reg_t> cb) { semihosting_cb = cb; }; | ||||
|  | ||||
|     std::pair<uint64_t, bool> load_file(std::string name, int type) { | ||||
|         return std::make_pair(entry_address, read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64)); | ||||
|     } | ||||
|  | ||||
|     bool read_elf_file(std::string name, uint8_t expected_elf_class) { | ||||
|         // Create elfio reader | ||||
|         ELFIO::elfio reader; | ||||
|         // Load ELF data | ||||
| @@ -343,8 +400,9 @@ struct riscv_hart_common { | ||||
|                 const auto fsize = pseg->get_file_size(); // 0x42c/0x0 | ||||
|                 const auto seg_data = pseg->get_data(); | ||||
|                 const auto type = pseg->get_type(); | ||||
|                 if(type == 1 && fsize > 0) { | ||||
|                     auto res = cb(pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data)); | ||||
|                 if(type == ELFIO::PT_LOAD && fsize > 0) { | ||||
|                     auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM, | ||||
|                                            pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data)); | ||||
|                     if(res != iss::Ok) | ||||
|                         CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                 } | ||||
| @@ -382,6 +440,7 @@ struct riscv_hart_common { | ||||
|         } | ||||
|         return false; | ||||
|     }; | ||||
|  | ||||
|     iss::status execute_sys_write(arch_if* aif, const std::array<uint64_t, 8>& loaded_payload, unsigned mem_type) { | ||||
|         uint64_t fd = loaded_payload[1]; | ||||
|         uint64_t buf_ptr = loaded_payload[2]; | ||||
| @@ -409,6 +468,363 @@ struct riscv_hart_common { | ||||
|             } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     constexpr bool has_compressed() { return traits<BASE>::MISA_VAL & 0b0100; } | ||||
|  | ||||
|     constexpr reg_t get_pc_mask() { return has_compressed() ? (reg_t)~1 : (reg_t)~3; } | ||||
|  | ||||
|     void disass_output(uint64_t pc, const std::string instr) override { | ||||
|         // NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x}    {:40} [p:{};s:0x{:x};c:{}]", pc, instr, lvl[this->reg.PRIV], | ||||
|         // (reg_t)state.mstatus, | ||||
|         //                                     this->reg.cycle + cycle_offset); | ||||
|         NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x}    {:40} [p:{};c:{}]", pc, instr, lvl[this->reg.PRIV], | ||||
|                                             this->reg.cycle + cycle_offset); | ||||
|     }; | ||||
|  | ||||
|     void register_csr(unsigned addr, rd_csr_f f) { csr_rd_cb[addr] = f; } | ||||
|     void register_csr(unsigned addr, wr_csr_f f) { csr_wr_cb[addr] = f; } | ||||
|     void register_csr(unsigned addr, rd_csr_f rdf, wr_csr_f wrf) { | ||||
|         csr_rd_cb[addr] = rdf; | ||||
|         csr_wr_cb[addr] = wrf; | ||||
|     } | ||||
|     void unregister_csr_rd(unsigned addr) { csr_rd_cb.erase(addr); } | ||||
|     void unregister_csr_wr(unsigned addr) { csr_wr_cb.erase(addr); } | ||||
|  | ||||
|     bool debug_mode_active() { return this->reg.PRIV & 0x4; } | ||||
|  | ||||
|     const reg_t& get_mhartid() const { return mhartid_reg; } | ||||
|     void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; | ||||
|  | ||||
|     iss::status read_csr(unsigned addr, reg_t& val) { | ||||
|         if(addr >= csr.size()) | ||||
|             return iss::Err; | ||||
|         auto req_priv_lvl = (addr >> 8) & 0x3; | ||||
|         if(this->reg.PRIV < req_priv_lvl) // not having required privileges | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         auto it = csr_rd_cb.find(addr); | ||||
|         if(it == csr_rd_cb.end() || !it->second) // non existent register | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         return it->second(addr, val); | ||||
|     } | ||||
|  | ||||
|     iss::status write_csr(unsigned addr, reg_t val) { | ||||
|         if(addr >= csr.size()) | ||||
|             return iss::Err; | ||||
|         auto req_priv_lvl = (addr >> 8) & 0x3; | ||||
|         if(this->reg.PRIV < req_priv_lvl) // not having required privileges | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         if((addr & 0xc00) == 0xc00) // writing to read-only region | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         auto it = csr_wr_cb.find(addr); | ||||
|         if(it == csr_wr_cb.end() || !it->second) // non existent register | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         return it->second(addr, val); | ||||
|     } | ||||
|  | ||||
|     iss::status read_null(unsigned addr, reg_t& val) { | ||||
|         val = 0; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; } | ||||
|  | ||||
|     iss::status read_plain(unsigned addr, reg_t& val) { | ||||
|         val = csr[addr]; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_plain(unsigned addr, reg_t val) { | ||||
|         csr[addr] = val; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_cycle(unsigned addr, reg_t& val) { | ||||
|         auto cycle_val = this->reg.cycle + cycle_offset; | ||||
|         if(addr == mcycle) { | ||||
|             val = static_cast<reg_t>(cycle_val); | ||||
|         } else if(addr == mcycleh) { | ||||
|             val = static_cast<reg_t>(cycle_val >> 32); | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_cycle(unsigned addr, reg_t val) { | ||||
|         if(sizeof(typename traits<BASE>::reg_t) != 4) { | ||||
|             mcycle_csr = static_cast<uint64_t>(val); | ||||
|         } else { | ||||
|             if(addr == mcycle) { | ||||
|                 mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val; | ||||
|             } else { | ||||
|                 mcycle_csr = (static_cast<uint64_t>(val) << 32) + (mcycle_csr & 0xffffffff); | ||||
|             } | ||||
|         } | ||||
|         cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_instret(unsigned addr, reg_t& val) { | ||||
|         if((addr & 0xff) == (minstret & 0xff)) { | ||||
|             val = static_cast<reg_t>(this->reg.instret); | ||||
|         } else if((addr & 0xff) == (minstreth & 0xff)) { | ||||
|             val = static_cast<reg_t>(this->reg.instret >> 32); | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_instret(unsigned addr, reg_t val) { | ||||
|         if(sizeof(typename traits<BASE>::reg_t) != 4) { | ||||
|             this->reg.instret = static_cast<uint64_t>(val); | ||||
|         } else { | ||||
|             if((addr & 0xff) == (minstret & 0xff)) { | ||||
|                 this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val; | ||||
|             } else { | ||||
|                 this->reg.instret = (static_cast<uint64_t>(val) << 32) + (this->reg.instret & 0xffffffff); | ||||
|             } | ||||
|         } | ||||
|         this->reg.instret--; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_time(unsigned addr, reg_t& val) { | ||||
|         uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052; | ||||
|         if(addr == time) { | ||||
|             val = static_cast<reg_t>(time_val); | ||||
|         } else if(addr == timeh) { | ||||
|             if(sizeof(typename traits<BASE>::reg_t) != 4) | ||||
|                 return iss::Err; | ||||
|             val = static_cast<reg_t>(time_val >> 32); | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_tvec(unsigned addr, reg_t& val) { | ||||
|         val = csr[addr] & ~2; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_hartid(unsigned addr, reg_t& val) { | ||||
|         val = mhartid_reg; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_epc(unsigned addr, reg_t val) { | ||||
|         csr[addr] = val & get_pc_mask(); | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_dcsr(unsigned addr, reg_t val) { | ||||
|         if(!debug_mode_active()) | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         //                  +-------------- ebreakm | ||||
|         //                  |   +---------- stepi | ||||
|         //                  |   |  +++----- cause | ||||
|         //                  |   |  |||   +- step | ||||
|         csr[addr] = val & 0b1000100111000100U; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_debug(unsigned addr, reg_t& val) { | ||||
|         if(!debug_mode_active()) | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         val = csr[addr]; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_dscratch(unsigned addr, reg_t val) { | ||||
|         if(!debug_mode_active()) | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         csr[addr] = val; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_dpc(unsigned addr, reg_t& val) { | ||||
|         if(!debug_mode_active()) | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         val = this->reg.DPC; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_dpc(unsigned addr, reg_t val) { | ||||
|         if(!debug_mode_active()) | ||||
|             throw illegal_instruction_fault(this->fault_data); | ||||
|         this->reg.DPC = val; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_fcsr(unsigned addr, reg_t& val) { | ||||
|         switch(addr) { | ||||
|         case 1: // fflags, 4:0 | ||||
|             val = bit_sub<0, 5>(this->get_fcsr()); | ||||
|             break; | ||||
|         case 2: // frm, 7:5 | ||||
|             val = bit_sub<5, 3>(this->get_fcsr()); | ||||
|             break; | ||||
|         case 3: // fcsr | ||||
|             val = this->get_fcsr(); | ||||
|             break; | ||||
|         default: | ||||
|             return iss::Err; | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_fcsr(unsigned addr, reg_t val) { | ||||
|         switch(addr) { | ||||
|         case 1: // fflags, 4:0 | ||||
|             this->set_fcsr((this->get_fcsr() & 0xffffffe0) | (val & 0x1f)); | ||||
|             break; | ||||
|         case 2: // frm, 7:5 | ||||
|             this->set_fcsr((this->get_fcsr() & 0xffffff1f) | ((val & 0x7) << 5)); | ||||
|             break; | ||||
|         case 3: // fcsr | ||||
|             this->set_fcsr(val & 0xff); | ||||
|             break; | ||||
|         default: | ||||
|             return iss::Err; | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     priv_if<reg_t> get_priv_if() { | ||||
|         return priv_if<reg_t>{.read_csr = [this](unsigned addr, reg_t& val) -> iss::status { return read_csr(addr, val); }, | ||||
|                               .write_csr = [this](unsigned addr, reg_t val) -> iss::status { return write_csr(addr, val); }, | ||||
|                               .exec_htif = [this](uint8_t const* data) -> iss::status { return execute_htif(data); }, | ||||
|                               .raise_trap = [this](uint16_t trap_id, uint16_t cause, reg_t fault_data){ | ||||
|                                   this->reg.trap_state = 0x80ULL << 24 | (cause << 16) | trap_id; | ||||
|                                   this->fault_data = fault_data; | ||||
|  | ||||
|                               }, | ||||
|                               .csr_rd_cb{this->csr_rd_cb}, | ||||
|                               .csr_wr_cb{csr_wr_cb}, | ||||
|                               .state{this->state}, | ||||
|                               .PRIV{this->reg.PRIV}, | ||||
|                               .PC{this->reg.PC}, | ||||
|                               .tohost{this->tohost}, | ||||
|                               .fromhost{this->fromhost}, | ||||
|                               .max_irq{mcause_max_irq}}; | ||||
|     } | ||||
|  | ||||
|     iss::status execute_htif(uint8_t const* data) { | ||||
|         reg_t cur_data = *reinterpret_cast<const reg_t*>(data); | ||||
|         // Extract Device (bits 63:56) | ||||
|         uint8_t device = traits<BASE>::XLEN == 32 ? 0 : (cur_data >> 56) & 0xFF; | ||||
|         // Extract Command (bits 55:48) | ||||
|         uint8_t command = traits<BASE>::XLEN == 32 ? 0 : (cur_data >> 48) & 0xFF; | ||||
|         // Extract payload (bits 47:0) | ||||
|         uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; | ||||
|         if(payload_addr & 1) { | ||||
|             CPPLOG(FATAL) << "this->tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr | ||||
|                           << "), stopping simulation"; | ||||
|             this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|             this->interrupt_sim = payload_addr; | ||||
|             return iss::Ok; | ||||
|         } else if(device == 0 && command == 0) { | ||||
|             std::array<uint64_t, 8> loaded_payload; | ||||
|             if(memory.rd_mem(access_type::DEBUG_READ, payload_addr, 8 * sizeof(uint64_t), | ||||
|                              reinterpret_cast<uint8_t*>(loaded_payload.data())) == iss::Err) | ||||
|                 CPPLOG(ERR) << "Syscall read went wrong"; | ||||
|             uint64_t syscall_num = loaded_payload.at(0); | ||||
|             if(syscall_num == 64) { // SYS_WRITE | ||||
|                 return this->execute_sys_write(this, loaded_payload, traits<BASE>::MEM); | ||||
|             } else { | ||||
|                 CPPLOG(ERR) << "this->tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num | ||||
|                             << ") not implemented"; | ||||
|                 this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|                 this->interrupt_sim = payload_addr; | ||||
|                 return iss::Ok; | ||||
|             } | ||||
|         } else { | ||||
|             CPPLOG(ERR) << "this->tohost functionality not implemented for device " << device << " and command " << command; | ||||
|             this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|             this->interrupt_sim = payload_addr; | ||||
|             return iss::Ok; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     mem::memory_hierarchy memories; | ||||
|  | ||||
|     virtual mem::memory_if get_mem_if() override { | ||||
|         assert(false || "This function should nevver be called"); | ||||
|         return mem::memory_if{}; | ||||
|     } | ||||
|  | ||||
|     virtual void set_next(mem::memory_if mem_if) { memory = mem_if; }; | ||||
|  | ||||
|     void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } | ||||
|  | ||||
| protected: | ||||
|     hart_state<reg_t> state; | ||||
|  | ||||
|     static constexpr reg_t get_mstatus_mask_t(unsigned priv_lvl = PRIV_M) { | ||||
|         if(sizeof(reg_t) == 4) { | ||||
|             return priv_lvl == PRIV_U ? 0x80000011UL :   // 0b1...0 0001 0001 | ||||
|                        priv_lvl == PRIV_S ? 0x800de133UL // 0b0...0 0001 1000 1001 1001; | ||||
|                                           : 0x807ff9ddUL; | ||||
|         } else { | ||||
|             return priv_lvl == PRIV_U ? 0x011ULL : // 0b1...0 0001 0001 | ||||
|                        priv_lvl == PRIV_S ? 0x000de133ULL | ||||
|                                           : 0x007ff9ddULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     mem::memory_if memory; | ||||
|     struct riscv_instrumentation_if : public iss::instrumentation_if { | ||||
|  | ||||
|         riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch) | ||||
|         : arch(arch) {} | ||||
|         /** | ||||
|          * get the name of this architecture | ||||
|          * | ||||
|          * @return the name of this architecture | ||||
|          */ | ||||
|         const std::string core_type_name() const override { return traits<BASE>::core_type; } | ||||
|  | ||||
|         uint64_t get_pc() override { return arch.reg.PC; } | ||||
|  | ||||
|         uint64_t get_next_pc() override { return arch.reg.NEXT_PC; } | ||||
|  | ||||
|         uint64_t get_instr_word() override { return arch.reg.instruction; } | ||||
|  | ||||
|         uint64_t get_instr_count() override { return arch.reg.icount; } | ||||
|  | ||||
|         uint64_t get_pendig_traps() override { return arch.reg.trap_state; } | ||||
|  | ||||
|         uint64_t get_total_cycles() override { return arch.reg.cycle + arch.cycle_offset; } | ||||
|  | ||||
|         void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; } | ||||
|  | ||||
|         bool is_branch_taken() override { return arch.reg.last_branch; } | ||||
|  | ||||
|         unsigned get_reg_num() override { return traits<BASE>::NUM_REGS; } | ||||
|  | ||||
|         unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; } | ||||
|  | ||||
|         std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; } | ||||
|  | ||||
|         riscv_hart_common<BASE, LOGCAT>& arch; | ||||
|     }; | ||||
|  | ||||
|     friend struct riscv_instrumentation_if; | ||||
|     riscv_instrumentation_if instr_if; | ||||
|  | ||||
|     instrumentation_if* get_instrumentation_if() override { return &instr_if; }; | ||||
|  | ||||
|     using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>; | ||||
|     using csr_page_type = typename csr_type::page_type; | ||||
|     csr_type csr; | ||||
|  | ||||
|     std::unordered_map<unsigned, rd_csr_f> csr_rd_cb; | ||||
|     std::unordered_map<unsigned, wr_csr_f> csr_wr_cb; | ||||
|  | ||||
|     reg_t mhartid_reg{0x0}; | ||||
|     uint64_t mcycle_csr{0}; | ||||
|     uint64_t minstret_csr{0}; | ||||
|     reg_t fault_data; | ||||
|  | ||||
|     int64_t cycle_offset{0}; | ||||
|     int64_t instret_offset{0}; | ||||
|     semihosting_cb_t<reg_t> semihosting_cb; | ||||
|     unsigned mcause_max_irq{16U}; | ||||
| }; | ||||
|  | ||||
| } // namespace arch | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -244,7 +244,6 @@ struct tgc5c: public arch_if { | ||||
|         uint32_t last_branch = 0; | ||||
|     } reg; | ||||
| #pragma pack(pop) | ||||
|     std::array<address_type, 4> addr_mode; | ||||
|      | ||||
|     uint64_t interrupt_sim=0; | ||||
|  | ||||
|   | ||||
| @@ -23,35 +23,30 @@ using tgc5c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arc | ||||
| #ifdef CORE_TGC5D | ||||
| #include "riscv_hart_mu_p.h" | ||||
| #include <iss/arch/tgc5d.h> | ||||
| using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | | ||||
|                                                                                              iss::arch::FEAT_EXT_N)>; | ||||
| using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>; | ||||
| #endif | ||||
| #ifdef CORE_TGC5D_XRB_MAC | ||||
| #include "riscv_hart_mu_p.h" | ||||
| #include <iss/arch/tgc5d_xrb_mac.h> | ||||
| using tgc5d_xrb_mac_plat_type = | ||||
|     iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac, | ||||
|                                (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>; | ||||
|     iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>; | ||||
| #endif | ||||
| #ifdef CORE_TGC5D_XRB_NN | ||||
| #include "hwl.h" | ||||
| #include "riscv_hart_mu_p.h" | ||||
| #include <iss/arch/tgc5d_xrb_nn.h> | ||||
| using tgc5d_xrb_nn_plat_type = | ||||
|     iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn, | ||||
|                                               (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>; | ||||
|     iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>>; | ||||
| #endif | ||||
| #ifdef CORE_TGC5E | ||||
| #include "riscv_hart_mu_p.h" | ||||
| #include <iss/arch/tgc5e.h> | ||||
| using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | | ||||
|                                                                                              iss::arch::FEAT_EXT_N)>; | ||||
| using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>; | ||||
| #endif | ||||
| #ifdef CORE_TGC5X | ||||
| #include "riscv_hart_mu_p.h" | ||||
| #include <iss/arch/tgc5x.h> | ||||
| using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | | ||||
|                                                                                              iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>; | ||||
| using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>; | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										252
									
								
								src/iss/mem/clic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								src/iss/mem/clic.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
|  | ||||
| #include "iss/arch/riscv_hart_common.h" | ||||
| #include "iss/vm_types.h" | ||||
| #include <util/logging.h> | ||||
| #include "../mem/memory_if.h" | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
| struct clic_config { | ||||
|     uint64_t clic_base{0xc0000000}; | ||||
|     unsigned clic_int_ctl_bits{4}; | ||||
|     unsigned clic_num_irq{16}; | ||||
|     unsigned clic_num_trigger{0}; | ||||
|     bool nmode{false}; | ||||
| }; | ||||
|  | ||||
| inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 1 + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 2 + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *data = *(reg_ptr + 3); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + i) = *(data + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 1 + i) = *(data + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 2 + i) = *(data + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *(reg_ptr + 3) = *data; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> struct clic : public memory_elem { | ||||
|     using this_class = clic<WORD_TYPE>; | ||||
|     using reg_t = WORD_TYPE; | ||||
|     constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; | ||||
|  | ||||
|     clic(arch::priv_if<WORD_TYPE> hart_if, clic_config cfg) | ||||
|     : hart_if(hart_if) | ||||
|     , cfg(cfg) { | ||||
|         clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0}); | ||||
|         clic_cfg_reg = 0x30; | ||||
|         clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1; | ||||
|         clic_uact_lvl = clic_uprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1; | ||||
|         hart_if.csr_rd_cb[arch::mtvt] = MK_CSR_RD_CB(read_plain); | ||||
|         hart_if.csr_wr_cb[arch::mtvt] = MK_CSR_WR_CB(write_xtvt); | ||||
|         //        hart_if.csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);}; | ||||
|         //        hart_if.csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);}; | ||||
|         hart_if.csr_rd_cb[arch::mintstatus] = MK_CSR_RD_CB(read_intstatus); | ||||
|         hart_if.csr_wr_cb[arch::mintstatus] = MK_CSR_WR_CB(write_null); | ||||
|         //        hart_if.csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);}; | ||||
|         //        hart_if.csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);}; | ||||
|         //        hart_if.csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);}; | ||||
|         //        hart_if.csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);}; | ||||
|         hart_if.csr_rd_cb[arch::mintthresh] = MK_CSR_RD_CB(read_plain); | ||||
|         hart_if.csr_wr_cb[arch::mintthresh] = MK_CSR_WR_CB(write_intthresh); | ||||
|         if(cfg.nmode) { | ||||
|             hart_if.csr_rd_cb[arch::utvt] = MK_CSR_RD_CB(read_plain); | ||||
|             hart_if.csr_wr_cb[arch::utvt] = MK_CSR_WR_CB(write_xtvt); | ||||
|             hart_if.csr_rd_cb[arch::uintstatus] = MK_CSR_RD_CB(read_intstatus); | ||||
|             hart_if.csr_wr_cb[arch::uintstatus] = MK_CSR_WR_CB(write_null); | ||||
|             hart_if.csr_rd_cb[arch::uintthresh] = MK_CSR_RD_CB(read_plain); | ||||
|             hart_if.csr_wr_cb[arch::uintthresh] = MK_CSR_WR_CB(write_intthresh); | ||||
|         } | ||||
|         hart_if.csr[arch::mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; | ||||
|         hart_if.csr[arch::uintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1; | ||||
|     } | ||||
|  | ||||
|     ~clic() = default; | ||||
|  | ||||
|     memory_if get_mem_if() override { | ||||
|         return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)}, | ||||
|                          .wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}}; | ||||
|     } | ||||
|  | ||||
|     void set_next(memory_if mem) override { down_stream_mem = mem; } | ||||
|  | ||||
|     std::tuple<uint64_t, uint64_t> get_range() override { return {cfg.clic_base, cfg.clic_base + 0x7fff}; } | ||||
|  | ||||
| private: | ||||
|     iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { | ||||
|         if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000)) | ||||
|             return read_clic(addr, length, data); | ||||
|         return down_stream_mem.rd_mem(access, addr, length, data); | ||||
|     } | ||||
|  | ||||
|     iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { | ||||
|         if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000)) | ||||
|             return write_clic(addr, length, data); | ||||
|         return down_stream_mem.wr_mem(access, addr, length, data); | ||||
|     } | ||||
|  | ||||
|     iss::status read_clic(uint64_t addr, unsigned length, uint8_t* data); | ||||
|  | ||||
|     iss::status write_clic(uint64_t addr, unsigned length, uint8_t const* data); | ||||
|  | ||||
|     iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; } | ||||
|  | ||||
|     iss::status read_plain(unsigned addr, reg_t& val) { | ||||
|         val = hart_if.csr[addr]; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_xtvt(unsigned addr, reg_t val) { | ||||
|         hart_if.csr[addr] = val & ~0x3fULL; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_cause(unsigned addr, reg_t& val); | ||||
|     iss::status write_cause(unsigned addr, reg_t val); | ||||
|  | ||||
|     iss::status read_intstatus(unsigned addr, reg_t& val); | ||||
|     iss::status write_intthresh(unsigned addr, reg_t val); | ||||
|  | ||||
| protected: | ||||
|     arch::priv_if<WORD_TYPE> hart_if; | ||||
|     memory_if down_stream_mem; | ||||
|     clic_config cfg; | ||||
|     uint8_t clic_cfg_reg{0}; | ||||
|     std::array<uint32_t, 32> clic_inttrig_reg; | ||||
|     union clic_int_reg_t { | ||||
|         struct { | ||||
|             uint8_t ip; | ||||
|             uint8_t ie; | ||||
|             uint8_t attr; | ||||
|             uint8_t ctl; | ||||
|         }; | ||||
|         uint32_t raw; | ||||
|     }; | ||||
|     std::vector<clic_int_reg_t> clic_int_reg; | ||||
|     uint8_t clic_mprev_lvl{0}, clic_uprev_lvl{0}; | ||||
|     uint8_t clic_mact_lvl{0}, clic_uact_lvl{0}; | ||||
| }; | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_clic(uint64_t addr, unsigned length, uint8_t* const data) { | ||||
|     if(addr == cfg.clic_base) { // cliccfg | ||||
|         *data = clic_cfg_reg; | ||||
|         for(auto i = 1; i < length; ++i) | ||||
|             *(data + i) = 0; | ||||
|     } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig | ||||
|         auto offset = ((addr & 0x7fff) - 0x40) / 4; | ||||
|         read_reg_with_offset(clic_inttrig_reg[offset], addr & 0x3, data, length); | ||||
|     } else if(addr >= (cfg.clic_base + 0x1000) && | ||||
|               (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl | ||||
|         auto offset = ((addr & 0x7fff) - 0x1000) / 4; | ||||
|         read_reg_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length); | ||||
|     } else { | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = 0; | ||||
|     } | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) { | ||||
|     if(addr == cfg.clic_base) { // cliccfg | ||||
|         clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e); | ||||
|     } else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig | ||||
|         auto offset = ((addr & 0x7fff) - 0x40) / 4; | ||||
|         write_reg_with_offset(clic_inttrig_reg[offset], addr & 0x3, data, length); | ||||
|     } else if(addr >= (cfg.clic_base + 0x1000) && | ||||
|               (addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl | ||||
|         auto offset = ((addr & 0x7fff) - 0x1000) / 4; | ||||
|         write_reg_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length); | ||||
|         clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1 | ||||
|     } | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_cause(unsigned addr, reg_t& val) { | ||||
|     if((hart_if.csr[arch::mtvec] & 0x3) == 3) { | ||||
|         val = hart_if.csr[addr] & (1UL << (sizeof(reg_t) * 8) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16)); | ||||
|         auto mode = (addr >> 8) & 0x3; | ||||
|         switch(mode) { | ||||
|         case 0: | ||||
|             val |= clic_uprev_lvl << 16; | ||||
|             val |= hart_if.state.mstatus.UPIE << 27; | ||||
|             break; | ||||
|         default: | ||||
|             val |= clic_mprev_lvl << 16; | ||||
|             val |= hart_if.state.mstatus.MPIE << 27; | ||||
|             val |= hart_if.state.mstatus.MPP << 28; | ||||
|             break; | ||||
|         } | ||||
|     } else | ||||
|         val = hart_if.csr[addr] & ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1)); | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_cause(unsigned addr, reg_t val) { | ||||
|     if((hart_if.csr[arch::mtvec] & 0x3) == 3) { | ||||
|         auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16)); | ||||
|         hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask); | ||||
|         auto mode = (addr >> 8) & 0x3; | ||||
|         switch(mode) { | ||||
|         case 0: | ||||
|             clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; | ||||
|             hart_if.state.mstatus.UPIE = (val >> 27) & 0x1; | ||||
|             break; | ||||
|         default: | ||||
|             clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1; | ||||
|             hart_if.state.mstatus.MPIE = (val >> 27) & 0x1; | ||||
|             hart_if.state.mstatus.MPP = (val >> 28) & 0x3; | ||||
|             break; | ||||
|         } | ||||
|     } else { | ||||
|         auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1)); | ||||
|         hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask); | ||||
|     } | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_intstatus(unsigned addr, reg_t& val) { | ||||
|     auto mode = (addr >> 8) & 0x3; | ||||
|     val = clic_uact_lvl & 0xff; | ||||
|     if(mode == 0x3) | ||||
|         val += (clic_mact_lvl & 0xff) << 24; | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_intthresh(unsigned addr, reg_t val) { | ||||
|     hart_if.csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1; | ||||
|     return iss::Ok; | ||||
| } | ||||
|  | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
							
								
								
									
										67
									
								
								src/iss/mem/memory_if.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/iss/mem/memory_if.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #include "memory_if.h" | ||||
| #include <algorithm> | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
| void memory_hierarchy::root(memory_elem& e) { | ||||
|     hierarchy.push_front(&e); | ||||
|     root_set=true; | ||||
|     update_chain(); | ||||
| } | ||||
| void memory_hierarchy::prepend(memory_elem& e) { | ||||
|     if(root_set) | ||||
|         hierarchy.insert(hierarchy.begin()+1, &e); | ||||
|     else | ||||
|         hierarchy.push_front(&e); | ||||
|     update_chain(); | ||||
| } | ||||
| void memory_hierarchy::append(memory_elem& e) { | ||||
|     hierarchy.push_back(&e); | ||||
|     update_chain(); | ||||
| } | ||||
| void memory_hierarchy::insert_before(memory_elem&) {} | ||||
| void memory_hierarchy::insert_after(memory_elem&) {} | ||||
| void memory_hierarchy::replace_last(memory_elem& e) { | ||||
|     hierarchy.pop_back(); | ||||
|     hierarchy.push_back(&e); | ||||
| } | ||||
| void memory_hierarchy::update_chain() { | ||||
|     bool tail = false; | ||||
|     for(size_t i = 0; i < hierarchy.size(); ++i) { | ||||
|         hierarchy[i]->register_csrs(); | ||||
|         if(i) | ||||
|             hierarchy[i - 1]->set_next(hierarchy[i]->get_mem_if()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void memory_hierarchy::prepend(std::unique_ptr<memory_elem>&& p) { | ||||
|     prepend(*p); | ||||
|     owned_elems.push_back(std::move(p)); | ||||
| } | ||||
|  | ||||
| void memory_hierarchy::append(std::unique_ptr<memory_elem>&& p) { | ||||
|     append(*p); | ||||
|     owned_elems.push_back(std::move(p)); | ||||
| } | ||||
|  | ||||
| void memory_hierarchy::insert_before(std::unique_ptr<memory_elem>&& p) { | ||||
|     insert_before(*p); | ||||
|     owned_elems.push_back(std::move(p)); | ||||
| } | ||||
|  | ||||
| void memory_hierarchy::insert_after(std::unique_ptr<memory_elem>&& p) { | ||||
|     insert_after(*p); | ||||
|     owned_elems.push_back(std::move(p)); | ||||
| } | ||||
|  | ||||
| void memory_hierarchy::replace_last(std::unique_ptr<memory_elem>&& p) { | ||||
|     auto e = hierarchy.back(); | ||||
|     replace_last(*p); | ||||
|     auto it  = std::find_if(std::begin(owned_elems), std::end(owned_elems), [e](std::unique_ptr<memory_elem> const& p) {return p.get()==e;}); | ||||
|     if(it!=std::end(owned_elems)) | ||||
|         owned_elems.erase(it); | ||||
|     owned_elems.push_back(std::move(p)); | ||||
| } | ||||
|  | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
							
								
								
									
										87
									
								
								src/iss/mem/memory_if.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/iss/mem/memory_if.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2025 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||
|  *    may be used to endorse or promote products derived from this software | ||||
|  *    without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
|  * POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  * Contributors: | ||||
|  *       eyck@minres.com - initial implementation | ||||
|  ******************************************************************************/ | ||||
|  | ||||
| #ifndef _MEMORY_MEMORY_IF_ | ||||
| #define _MEMORY_MEMORY_IF_ | ||||
|  | ||||
| #include "iss/vm_types.h" | ||||
| #include <deque> | ||||
| #include <vector> | ||||
| #include <functional> | ||||
| #include <limits> | ||||
| #include <util/delegate.h> | ||||
| #include <memory> | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
|  | ||||
| using rd_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t*); | ||||
| using wr_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t const*); | ||||
|  | ||||
| struct memory_if { | ||||
|     util::delegate<iss::status(access_type, uint64_t, unsigned, uint8_t*)> rd_mem; | ||||
|     util::delegate<iss::status(access_type, uint64_t, unsigned, uint8_t const*)> wr_mem; | ||||
| }; | ||||
|  | ||||
| struct memory_elem { | ||||
|     virtual ~memory_elem() = default; | ||||
|     virtual memory_if get_mem_if() = 0; | ||||
|     virtual void set_next(memory_if) = 0; | ||||
|     virtual void register_csrs() {} | ||||
|     virtual std::tuple<uint64_t, uint64_t> get_range() { return {0, std::numeric_limits<uint64_t>::max()}; } | ||||
| }; | ||||
|  | ||||
| struct memory_hierarchy { | ||||
|     void root(memory_elem&); | ||||
|     void prepend(memory_elem&); | ||||
|     void append(memory_elem&); | ||||
|     void insert_before(memory_elem&); | ||||
|     void insert_after(memory_elem&); | ||||
|     void replace_last(memory_elem&); | ||||
|     void prepend(std::unique_ptr<memory_elem>&&); | ||||
|     void append(std::unique_ptr<memory_elem>&&); | ||||
|     void insert_before(std::unique_ptr<memory_elem>&&); | ||||
|     void insert_after(std::unique_ptr<memory_elem>&&); | ||||
|     void replace_last(std::unique_ptr<memory_elem>&&); | ||||
|  | ||||
| protected: | ||||
|     void update_chain(); | ||||
|     std::deque<memory_elem*> hierarchy; | ||||
|     std::vector<std::unique_ptr<memory_elem>> owned_elems; | ||||
|     bool root_set{false}; | ||||
| }; | ||||
|  | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
| #endif | ||||
							
								
								
									
										56
									
								
								src/iss/mem/memory_with_htif.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/iss/mem/memory_with_htif.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #ifndef _MEMORY_WITH_HTIF_ | ||||
| #define _MEMORY_WITH_HTIF_ | ||||
|  | ||||
| #include "iss/arch/riscv_hart_common.h" | ||||
| #include "iss/vm_types.h" | ||||
| #include <util/logging.h> | ||||
| #include <util/sparse_array.h> | ||||
| #include "../mem/memory_if.h" | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
| template <typename WORD_TYPE> struct memory_with_htif : public memory_elem { | ||||
|     using this_class = memory_with_htif<WORD_TYPE>; | ||||
|     constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; | ||||
|  | ||||
|     memory_with_htif(arch::priv_if<WORD_TYPE> hart_if) | ||||
|     : hart_if(hart_if) {} | ||||
|  | ||||
|     ~memory_with_htif() = default; | ||||
|  | ||||
|     memory_if get_mem_if() override { | ||||
|         return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)}, | ||||
|                          .wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}}; | ||||
|     } | ||||
|  | ||||
|     void set_next(memory_if) override { | ||||
|         // intenrionally left empty, leaf element | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { | ||||
|         for(auto offs = 0U; offs < length; ++offs) { | ||||
|             *(data + offs) = mem[(addr + offs) % mem.size()]; | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { | ||||
|         mem_type::page_type& p = mem(addr / mem.page_size); | ||||
|         std::copy(data, data + length, p.data() + (addr & mem.page_addr_mask)); | ||||
|         // this->tohost handling in case of riscv-test | ||||
|         // according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754: | ||||
|         if(access && iss::access_type::FUNC && addr == hart_if.tohost) { | ||||
|             return hart_if.exec_htif(data); | ||||
|         } | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     using mem_type = util::sparse_array<uint8_t, 1ULL << 32>; | ||||
|     mem_type mem; | ||||
|     arch::priv_if<WORD_TYPE> hart_if; | ||||
| }; | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
| #endif // _MEMORY_WITH_HTIF_ | ||||
							
								
								
									
										325
									
								
								src/iss/mem/mmu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								src/iss/mem/mmu.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,325 @@ | ||||
|  | ||||
| #include "iss/arch/riscv_hart_common.h" | ||||
| #include "iss/vm_types.h" | ||||
| #include <util/logging.h> | ||||
| #include "../mem/memory_if.h" | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
| enum { | ||||
|     PGSHIFT = 12, | ||||
|     PTE_PPN_SHIFT = 10, | ||||
|     // page table entry (PTE) fields | ||||
|     PTE_V = 0x001,   // Valid | ||||
|     PTE_R = 0x002,   // Read | ||||
|     PTE_W = 0x004,   // Write | ||||
|     PTE_X = 0x008,   // Execute | ||||
|     PTE_U = 0x010,   // User | ||||
|     PTE_G = 0x020,   // Global | ||||
|     PTE_A = 0x040,   // Accessed | ||||
|     PTE_D = 0x080,   // Dirty | ||||
|     PTE_SOFT = 0x300 // Reserved for Software | ||||
| }; | ||||
|  | ||||
|  | ||||
| template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); } | ||||
|  | ||||
| struct vm_info { | ||||
|     int levels; | ||||
|     int idxbits; | ||||
|     int ptesize; | ||||
|     uint64_t ptbase; | ||||
|     bool is_active() { return levels; } | ||||
| }; | ||||
|  | ||||
| inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 1 + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 2 + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *data = *(reg_ptr + 3); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + i) = *(data + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 1 + i) = *(data + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 2 + i) = *(data + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *(reg_ptr + 3) = *data; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| //TODO: update vminfo on trap enter and leave as well as mstatus write, reset | ||||
| template <typename WORD_TYPE> struct mmu : public memory_elem { | ||||
|     using this_class = mmu<WORD_TYPE>; | ||||
|     using reg_t = WORD_TYPE; | ||||
|     constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; | ||||
|  | ||||
|     constexpr static reg_t PGSIZE = 1 << PGSHIFT; | ||||
|     constexpr static reg_t PGMASK = PGSIZE - 1; | ||||
|  | ||||
|  | ||||
|  | ||||
|     mmu(arch::priv_if<WORD_TYPE> hart_if) | ||||
|     : hart_if(hart_if) { | ||||
|         hart_if.csr_rd_cb[satp] = MK_CSR_RD_CB(read_satp); | ||||
|         hart_if.csr_wr_cb[satp] = MK_CSR_WR_CB(write_satp); | ||||
|    } | ||||
|  | ||||
|     virtual ~mmu() = default; | ||||
|  | ||||
|     memory_if get_mem_if() override { | ||||
|         return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)}, | ||||
|                          .wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}}; | ||||
|     } | ||||
|  | ||||
|     void set_next(memory_if mem) override { down_stream_mem = mem; } | ||||
|  | ||||
| private: | ||||
|     iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { | ||||
|         if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary | ||||
|             vm_info vm = decode_vm_info(hart_if.PRIV, satp); | ||||
|             if(vm.levels != 0) { // VM is active | ||||
|                 auto split_addr = (addr + length) & ~PGMASK; | ||||
|                 auto len1 = split_addr - addr; | ||||
|                 auto res = down_stream_mem.rd_mem(access, addr, len1, data); | ||||
|                 if(res == iss::Ok) | ||||
|                     res = down_stream_mem.rd_mem(access, split_addr, length - len1, data + len1); | ||||
|                 return res; | ||||
|             } | ||||
|         } | ||||
|         return down_stream_mem.rd_mem(access, addr, length, data); | ||||
|     } | ||||
|  | ||||
|     iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { | ||||
|         if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary | ||||
|             vm_info vm = decode_vm_info(hart_if.PRIV, satp); | ||||
|             if(vm.levels != 0) { // VM is active | ||||
|                 auto split_addr = (addr + length) & ~PGMASK; | ||||
|                 auto len1 = split_addr - addr; | ||||
|                 auto res = down_stream_mem.wr_mem(access, addr, len1, data); | ||||
|                 if(res == iss::Ok) | ||||
|                     res = down_stream_mem.wr_mem(access,  split_addr, length - len1, data + len1); | ||||
|                 return res; | ||||
|             } | ||||
|         } | ||||
|         return down_stream_mem.wr_mem(access, virt2phys(access, addr), length, data); | ||||
|     } | ||||
|     void update_vm_info(); | ||||
|  | ||||
|     iss::status read_plain(unsigned addr, reg_t& val) { | ||||
|         val = hart_if.csr[addr]; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_plain(unsigned addr, reg_t const& val) { | ||||
|         hart_if.csr[addr] = val; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status read_satp(unsigned addr, reg_t& val) { | ||||
|         auto tvm = bit_sub<20, 1>(hart_if.state.mstatus()); | ||||
|         if(hart_if.PRIV == arch::PRIV_S & tvm != 0) { | ||||
|             hart_if.raise_trap(2, 0,  hart_if.PC); | ||||
| //            hart_if.reg.trap_state = (1 << 31) | (2 << 16); | ||||
| //            hart_if.fault_data = hart_if.reg.PC; | ||||
|             return iss::Err; | ||||
|         } | ||||
|         val = satp; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_satp(unsigned addr, reg_t val) { | ||||
|         reg_t tvm = hart_if.state.mstatus.TVM; | ||||
|         if(hart_if.PRIV == arch::PRIV_S & tvm != 0) { | ||||
|             hart_if.raise_trap(2, 0,  hart_if.PC); | ||||
| //            hart_if.reg.trap_state = (1 << 31) | (2 << 16); | ||||
| //            hart_if.fault_data = hart_if.reg.PC; | ||||
|             return iss::Err; | ||||
|         } | ||||
|         satp = val; | ||||
|         update_vm_info(); | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     uint64_t virt2phys(iss::access_type access, uint64_t addr); | ||||
|  | ||||
|     static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) { | ||||
|         if(state == arch::PRIV_M) | ||||
|             return {0, 0, 0, 0}; | ||||
|         if(state <= arch::PRIV_S) | ||||
|             switch(bit_sub<31, 1>(sptbr)) { | ||||
|             case 0: | ||||
|                 return {0, 0, 0, 0}; // off | ||||
|             case 1: | ||||
|                 return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32 | ||||
|             default: | ||||
|                 abort(); | ||||
|             } | ||||
|         abort(); | ||||
|         return {0, 0, 0, 0}; // dummy | ||||
|     } | ||||
|  | ||||
|     static inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr) { | ||||
|         if(state == arch::PRIV_M) | ||||
|             return {0, 0, 0, 0}; | ||||
|         if(state <= arch::PRIV_S) | ||||
|             switch(bit_sub<60, 4>(sptbr)) { | ||||
|             case 0: | ||||
|                 return {0, 0, 0, 0}; // off | ||||
|             case 8: | ||||
|                 return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39 | ||||
|             case 9: | ||||
|                 return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48 | ||||
|             case 10: | ||||
|                 return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57 | ||||
|             case 11: | ||||
|                 return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64 | ||||
|             default: | ||||
|                 abort(); | ||||
|             } | ||||
|         abort(); | ||||
|         return {0, 0, 0, 0}; // dummy | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     reg_t satp; | ||||
|     std::unordered_map<reg_t, uint64_t> ptw; | ||||
|     std::array<vm_info, 2> vmt; | ||||
|     std::array<address_type, 4> addr_mode; | ||||
|  | ||||
|     arch::priv_if<WORD_TYPE> hart_if; | ||||
|     memory_if down_stream_mem; | ||||
| }; | ||||
|  | ||||
| template <typename WORD_TYPE> uint64_t  mmu<WORD_TYPE>::virt2phys(iss::access_type access, uint64_t addr) { | ||||
|     const auto type = access & iss::access_type::FUNC; | ||||
|     auto it = ptw.find(addr >> PGSHIFT); | ||||
|     if(it != ptw.end()) { | ||||
|         const reg_t pte = it->second; | ||||
|         const reg_t ad = PTE_A | (type == iss::access_type::WRITE) * PTE_D; | ||||
| #ifdef RISCV_ENABLE_DIRTY | ||||
|         // set accessed and possibly dirty bits. | ||||
|         *(uint32_t*)ppte |= ad; | ||||
|         return {addr.getAccessType(), addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)}; | ||||
| #else | ||||
|         // take exception if access or possibly dirty bit is not set. | ||||
|         if((pte & ad) == ad) | ||||
|             return {(pte & (~PGMASK)) | (addr & PGMASK)}; | ||||
|         else | ||||
|             ptw.erase(it); // throw an exception | ||||
| #endif | ||||
|     } else { | ||||
|         uint32_t mode = type != iss::access_type::FETCH && hart_if.state.mstatus.MPRV ? // MPRV | ||||
|                 hart_if.state.mstatus.MPP | ||||
|                 : hart_if.PRIV; | ||||
|  | ||||
|         const vm_info& vm = vmt[static_cast<uint16_t>(type) / 2]; | ||||
|  | ||||
|         const bool s_mode = mode == arch::PRIV_S; | ||||
|         const bool sum = hart_if.state.mstatus.SUM; | ||||
|         const bool mxr = hart_if.state.mstatus.MXR; | ||||
|  | ||||
|         // verify bits xlen-1:va_bits-1 are all equal | ||||
|         const int va_bits = PGSHIFT + vm.levels * vm.idxbits; | ||||
|         const reg_t mask = (reg_t(1) << (sizeof(reg_t)*8 - (va_bits - 1))) - 1; | ||||
|         const reg_t masked_msbs = (addr >> (va_bits - 1)) & mask; | ||||
|         const int levels = (masked_msbs != 0 && masked_msbs != mask) ? 0 : vm.levels; | ||||
|  | ||||
|         reg_t base = vm.ptbase; | ||||
|         for(int i = levels - 1; i >= 0; i--) { | ||||
|             const int ptshift = i * vm.idxbits; | ||||
|             const reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1); | ||||
|  | ||||
|             // check that physical address of PTE is legal | ||||
|             reg_t pte = 0; | ||||
|             const uint8_t res = down_stream_mem.rd_mem(iss::access_type::READ, base + idx * vm.ptesize, vm.ptesize, | ||||
|                                            (uint8_t*)&pte); | ||||
|             if(res != 0) | ||||
|                 throw arch::trap_load_access_fault(addr); | ||||
|             const reg_t ppn = pte >> PTE_PPN_SHIFT; | ||||
|  | ||||
|             if(PTE_TABLE(pte)) { // next level of page table | ||||
|                 base = ppn << PGSHIFT; | ||||
|             } else if((pte & PTE_U) ? s_mode && (type == iss::access_type::FETCH || !sum) : !s_mode) { | ||||
|                 break; | ||||
|             } else if(!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { | ||||
|                 break; | ||||
|             } else if(type == (type == iss::access_type::FETCH  ? !(pte & PTE_X) | ||||
|                                : type == iss::access_type::READ ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) | ||||
|                                                                 : !((pte & PTE_R) && (pte & PTE_W)))) { | ||||
|                 break; | ||||
|             } else if((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { | ||||
|                 break; | ||||
|             } else { | ||||
|                 const reg_t ad = PTE_A | ((type == iss::access_type::WRITE) * PTE_D); | ||||
| #ifdef RISCV_ENABLE_DIRTY | ||||
|                 // set accessed and possibly dirty bits. | ||||
|                 *(uint32_t*)ppte |= ad; | ||||
| #else | ||||
|                 // take exception if access or possibly dirty bit is not set. | ||||
|                 if((pte & ad) != ad) | ||||
|                     break; | ||||
| #endif | ||||
|                 // for superpage mappings, make a fake leaf PTE for the TLB's benefit. | ||||
|                 const reg_t vpn = addr >> PGSHIFT; | ||||
|                 const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; | ||||
|                 const reg_t offset = addr & PGMASK; | ||||
|                 ptw[vpn] = value | (pte & 0xff); | ||||
|                 return value | offset; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     switch(type) { | ||||
|     case access_type::FETCH: | ||||
|         hart_if.raise_trap(12, 0, addr); | ||||
|         throw arch::trap_instruction_page_fault(addr); | ||||
|     case access_type::READ: | ||||
|         hart_if.raise_trap(13, 0, addr); | ||||
|         throw arch::trap_load_page_fault(addr); | ||||
|     case access_type::WRITE: | ||||
|         hart_if.raise_trap(15, 0, addr); | ||||
|         throw arch::trap_store_page_fault(addr); | ||||
|     default: | ||||
|         abort(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> inline void mmu<WORD_TYPE>::update_vm_info() { | ||||
|     vmt[1] = decode_vm_info(hart_if.PRIV, satp); | ||||
|     addr_mode[3] = addr_mode[2] = vmt[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; | ||||
|     if(hart_if.state.mstatus.MPRV) | ||||
|         vmt[0] = decode_vm_info(hart_if.state.mstatus.MPP, satp); | ||||
|     else | ||||
|         vmt[0] = vmt[1]; | ||||
|     addr_mode[1] = addr_mode[0] = vmt[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; | ||||
|     ptw.clear(); | ||||
| } | ||||
|  | ||||
|  | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
							
								
								
									
										211
									
								
								src/iss/mem/pmp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/iss/mem/pmp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
|  | ||||
| #include "iss/arch/riscv_hart_common.h" | ||||
| #include "iss/vm_types.h" | ||||
| #include <util/logging.h> | ||||
| #include "../mem/memory_if.h" | ||||
|  | ||||
| namespace iss { | ||||
| namespace mem { | ||||
| struct clic_config { | ||||
|     uint64_t clic_base{0xc0000000}; | ||||
|     unsigned clic_int_ctl_bits{4}; | ||||
|     unsigned clic_num_irq{16}; | ||||
|     unsigned clic_num_trigger{0}; | ||||
|     bool nmode{false}; | ||||
| }; | ||||
|  | ||||
| inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 1 + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(data + i) = *(reg_ptr + 2 + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *data = *(reg_ptr + 3); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) { | ||||
|     auto reg_ptr = reinterpret_cast<uint8_t*>(®); | ||||
|     switch(offs) { | ||||
|     default: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + i) = *(data + i); | ||||
|         break; | ||||
|     case 1: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 1 + i) = *(data + i); | ||||
|         break; | ||||
|     case 2: | ||||
|         for(auto i = 0U; i < length; ++i) | ||||
|             *(reg_ptr + 2 + i) = *(data + i); | ||||
|         break; | ||||
|     case 3: | ||||
|         *(reg_ptr + 3) = *data; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename WORD_TYPE> struct pmp : public memory_elem { | ||||
|     using this_class = pmp<WORD_TYPE>; | ||||
|     using reg_t = WORD_TYPE; | ||||
|     constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8; | ||||
|  | ||||
|     pmp(arch::priv_if<WORD_TYPE> hart_if) | ||||
|     : hart_if(hart_if) { | ||||
|         for(size_t i = arch::pmpaddr0; i <= arch::pmpaddr15; ++i) { | ||||
|             hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain); | ||||
|             hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_plain); | ||||
|         } | ||||
|         for(size_t i = arch::pmpcfg0; i < arch::pmpcfg0 + 16 / sizeof(reg_t); ++i) { | ||||
|             hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain); | ||||
|             hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_pmpcfg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     virtual ~pmp() = default; | ||||
|  | ||||
|     memory_if get_mem_if() override { | ||||
|         return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)}, | ||||
|                          .wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}}; | ||||
|     } | ||||
|  | ||||
|     void set_next(memory_if mem) override { down_stream_mem = mem; } | ||||
|  | ||||
| private: | ||||
|     iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) { | ||||
|         if(!pmp_check(access, addr, length) && !is_debug(access)) { | ||||
|             hart_if.fault_data = addr; | ||||
|             if(is_debug(access)) | ||||
|                 throw trap_access(0, addr); | ||||
|             hart_if.reg.trap_state = (1UL << 31) | ((access == access_type::FETCH ? 1 : 5) << 16); // issue trap 1 | ||||
|             return iss::Err; | ||||
|         } | ||||
|         return down_stream_mem.rd_mem(access, addr, length, data); | ||||
|     } | ||||
|  | ||||
|     iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) { | ||||
|         if(!pmp_check(access, addr, length) && !is_debug(access)) { | ||||
|             hart_if.fault_data = addr; | ||||
|             if(is_debug(access)) | ||||
|                 throw trap_access(0, addr); | ||||
|             hart_if.reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 1 | ||||
|             return iss::Err; | ||||
|         } | ||||
|         return down_stream_mem.wr_mem(access, addr, length, data); | ||||
|     } | ||||
|  | ||||
|     iss::status read_plain(unsigned addr, reg_t& val) { | ||||
|         val = hart_if.csr[addr]; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_plain(unsigned addr, reg_t const& val) { | ||||
|         hart_if.csr[addr] = val; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     iss::status write_pmpcfg(unsigned addr, reg_t val) { | ||||
|         hart_if.csr[addr] = val & 0x9f9f9f9f; | ||||
|         return iss::Ok; | ||||
|     } | ||||
|  | ||||
|     bool pmp_check(const access_type type, const uint64_t addr, const unsigned len); | ||||
|  | ||||
| protected: | ||||
|     arch::priv_if<WORD_TYPE> hart_if; | ||||
|     memory_if down_stream_mem; | ||||
| }; | ||||
|  | ||||
| template <typename WORD_TYPE> bool pmp<WORD_TYPE>::pmp_check(const access_type type, const uint64_t addr, const unsigned len) { | ||||
|     constexpr auto PMP_SHIFT = 2U; | ||||
|     constexpr auto PMP_R = 0x1U; | ||||
|     constexpr auto PMP_W = 0x2U; | ||||
|     constexpr auto PMP_X = 0x4U; | ||||
|     constexpr auto PMP_A = 0x18U; | ||||
|     constexpr auto PMP_L = 0x80U; | ||||
|     constexpr auto PMP_TOR = 0x1U; | ||||
|     constexpr auto PMP_NA4 = 0x2U; | ||||
|     constexpr auto PMP_NAPOT = 0x3U; | ||||
|     reg_t base = 0; | ||||
|     auto any_active = false; | ||||
|     auto const cfg_reg_size = sizeof(reg_t); | ||||
|     for(size_t i = 0; i < 16; i++) { | ||||
|         reg_t tor = hart_if.csr[arch::pmpaddr0 + i] << PMP_SHIFT; | ||||
|         uint8_t cfg = hart_if.csr[arch::pmpcfg0 + (i / cfg_reg_size)] >> (i % cfg_reg_size); | ||||
|         if(cfg & PMP_A) { | ||||
|             any_active = true; | ||||
|             auto pmp_a = (cfg & PMP_A) >> 3; | ||||
|             auto is_tor = pmp_a == PMP_TOR; | ||||
|             auto is_na4 = pmp_a == PMP_NA4; | ||||
|  | ||||
|             reg_t mask = (hart_if.csr[arch::pmpaddr0 + i] << 1) | (!is_na4); | ||||
|             mask = ~(mask & ~(mask + 1)) << PMP_SHIFT; | ||||
|  | ||||
|             // Check each 4-byte sector of the access | ||||
|             auto any_match = false; | ||||
|             auto all_match = true; | ||||
|             for(reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) { | ||||
|                 reg_t cur_addr = addr + offset; | ||||
|                 auto napot_match = ((cur_addr ^ tor) & mask) == 0; | ||||
|                 auto tor_match = base <= (cur_addr + len - 1) && cur_addr < tor; | ||||
|                 auto match = is_tor ? tor_match : napot_match; | ||||
|                 any_match |= match; | ||||
|                 all_match &= match; | ||||
|             } | ||||
|             if(any_match) { | ||||
|                 // If the PMP matches only a strict subset of the access, fail it | ||||
|                 if(!all_match) | ||||
|                     return false; | ||||
|                 return (hart_if.reg.PRIV == arch::PRIV_M && !(cfg & PMP_L)) || (type == access_type::READ && (cfg & PMP_R)) || | ||||
|                        (type == access_type::WRITE && (cfg & PMP_W)) || (type == access_type::FETCH && (cfg & PMP_X)); | ||||
|             } | ||||
|         } | ||||
|         base = tor; | ||||
|     } | ||||
|     //    constexpr auto pmp_num_regs = 16; | ||||
|     //    reg_t tor_base = 0; | ||||
|     //    auto any_active = false; | ||||
|     //    auto lower_addr = addr >>2; | ||||
|     //    auto upper_addr = (addr+len-1)>>2; | ||||
|     //    for (size_t i = 0; i < pmp_num_regs; i++) { | ||||
|     //        uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4); | ||||
|     //        uint8_t cfg_next = i==(pmp_num_regs-1)? 0 : csr[pmpcfg0+((i+1)/4)]>>((i+1)%4); | ||||
|     //        auto pmpaddr = csr[pmpaddr0+i]; | ||||
|     //        if (cfg & PMP_A) { | ||||
|     //            any_active=true; | ||||
|     //            auto is_tor = bit_sub<3, 2>(cfg) == PMP_TOR; | ||||
|     //            auto is_napot = bit_sub<4, 1>(cfg) && bit_sub<3, 2>(cfg_next)!= PMP_TOR; | ||||
|     //            if(is_napot) { | ||||
|     //                reg_t mask = bit_sub<3, 1>(cfg)?~( pmpaddr & ~(pmpaddr + 1)): 0x3fffffff; | ||||
|     //                auto mpmpaddr = pmpaddr & mask; | ||||
|     //                if((lower_addr&mask) == mpmpaddr && (upper_addr&mask)==mpmpaddr) | ||||
|     //                    return  (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) || | ||||
|     //                            (type == access_type::READ && (cfg & PMP_R)) || | ||||
|     //                            (type == access_type::WRITE && (cfg & PMP_W)) || | ||||
|     //                            (type == access_type::FETCH && (cfg & PMP_X)); | ||||
|     //            } else if(is_tor) { | ||||
|     //                if(lower_addr>=tor_base && upper_addr<=pmpaddr) | ||||
|     //                    return  (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) || | ||||
|     //                            (type == access_type::READ && (cfg & PMP_R)) || | ||||
|     //                            (type == access_type::WRITE && (cfg & PMP_W)) || | ||||
|     //                            (type == access_type::FETCH && (cfg & PMP_X)); | ||||
|     //            } | ||||
|     //        } | ||||
|     //        tor_base = pmpaddr; | ||||
|     //    } | ||||
|     return !any_active || hart_if.reg.PRIV == arch::PRIV_M; | ||||
| } | ||||
|  | ||||
| } // namespace mem | ||||
| } // namespace iss | ||||
| @@ -387,7 +387,7 @@ template <unsigned int BUSWIDTH> void core_complex<BUSWIDTH>::run() { | ||||
|         quantum_keeper.reset(); | ||||
|         cpu->set_interrupt_execution(false); | ||||
|         cpu->start(dump_ir); | ||||
|     } while(cpu->get_interrupt_execution()); | ||||
|     } while(!cpu->get_interrupt_execution()); | ||||
|     sc_stop(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -71,44 +71,61 @@ public: | ||||
|     iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data) override { | ||||
|         if(addr.access && iss::access_type::DEBUG) | ||||
|             return owner->write_mem_dbg(addr.val, length, data) ? iss::Ok : iss::Err; | ||||
|         else { | ||||
|             auto tohost_upper = (sizeof(reg_t) == 4 && addr.val == (this->tohost + 4)) || (sizeof(reg_t) == 8 && addr.val == this->tohost); | ||||
|             auto tohost_lower = (sizeof(reg_t) == 4 && addr.val == this->tohost) || (sizeof(reg_t) == 64 && addr.val == this->tohost); | ||||
|             if(tohost_lower || tohost_upper) { | ||||
|                 if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { | ||||
|                     switch(hostvar >> 48) { | ||||
|                     case 0: | ||||
|                         if(hostvar != 0x1) { | ||||
|                             SCCINFO(owner->hier_name()) | ||||
|                                 << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar << "), stopping simulation"; | ||||
|                         } else { | ||||
|                             SCCINFO(owner->hier_name()) | ||||
|                                 << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar << "), stopping simulation"; | ||||
|                         } | ||||
|                         this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|                         this->interrupt_sim = hostvar; | ||||
| #ifndef WITH_TCC | ||||
|                         throw(iss::simulation_stopped(hostvar)); | ||||
| #endif | ||||
|                         break; | ||||
|                     default: | ||||
|                         break; | ||||
|                     } | ||||
|                 } else if(tohost_lower) | ||||
|                     to_host_wr_cnt++; | ||||
|                 return iss::Ok; | ||||
|             } else { | ||||
|                 auto res = owner->write_mem(addr.val, length, data) ? iss::Ok : iss::Err; | ||||
|                 // clear MTIP on mtimecmp write | ||||
|                 if(addr.val == 0x2004000) { | ||||
|                     reg_t val; | ||||
|                     this->read_csr(iss::arch::mip, val); | ||||
|                     if(val & (1ULL << 7)) | ||||
|                         this->write_csr(iss::arch::mip, val & ~(1ULL << 7)); | ||||
|         if(addr.val == this->tohost) { | ||||
|             reg_t cur_data = *reinterpret_cast<const reg_t*>(data); | ||||
|             // Extract Device (bits 63:56) | ||||
|             uint8_t device = sizeof(reg_t) == 4 ? 0 : (cur_data >> 56) & 0xFF; | ||||
|             // Extract Command (bits 55:48) | ||||
|             uint8_t command = sizeof(reg_t) == 4 ? 0 : (cur_data >> 48) & 0xFF; | ||||
|             // Extract payload (bits 47:0) | ||||
|             uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL; // 24bits | ||||
|             if(payload_addr & 1) { | ||||
|                 if(payload_addr != 0x1) { | ||||
|                     SCCERR(owner->hier_name()) << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr | ||||
|                                                << "), stopping simulation"; | ||||
|                 } else { | ||||
|                     SCCINFO(owner->hier_name()) | ||||
|                         << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr << "), stopping simulation"; | ||||
|                 } | ||||
|                 return res; | ||||
|                 this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|                 this->interrupt_sim = payload_addr; | ||||
| #ifndef WITH_TCC | ||||
|                 throw(iss::simulation_stopped(payload_addr)); | ||||
| #endif | ||||
|                 return iss::Ok; | ||||
|             } | ||||
|             if(device == 0 && command == 0) { | ||||
|                 std::array<uint64_t, 8> loaded_payload; | ||||
|                 auto res = owner->read_mem(payload_addr, 8 * sizeof(uint64_t), reinterpret_cast<uint8_t*>(loaded_payload.data()), false) | ||||
|                                ? iss::Ok | ||||
|                                : iss::Err; | ||||
|                 if(res == iss::Err) { | ||||
|                     SCCERR(owner->hier_name()) << "Syscall read went wrong"; | ||||
|                     return iss::Ok; | ||||
|                 } | ||||
|                 uint64_t syscall_num = loaded_payload.at(0); | ||||
|                 if(syscall_num == 64) // SYS_WRITE | ||||
|                     return this->execute_sys_write(this, loaded_payload, PLAT::MEM); | ||||
|                 SCCERR(owner->hier_name()) << "tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num | ||||
|                                            << ") not implemented"; | ||||
|                 this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|                 this->interrupt_sim = payload_addr; | ||||
|                 return iss::Ok; | ||||
|             } | ||||
|             SCCERR(owner->hier_name()) << "tohost functionality not implemented for device " << device << " and command " << command; | ||||
|             this->reg.trap_state = std::numeric_limits<uint32_t>::max(); | ||||
|             this->interrupt_sim = payload_addr; | ||||
|             return iss::Ok; | ||||
|         } | ||||
|         auto res = owner->write_mem(addr.val, length, data) ? iss::Ok : iss::Err; | ||||
|         // clear MTIP on mtimecmp write | ||||
|         if(addr.val == 0x2004000) { | ||||
|             reg_t val; | ||||
|             this->read_csr(iss::arch::mip, val); | ||||
|             if(val & (1ULL << 7)) | ||||
|                 this->write_csr(iss::arch::mip, val & ~(1ULL << 7)); | ||||
|         } | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     iss::status read_csr(unsigned addr, reg_t& val) override { | ||||
| @@ -129,10 +146,10 @@ public: | ||||
|  | ||||
|     void wait_until(uint64_t flags) override { | ||||
|         SCCDEBUG(owner->hier_name()) << "Sleeping until interrupt"; | ||||
|         PLAT::wait_until(flags); | ||||
|         while(this->reg.pending_trap == 0 && (this->csr[iss::arch::mip] & this->csr[iss::arch::mie]) == 0) { | ||||
|             sc_core::wait(wfi_evt); | ||||
|         } | ||||
|         PLAT::wait_until(flags); | ||||
|     } | ||||
|  | ||||
|     void local_irq(short id, bool value) override { | ||||
| @@ -165,7 +182,6 @@ public: | ||||
| private: | ||||
|     sysc::tgfs::core_complex_if* const owner{nullptr}; | ||||
|     sc_core::sc_event wfi_evt; | ||||
|     uint64_t hostvar{std::numeric_limits<uint64_t>::max()}; | ||||
|     unsigned to_host_wr_cnt = 0; | ||||
|     bool first{true}; | ||||
| }; | ||||
|   | ||||
| @@ -4785,8 +4785,6 @@ continuation_e vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, jit_hold | ||||
|     code_word_t instr = 0; | ||||
|     phys_addr_t paddr(pc); | ||||
|     auto *const data = (uint8_t *)&instr; | ||||
|     if(this->core.has_mmu()) | ||||
|         paddr = this->core.virt2phys(pc); | ||||
|     auto res = this->core.read(paddr, 4, data); | ||||
|     if (res != iss::Ok) | ||||
|         return ILLEGAL_FETCH; | ||||
| @@ -4926,4 +4924,4 @@ volatile std::array<bool, 2> dummy = { | ||||
| }; | ||||
| } | ||||
| } | ||||
| // clang-format on | ||||
| // clang-format on | ||||
|   | ||||
| @@ -251,22 +251,8 @@ private: | ||||
|     decoder instr_decoder; | ||||
|  | ||||
|     iss::status fetch_ins(virt_addr_t pc, uint8_t * data){ | ||||
|         if(this->core.has_mmu()) { | ||||
|             auto phys_pc = this->core.virt2phys(pc); | ||||
| //            if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary | ||||
| //                if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err; | ||||
| //                if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction | ||||
| //                    if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) | ||||
| //                        return iss::Err; | ||||
| //            } else { | ||||
|                 if (this->core.read(phys_pc, 4, data) != iss::Ok) | ||||
|                     return iss::Err; | ||||
| //            } | ||||
|         } else { | ||||
|             if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok) | ||||
|                 return iss::Err; | ||||
|  | ||||
|         } | ||||
|         if (this->core.read(iss::address_type::PHYSICAL, pc.access, pc.space, pc.val, 4, data) != iss::Ok) | ||||
|             return iss::Err; | ||||
|         return iss::Ok; | ||||
|     } | ||||
| }; | ||||
| @@ -2695,11 +2681,12 @@ std::unique_ptr<vm_if> create<arch::tgc5c>(arch::tgc5c *core, unsigned short por | ||||
| } // namespace iss | ||||
|  | ||||
| #include <iss/arch/riscv_hart_m_p.h> | ||||
| #include <iss/arch/riscv_hart_msu_vp.h> | ||||
| #include <iss/arch/riscv_hart_mu_p.h> | ||||
| #include <iss/factory.h> | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
| volatile std::array<bool, 3> dummy = { | ||||
|         core_factory::instance().register_creator("tgc5c|m_p|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::tgc5c>(); | ||||
| 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||
| @@ -2719,8 +2706,18 @@ volatile std::array<bool, 2> dummy = { | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("tgc5c|mus_vp|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_msu_vp<iss::arch::tgc5c>(); | ||||
| 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||
|             if(init_data){ | ||||
|                 auto* cb = reinterpret_cast<semihosting_cb_t<arch::traits<arch::tgc5c>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }) | ||||
| }; | ||||
| } | ||||
| } | ||||
| // clang-format on | ||||
| // clang-format on | ||||
|   | ||||
| @@ -4944,8 +4944,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, BasicBlock *this_block) | ||||
|     // const typename traits::addr_t upper_bits = ~traits::PGMASK; | ||||
|     phys_addr_t paddr(pc); | ||||
|     auto *const data = (uint8_t *)&instr; | ||||
|     if(this->core.has_mmu()) | ||||
|         paddr = this->core.virt2phys(pc); | ||||
|     auto res = this->core.read(paddr, 4, data); | ||||
|     if (res != iss::Ok)  | ||||
|         return std::make_tuple(ILLEGAL_FETCH, nullptr); | ||||
|   | ||||
| @@ -3743,8 +3743,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, tu_builder& tu) { | ||||
|     enum {TRAP_ID=1<<16}; | ||||
|     code_word_t instr = 0; | ||||
|     phys_addr_t paddr(pc); | ||||
|     if(this->core.has_mmu()) | ||||
|         paddr = this->core.virt2phys(pc); | ||||
|     auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr)); | ||||
|     if (res != iss::Ok) | ||||
|         return ILLEGAL_FETCH; | ||||
| @@ -3833,4 +3831,4 @@ volatile std::array<bool, 2> dummy = { | ||||
| }; | ||||
| } | ||||
| } | ||||
| // clang-format on | ||||
| // clang-format on | ||||
|   | ||||
		Reference in New Issue
	
	Block a user