From a35974c9f5d6c9dca98eac016294f1cc84aa7762 Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Sun, 16 May 2021 15:06:42 +0200 Subject: [PATCH] make cpu type in core_complex configurable --- CMakeLists.txt | 24 +- incl/iss/arch/riscv_hart_common.h | 235 ++++++ incl/iss/arch/riscv_hart_m_p.h | 218 +----- incl/iss/arch/riscv_hart_msu_vp.h | 1199 +++++++++++++++++++++++++++++ incl/iss/arch/riscv_hart_mu_p.h | 427 ++++------ incl/sysc/core_complex.h | 14 +- src/sysc/core_complex.cpp | 172 +++-- 7 files changed, 1746 insertions(+), 543 deletions(-) create mode 100644 incl/iss/arch/riscv_hart_common.h create mode 100644 incl/iss/arch/riscv_hart_msu_vp.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 92f4351..f5b9007 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,15 @@ set_target_properties(${PROJECT_NAME} PROPERTIES if(SystemC_FOUND) add_library(${PROJECT_NAME}_sc src/sysc/core_complex.cpp) target_compile_definitions(${PROJECT_NAME}_sc PUBLIC WITH_SYSTEMC) - target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_${CORE_NAME}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_b.h) + target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_B) + endif() + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_c.h) + target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_C) + endif() + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_d.h) + target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_D) + endif() target_include_directories(${PROJECT_NAME}_sc PUBLIC ../incl ${SystemC_INCLUDE_DIRS} ${CCI_INCLUDE_DIRS}) if(SCV_FOUND) @@ -110,17 +118,3 @@ install(TARGETS dbt-core-tgc tgc-sim PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT devel # headers for mac (note the different component -> different package) INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers ) - - - -# -# SYSTEM PACKAGING (RPM, TGZ, ...) -# _____________________________________________________________________________ - -#include(CPackConfig) - -# -# CMAKE PACKAGING (for other CMake projects to use this one easily) -# _____________________________________________________________________________ - -#include(PackageConfigurator) \ No newline at end of file diff --git a/incl/iss/arch/riscv_hart_common.h b/incl/iss/arch/riscv_hart_common.h new file mode 100644 index 0000000..ee87b45 --- /dev/null +++ b/incl/iss/arch/riscv_hart_common.h @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright (C) 2017, 2018, 2021 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 _RISCV_HART_COMMON +#define _RISCV_HART_COMMON + +#include "iss/arch_if.h" +#include + +namespace iss { +namespace arch { + +enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; + +enum riscv_csr { + /* user-level CSR */ + // User Trap Setup + ustatus = 0x000, + uie = 0x004, + utvec = 0x005, + // User Trap Handling + uscratch = 0x040, + uepc = 0x041, + ucause = 0x042, + utval = 0x043, + uip = 0x044, + // User Floating-Point CSRs + fflags = 0x001, + frm = 0x002, + fcsr = 0x003, + // User Counter/Timers + cycle = 0xC00, + time = 0xC01, + instret = 0xC02, + hpmcounter3 = 0xC03, + hpmcounter4 = 0xC04, + /*...*/ + hpmcounter31 = 0xC1F, + cycleh = 0xC80, + timeh = 0xC81, + instreth = 0xC82, + hpmcounter3h = 0xC83, + hpmcounter4h = 0xC84, + /*...*/ + hpmcounter31h = 0xC9F, + /* supervisor-level CSR */ + // Supervisor Trap Setup + sstatus = 0x100, + sedeleg = 0x102, + sideleg = 0x103, + sie = 0x104, + stvec = 0x105, + scounteren = 0x106, + // Supervisor Trap Handling + sscratch = 0x140, + sepc = 0x141, + scause = 0x142, + stval = 0x143, + sip = 0x144, + // Supervisor Protection and Translation + satp = 0x180, + /* machine-level CSR */ + // Machine Information Registers + mvendorid = 0xF11, + marchid = 0xF12, + mimpid = 0xF13, + mhartid = 0xF14, + // Machine Trap Setup + mstatus = 0x300, + misa = 0x301, + medeleg = 0x302, + mideleg = 0x303, + mie = 0x304, + mtvec = 0x305, + mcounteren = 0x306, + // Machine Trap Handling + mscratch = 0x340, + mepc = 0x341, + mcause = 0x342, + mtval = 0x343, + mip = 0x344, + // Physical Memory Protection + pmpcfg0 = 0x3A0, + pmpcfg1 = 0x3A1, + pmpcfg2 = 0x3A2, + pmpcfg3 = 0x3A3, + pmpaddr0 = 0x3B0, + pmpaddr1 = 0x3B1, + pmpaddr2 = 0x3B2, + pmpaddr3 = 0x3B3, + pmpaddr4 = 0x3B4, + pmpaddr5 = 0x3B5, + pmpaddr6 = 0x3B6, + pmpaddr7 = 0x3B7, + pmpaddr8 = 0x3B8, + pmpaddr9 = 0x3B9, + pmpaddr10 = 0x3BA, + pmpaddr11 = 0x3BB, + pmpaddr12 = 0x3BC, + pmpaddr13 = 0x3BD, + pmpaddr14 = 0x3BE, + pmpaddr15 = 0x3BF, + // Machine Counter/Timers + mcycle = 0xB00, + minstret = 0xB02, + mhpmcounter3 = 0xB03, + mhpmcounter4 = 0xB04, + /*...*/ + mhpmcounter31 = 0xB1F, + mcycleh = 0xB80, + minstreth = 0xB82, + mhpmcounter3h = 0xB83, + mhpmcounter4h = 0xB84, + /*...*/ + mhpmcounter31h = 0xB9F, + // Machine Counter Setup + mhpmevent3 = 0x323, + mhpmevent4 = 0x324, + /*...*/ + mhpmevent31 = 0x33F, + // Debug/Trace Registers (shared with Debug Mode) + tselect = 0x7A0, + tdata1 = 0x7A1, + tdata2 = 0x7A2, + tdata3 = 0x7A3, + // Debug Mode Registers + dcsr = 0x7B0, + dpc = 0x7B1, + dscratch = 0x7B2 +}; + + +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 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 }; + +enum { + ISA_A = 1, + ISA_B = 1 << 1, + ISA_C = 1 << 2, + ISA_D = 1 << 3, + ISA_E = 1 << 4, + ISA_F = 1 << 5, + ISA_G = 1 << 6, + ISA_I = 1 << 8, + ISA_M = 1 << 12, + ISA_N = 1 << 13, + ISA_Q = 1 << 16, + ISA_S = 1 << 18, + ISA_U = 1 << 20 +}; + +struct vm_info { + int levels; + int idxbits; + int ptesize; + uint64_t ptbase; + bool is_active() { return levels; } +}; + +class trap_load_access_fault : public trap_access { +public: + trap_load_access_fault(uint64_t badaddr) + : trap_access(5 << 16, badaddr) {} +}; +class illegal_instruction_fault : public trap_access { +public: + illegal_instruction_fault(uint64_t badaddr) + : trap_access(2 << 16, badaddr) {} +}; +class trap_instruction_page_fault : public trap_access { +public: + trap_instruction_page_fault(uint64_t badaddr) + : trap_access(12 << 16, badaddr) {} +}; +class trap_load_page_fault : public trap_access { +public: + trap_load_page_fault(uint64_t badaddr) + : trap_access(13 << 16, badaddr) {} +}; +class trap_store_page_fault : public trap_access { +public: + trap_store_page_fault(uint64_t badaddr) + : trap_access(15 << 16, badaddr) {} +}; +} +} + +#endif diff --git a/incl/iss/arch/riscv_hart_m_p.h b/incl/iss/arch/riscv_hart_m_p.h index 8b0b594..a0230f9 100644 --- a/incl/iss/arch/riscv_hart_m_p.h +++ b/incl/iss/arch/riscv_hart_m_p.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2017, 2018, MINRES Technologies GmbH + * Copyright (C) 2021, MINRES Technologies GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,11 +32,11 @@ * eyck@minres.com - initial implementation ******************************************************************************/ -#ifndef _RISCV_CORE_H_ -#define _RISCV_CORE_H_ +#ifndef _RISCV_HART_M_P_H +#define _RISCV_HART_M_P_H +#include "riscv_hart_common.h" #include "iss/arch/traits.h" -#include "iss/arch_if.h" #include "iss/instrumentation_if.h" #include "iss/log_categories.h" #include "iss/vm_if.h" @@ -66,185 +66,30 @@ namespace iss { namespace arch { -enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; - -enum riscv_csr { - /* user-level CSR */ - // User Trap Setup - ustatus = 0x000, - uie = 0x004, - utvec = 0x005, - // User Trap Handling - uscratch = 0x040, - uepc = 0x041, - ucause = 0x042, - utval = 0x043, - uip = 0x044, - // User Floating-Point CSRs - fflags = 0x001, - frm = 0x002, - fcsr = 0x003, - // User Counter/Timers - cycle = 0xC00, - time = 0xC01, - instret = 0xC02, - hpmcounter3 = 0xC03, - hpmcounter4 = 0xC04, - /*...*/ - hpmcounter31 = 0xC1F, - cycleh = 0xC80, - timeh = 0xC81, - instreth = 0xC82, - hpmcounter3h = 0xC83, - hpmcounter4h = 0xC84, - /*...*/ - hpmcounter31h = 0xC9F, - /* supervisor-level CSR */ - // Supervisor Trap Setup - sstatus = 0x100, - sedeleg = 0x102, - sideleg = 0x103, - sie = 0x104, - stvec = 0x105, - scounteren = 0x106, - // Supervisor Trap Handling - sscratch = 0x140, - sepc = 0x141, - scause = 0x142, - stval = 0x143, - sip = 0x144, - // Supervisor Protection and Translation - satp = 0x180, - /* machine-level CSR */ - // Machine Information Registers - mvendorid = 0xF11, - marchid = 0xF12, - mimpid = 0xF13, - mhartid = 0xF14, - // Machine Trap Setup - mstatus = 0x300, - misa = 0x301, - medeleg = 0x302, - mideleg = 0x303, - mie = 0x304, - mtvec = 0x305, - mcounteren = 0x306, - // Machine Trap Handling - mscratch = 0x340, - mepc = 0x341, - mcause = 0x342, - mtval = 0x343, - mip = 0x344, - // Machine Protection and Translation - pmpcfg0 = 0x3A0, - pmpcfg1 = 0x3A1, - pmpcfg2 = 0x3A2, - pmpcfg3 = 0x3A3, - pmpaddr0 = 0x3B0, - pmpaddr1 = 0x3B1, - /*...*/ - pmpaddr15 = 0x3BF, - // Machine Counter/Timers - mcycle = 0xB00, - minstret = 0xB02, - mhpmcounter3 = 0xB03, - mhpmcounter4 = 0xB04, - /*...*/ - mhpmcounter31 = 0xB1F, - mcycleh = 0xB80, - minstreth = 0xB82, - mhpmcounter3h = 0xB83, - mhpmcounter4h = 0xB84, - /*...*/ - mhpmcounter31h = 0xB9F, - // Machine Counter Setup - mhpmevent3 = 0x323, - mhpmevent4 = 0x324, - /*...*/ - mhpmevent31 = 0x33F, - // Debug/Trace Registers (shared with Debug Mode) - tselect = 0x7A0, - tdata1 = 0x7A1, - tdata2 = 0x7A2, - tdata3 = 0x7A3, - // Debug Mode Registers - dcsr = 0x7B0, - dpc = 0x7B1, - dscratch = 0x7B2 -}; - -namespace { - -std::array trap_str = {{"" - "Instruction address misaligned", // 0 - "Instruction access fault", // 1 - "Illegal instruction", // 2 - "Breakpoint", // 3 - "Load address misaligned", // 4 - "Load access fault", // 5 - "Store/AMO address misaligned", // 6 - "Store/AMO access fault", // 7 - "Environment call from U-mode", // 8 - "Environment call from S-mode", // 9 - "Reserved", // a - "Environment call from M-mode", // b - "Instruction page fault", // c - "Load page fault", // d - "Reserved", // e - "Store/AMO page fault"}}; -std::array irq_str = { - {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", - "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", - "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; - -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 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 }; - -enum { - ISA_A = 1, - ISA_B = 1 << 1, - ISA_C = 1 << 2, - ISA_D = 1 << 3, - ISA_E = 1 << 4, - ISA_F = 1 << 5, - ISA_G = 1 << 6, - ISA_I = 1 << 8, - ISA_M = 1 << 12, - ISA_N = 1 << 13, - ISA_Q = 1 << 16, - ISA_S = 1 << 18, - ISA_U = 1 << 20 -}; - -class trap_load_access_fault : public trap_access { -public: - trap_load_access_fault(uint64_t badaddr) - : trap_access(5 << 16, badaddr) {} -}; -class illegal_instruction_fault : public trap_access { -public: - illegal_instruction_fault(uint64_t badaddr) - : trap_access(2 << 16, badaddr) {} -}; -} // namespace - template class riscv_hart_m_p : public BASE { +protected: + const std::array lvl = {{'U', 'S', 'H', 'M'}}; + const std::array trap_str = {{"" + "Instruction address misaligned", // 0 + "Instruction access fault", // 1 + "Illegal instruction", // 2 + "Breakpoint", // 3 + "Load address misaligned", // 4 + "Load access fault", // 5 + "Store/AMO address misaligned", // 6 + "Store/AMO access fault", // 7 + "Environment call from U-mode", // 8 + "Environment call from S-mode", // 9 + "Reserved", // a + "Environment call from M-mode", // b + "Instruction page fault", // c + "Load page fault", // d + "Reserved", // e + "Store/AMO page fault"}}; + const std::array irq_str = { + {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", + "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", + "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; public: using super = BASE; using this_class = riscv_hart_m_p; @@ -313,6 +158,7 @@ public: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported } }; + using hart_state_type = hart_state; constexpr reg_t get_irq_mask() { return 0b101110111011; // only machine mode is supported @@ -387,7 +233,7 @@ protected: virtual iss::status read_csr(unsigned addr, reg_t &val); virtual iss::status write_csr(unsigned addr, reg_t val); - hart_state state; + hart_state_type state; uint64_t cycle_offset; reg_t fault_data; uint64_t tohost = tohost_dflt; @@ -729,7 +575,7 @@ template iss::status riscv_hart_m_p::read_time(unsigned ad } template iss::status riscv_hart_m_p::read_status(unsigned addr, reg_t &val) { - val = state.mstatus & hart_state::get_mask(); + val = state.mstatus & hart_state_type::get_mask(); return iss::Ok; } @@ -876,7 +722,7 @@ iss::status riscv_hart_m_p::write_mem(phys_addr_t paddr, unsigned length, template inline void riscv_hart_m_p::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state::mstatus_reset_val; + state.mstatus = hart_state_type::mstatus_reset_val; } template void riscv_hart_m_p::check_interrupt() { @@ -958,4 +804,4 @@ template uint64_t riscv_hart_m_p::leave_trap(uint64_t flag } // namespace arch } // namespace iss -#endif /* _RISCV_CORE_H_ */ +#endif /* _RISCV_HART_M_P_H */ diff --git a/incl/iss/arch/riscv_hart_msu_vp.h b/incl/iss/arch/riscv_hart_msu_vp.h new file mode 100644 index 0000000..0c86afe --- /dev/null +++ b/incl/iss/arch/riscv_hart_msu_vp.h @@ -0,0 +1,1199 @@ +/******************************************************************************* + * Copyright (C) 2017, 2018, 2021 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 _RISCV_HART_MSU_VP_H +#define _RISCV_HART_MSU_VP_H + +#include "riscv_hart_common.h" +#include "iss/arch/traits.h" +#include "iss/instrumentation_if.h" +#include "iss/log_categories.h" +#include "iss/vm_if.h" +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +namespace iss { +namespace arch { + +template class riscv_hart_msu_vp : public BASE { +protected: + const std::array lvl = {{'U', 'S', 'H', 'M'}}; + const std::array trap_str = {{"" + "Instruction address misaligned", // 0 + "Instruction access fault", // 1 + "Illegal instruction", // 2 + "Breakpoint", // 3 + "Load address misaligned", // 4 + "Load access fault", // 5 + "Store/AMO address misaligned", // 6 + "Store/AMO access fault", // 7 + "Environment call from U-mode", // 8 + "Environment call from S-mode", // 9 + "Reserved", // a + "Environment call from M-mode", // b + "Instruction page fault", // c + "Load page fault", // d + "Reserved", // e + "Store/AMO page fault"}}; + const std::array irq_str = { + {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", + "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", + "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; +public: + using super = BASE; + using this_class = riscv_hart_msu_vp; + using virt_addr_t = typename super::virt_addr_t; + using phys_addr_t = typename super::phys_addr_t; + using reg_t = typename super::reg_t; + using addr_t = typename super::addr_t; + + using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &); + using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t); + + // primary template + template struct hart_state {}; + // specialization 32bit + template class hart_state::value>::type> { + public: + BEGIN_BF_DECL(mstatus_t, T); + // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11))) + BF_FIELD(SD, 31, 1); + // Trap SRET + BF_FIELD(TSR, 22, 1); + // Timeout Wait + BF_FIELD(TW, 21, 1); + // Trap Virtual Memory + BF_FIELD(TVM, 20, 1); + // Make eXecutable Readable + BF_FIELD(MXR, 19, 1); + // permit Supervisor User Memory access + BF_FIELD(SUM, 18, 1); + // Modify PRiVilege + BF_FIELD(MPRV, 17, 1); + // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty + BF_FIELD(XS, 15, 2); + // floating-point unit status Off/Initial/Clean/Dirty + BF_FIELD(FS, 13, 2); + // machine previous privilege + BF_FIELD(MPP, 11, 2); + // supervisor previous privilege + BF_FIELD(SPP, 8, 1); + // previous machine interrupt-enable + BF_FIELD(MPIE, 7, 1); + // previous supervisor interrupt-enable + BF_FIELD(SPIE, 5, 1); + // previous user interrupt-enable + BF_FIELD(UPIE, 4, 1); + // machine interrupt-enable + BF_FIELD(MIE, 3, 1); + // supervisor interrupt-enable + BF_FIELD(SIE, 1, 1); + // user interrupt-enable + BF_FIELD(UIE, 0, 1); + END_BF_DECL(); + + mstatus_t mstatus; + + static const reg_t mstatus_reset_val = 0; + + void write_mstatus(T val, unsigned priv_lvl) { + auto mask = get_mask(priv_lvl); + auto new_val = (mstatus.st.value & ~mask) | (val & mask); + mstatus = new_val; + } + + T satp; + + static constexpr T get_misa() { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; } + + static constexpr uint32_t get_mask(unsigned priv_lvl) { +#if __cplusplus < 201402L + return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL; +#else + switch (priv_lvl) { + case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001 + case PRIV_S: return 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011 + default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 + } +#endif + } + + static inline vm_info decode_vm_info(uint32_t state, T sptbr) { + if (state == PRIV_M) return {0, 0, 0, 0}; + if (state <= PRIV_S) + switch (bit_sub<31, 1>(sptbr)) { + case 0: return {0, 0, 0, 0}; // off + case 1: return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32 + default: abort(); + } + abort(); + return {0, 0, 0, 0}; // dummy + } + }; + // specialization 64bit + template class hart_state::value>::type> { + public: + BEGIN_BF_DECL(mstatus_t, T); + // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11))) + BF_FIELD(SD, 63, 1); + // value of XLEN for S-mode + BF_FIELD(SXL, 34, 2); + // value of XLEN for U-mode + BF_FIELD(UXL, 32, 2); + // Trap SRET + BF_FIELD(TSR, 22, 1); + // Timeout Wait + BF_FIELD(TW, 21, 1); + // Trap Virtual Memory + BF_FIELD(TVM, 20, 1); + // Make eXecutable Readable + BF_FIELD(MXR, 19, 1); + // permit Supervisor User Memory access + BF_FIELD(SUM, 18, 1); + // Modify PRiVilege + BF_FIELD(MPRV, 17, 1); + // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty + BF_FIELD(XS, 15, 2); + // floating-point unit status Off/Initial/Clean/Dirty + BF_FIELD(FS, 13, 2); + // machine previous privilege + BF_FIELD(MPP, 11, 2); + // supervisor previous privilege + BF_FIELD(SPP, 8, 1); + // previous machine interrupt-enable + BF_FIELD(MPIE, 7, 1); + // previous supervisor interrupt-enable + BF_FIELD(SPIE, 5, 1); + // previous user interrupt-enable + BF_FIELD(UPIE, 4, 1); + // machine interrupt-enable + BF_FIELD(MIE, 3, 1); + // supervisor interrupt-enable + BF_FIELD(SIE, 1, 1); + // user interrupt-enable + BF_FIELD(UIE, 0, 1); + END_BF_DECL(); + + mstatus_t mstatus; + + static const reg_t mstatus_reset_val = 0xa00000000; + + void write_mstatus(T val, unsigned priv_lvl) { + T old_val = mstatus; + auto mask = get_mask(priv_lvl); + auto new_val = (old_val & ~mask) | (val & mask); + if ((new_val & mstatus.SXL.Mask) == 0) { + new_val |= old_val & mstatus.SXL.Mask; + } + if ((new_val & mstatus.UXL.Mask) == 0) { + new_val |= old_val & mstatus.UXL.Mask; + } + mstatus = new_val; + } + + T satp; + + static constexpr T get_misa() { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; } + + static constexpr T get_mask(unsigned priv_lvl) { + uint64_t ret; + switch (priv_lvl) { + case PRIV_U: ret = 0x8000000f00000011ULL;break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 + case PRIV_S: ret = 0x8000000f000de133ULL;break; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011 + default: ret = 0x8000000f007ff9ddULL;break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011 + } + return ret; + } + + static inline vm_info decode_vm_info(uint32_t state, T sptbr) { + if (state == PRIV_M) return {0, 0, 0, 0}; + if (state <= PRIV_S) + switch (bit_sub<60, 4>(sptbr)) { + case 0: return {0, 0, 0, 0}; // off + case 8: return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV39 + case 9: return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV48 + case 10: return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV57 + case 11: return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV64 + default: abort(); + } + abort(); + return {0, 0, 0, 0}; // dummy + } + }; + using hart_state_type = hart_state; + + const typename super::reg_t PGSIZE = 1 << PGSHIFT; + const typename super::reg_t PGMASK = PGSIZE - 1; + + constexpr reg_t get_irq_mask(size_t mode) { + std::array m = {{ + 0b000100010001, // U mode + 0b001100110011, // S mode + 0, + 0b101110111011 // M mode + }}; + return m[mode]; + } + + riscv_hart_msu_vp(); + virtual ~riscv_hart_msu_vp() = default; + + void reset(uint64_t address) override; + + std::pair load_file(std::string name, int type = -1) override; + + virtual phys_addr_t virt2phys(const iss::addr_t &addr) override; + + iss::status read(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, uint8_t *const data) override; + iss::status write(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, const uint8_t *const data) override; + + virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_msu_vp::enter_trap(flags, fault_data); } + virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override; + virtual uint64_t leave_trap(uint64_t flags) override; + void wait_until(uint64_t flags) override; + + void disass_output(uint64_t pc, const std::string instr) override { + CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]", + pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.icount); + }; + + iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; } + +protected: + struct riscv_instrumentation_if : public iss::instrumentation_if { + + riscv_instrumentation_if(riscv_hart_msu_vp &arch) + : arch(arch) {} + /** + * get the name of this architecture + * + * @return the name of this architecture + */ + const std::string core_type_name() const override { return traits::core_type; } + + virtual uint64_t get_pc() { return arch.get_pc(); }; + + virtual uint64_t get_next_pc() { return arch.get_next_pc(); }; + + virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; }; + + riscv_hart_msu_vp &arch; + }; + + friend struct riscv_instrumentation_if; + addr_t get_pc() { return this->reg.PC; } + addr_t get_next_pc() { return this->reg.NEXT_PC; } + + virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data); + virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data); + + virtual iss::status read_csr(unsigned addr, reg_t &val); + virtual iss::status write_csr(unsigned addr, reg_t val); + + hart_state_type state; + uint64_t cycle_offset; + reg_t fault_data; + std::array vm; + uint64_t tohost = tohost_dflt; + uint64_t fromhost = fromhost_dflt; + unsigned to_host_wr_cnt = 0; + riscv_instrumentation_if instr_if; + + using mem_type = util::sparse_array; + using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; + using csr_page_type = typename csr_type::page_type; + mem_type mem; + csr_type csr; + void update_vm_info(); + std::stringstream uart_buf; + std::unordered_map ptw; + std::unordered_map atomic_reservation; + std::unordered_map csr_rd_cb; + std::unordered_map csr_wr_cb; + +private: + iss::status read_cycle(unsigned addr, reg_t &val); + iss::status read_time(unsigned addr, reg_t &val); + iss::status read_status(unsigned addr, reg_t &val); + iss::status write_status(unsigned addr, reg_t val); + iss::status read_ie(unsigned addr, reg_t &val); + iss::status write_ie(unsigned addr, reg_t val); + iss::status read_ip(unsigned addr, reg_t &val); + iss::status write_ip(unsigned addr, reg_t val); + iss::status read_satp(unsigned addr, reg_t &val); + iss::status write_satp(unsigned addr, reg_t val); + iss::status read_fcsr(unsigned addr, reg_t &val); + iss::status write_fcsr(unsigned addr, reg_t val); + +protected: + void check_interrupt(); +}; + +template +riscv_hart_msu_vp::riscv_hart_msu_vp() +: state() +, cycle_offset(0) +, instr_if(*this) { + csr[misa] = hart_state_type::get_misa(); + uart_buf.str(""); + // read-only registers + csr_wr_cb[misa] = nullptr; + for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr; + for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr; + // special handling + csr_rd_cb[time] = &this_class::read_time; + csr_wr_cb[time] = nullptr; + csr_rd_cb[timeh] = &this_class::read_time; + csr_wr_cb[timeh] = nullptr; + csr_rd_cb[mcycle] = &this_class::read_cycle; + csr_rd_cb[mcycleh] = &this_class::read_cycle; + csr_rd_cb[minstret] = &this_class::read_cycle; + csr_rd_cb[minstreth] = &this_class::read_cycle; + csr_rd_cb[mstatus] = &this_class::read_status; + csr_wr_cb[mstatus] = &this_class::write_status; + csr_rd_cb[sstatus] = &this_class::read_status; + csr_wr_cb[sstatus] = &this_class::write_status; + csr_rd_cb[ustatus] = &this_class::read_status; + csr_wr_cb[ustatus] = &this_class::write_status; + csr_rd_cb[mip] = &this_class::read_ip; + csr_wr_cb[mip] = &this_class::write_ip; + csr_rd_cb[sip] = &this_class::read_ip; + csr_wr_cb[sip] = &this_class::write_ip; + csr_rd_cb[uip] = &this_class::read_ip; + csr_wr_cb[uip] = &this_class::write_ip; + csr_rd_cb[mie] = &this_class::read_ie; + csr_wr_cb[mie] = &this_class::write_ie; + csr_rd_cb[sie] = &this_class::read_ie; + csr_wr_cb[sie] = &this_class::write_ie; + csr_rd_cb[uie] = &this_class::read_ie; + csr_wr_cb[uie] = &this_class::write_ie; + csr_rd_cb[satp] = &this_class::read_satp; + csr_wr_cb[satp] = &this_class::write_satp; + csr_rd_cb[fcsr] = &this_class::read_fcsr; + csr_wr_cb[fcsr] = &this_class::write_fcsr; + csr_rd_cb[fflags] = &this_class::read_fcsr; + csr_wr_cb[fflags] = &this_class::write_fcsr; + csr_rd_cb[frm] = &this_class::read_fcsr; + csr_wr_cb[frm] = &this_class::write_fcsr; +} + +template std::pair riscv_hart_msu_vp::load_file(std::string name, int type) { + FILE *fp = fopen(name.c_str(), "r"); + if (fp) { + std::array buf; + auto n = fread(buf.data(), 1, 4, fp); + if (n != 4) throw std::runtime_error("input file has insufficient size"); + buf[4] = 0; + if (strcmp(buf.data() + 1, "ELF") == 0) { + fclose(fp); + // Create elfio reader + ELFIO::elfio reader; + // Load ELF data + if (!reader.load(name)) throw std::runtime_error("could not process elf file"); + // check elf properties + if (reader.get_class() != ELFCLASS32) + if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file"); + if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file"); + if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file"); + for (const auto pseg : reader.segments) { + const auto fsize = pseg->get_file_size(); // 0x42c/0x0 + const auto seg_data = pseg->get_data(); + if (fsize > 0) { + auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, + traits::MEM, pseg->get_physical_address(), + fsize, reinterpret_cast(seg_data)); + if (res != iss::Ok) + LOG(ERROR) << "problem writing " << fsize << "bytes to 0x" << std::hex + << pseg->get_physical_address(); + } + } + for (const auto sec : reader.sections) { + if (sec->get_name() == ".tohost") { + tohost = sec->get_address(); + fromhost = tohost + 0x40; + } + } + + return std::make_pair(reader.get_entry(), true); + } + throw std::runtime_error("memory load file is not a valid elf file"); + } + throw std::runtime_error("memory load file not found"); +} + +template +iss::status riscv_hart_msu_vp::read(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, uint8_t *const data) { +#ifndef NDEBUG + if (access && iss::access_type::DEBUG) { + LOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr; + } else if(access && iss::access_type::FETCH){ + LOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr; + } else { + LOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr; + } +#endif + try { + switch (space) { + case traits::MEM: { + if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31); // issue trap 0 + return iss::Err; + } + try { + if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary + vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + if (vm.levels != 0) { // VM is active + auto split_addr = (addr + length) & ~PGMASK; + auto len1 = split_addr - addr; + auto res = read(type, access, space, addr, len1, data); + if (res == iss::Ok) + res = read(type, access, space, split_addr, length - len1, data + len1); + return res; + } + } + auto res = type==iss::address_type::PHYSICAL? + read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data): + read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); + if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault + return res; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } + } break; + case traits::CSR: { + if (length != sizeof(reg_t)) return iss::Err; + return read_csr(addr, *reinterpret_cast(data)); + } break; + case traits::FENCE: { + if ((addr + length) > mem.size()) return iss::Err; + switch (addr) { + case 2: // SFENCE:VMA lower + case 3: { // SFENCE:VMA upper + auto tvm = state.mstatus.TVM; + if (this->reg.PRIV == PRIV_S & tvm != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + return iss::Err; + } + return iss::Ok; + } + } + } break; + case traits::RES: { + auto it = atomic_reservation.find(addr); + if (it != atomic_reservation.end() && it->second != 0) { + memset(data, 0xff, length); + atomic_reservation.erase(addr); + } else + memset(data, 0, length); + } break; + default: + return iss::Err; // assert("Not supported"); + } + return iss::Ok; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } +} + +template +iss::status riscv_hart_msu_vp::write(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, const uint8_t *const data) { +#ifndef NDEBUG + const char *prefix = (access && iss::access_type::DEBUG) ? "debug " : ""; + switch (length) { + case 8: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint64_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 4: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint32_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 2: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint16_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 1: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << (uint16_t)data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + default: + LOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; + } +#endif + try { + switch (space) { + case traits::MEM: { + if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31); // issue trap 0 + return iss::Err; + } + try { + if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary + vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + if (vm.levels != 0) { // VM is active + auto split_addr = (addr + length) & ~PGMASK; + auto len1 = split_addr - addr; + auto res = write(type, access, space, addr, len1, data); + if (res == iss::Ok) + res = write(type, access, space, split_addr, length - len1, data + len1); + return res; + } + } + auto res = type==iss::address_type::PHYSICAL? + write_mem(phys_addr_t{access, space, addr}, length, data): + write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); + if (unlikely(res != iss::Ok)) + this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault) + return res; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } + + phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr}); + if ((paddr.val + length) > mem.size()) return iss::Err; + switch (paddr.val) { + case 0x10013000: // UART0 base, TXFIFO reg + case 0x10023000: // UART1 base, TXFIFO reg + uart_buf << (char)data[0]; + if (((char)data[0]) == '\n' || data[0] == 0) { + // LOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send + // '"<::CSR: { + if (length != sizeof(reg_t)) return iss::Err; + return write_csr(addr, *reinterpret_cast(data)); + } break; + case traits::FENCE: { + if ((addr + length) > mem.size()) return iss::Err; + switch (addr) { + case 2: + case 3: { + ptw.clear(); + auto tvm = state.mstatus.TVM; + if (this->reg.PRIV == PRIV_S & tvm != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + return iss::Err; + } + return iss::Ok; + } + } + } break; + case traits::RES: { + atomic_reservation[addr] = data[0]; + } break; + default: + return iss::Err; + } + return iss::Ok; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } +} + +template iss::status riscv_hart_msu_vp::read_csr(unsigned addr, reg_t &val) { + if (addr >= csr.size()) return iss::Err; + auto req_priv_lvl = (addr >> 8) & 0x3; + if (this->reg.PRIV < req_priv_lvl) throw illegal_instruction_fault(this->fault_data); + auto it = csr_rd_cb.find(addr); + if (it == csr_rd_cb.end()) { + val = csr[addr & csr.page_addr_mask]; + return iss::Ok; + } + rd_csr_f f = it->second; + if (f == nullptr) throw illegal_instruction_fault(this->fault_data); + return (this->*f)(addr, val); +} + +template iss::status riscv_hart_msu_vp::write_csr(unsigned addr, reg_t val) { + if (addr >= csr.size()) return iss::Err; + auto req_priv_lvl = (addr >> 8) & 0x3; + if (this->reg.PRIV < req_priv_lvl) + throw illegal_instruction_fault(this->fault_data); + if((addr&0xc00)==0xc00) + throw illegal_instruction_fault(this->fault_data); + auto it = csr_wr_cb.find(addr); + if (it == csr_wr_cb.end()) { + csr[addr & csr.page_addr_mask] = val; + return iss::Ok; + } + wr_csr_f f = it->second; + if (f == nullptr) throw illegal_instruction_fault(this->fault_data); + return (this->*f)(addr, val); +} + +template iss::status riscv_hart_msu_vp::read_cycle(unsigned addr, reg_t &val) { + auto cycle_val = this->reg.icount + cycle_offset; + if (addr == mcycle) { + val = static_cast(cycle_val); + } else if (addr == mcycleh) { + if (sizeof(typename traits::reg_t) != 4) return iss::Err; + val = static_cast(cycle_val >> 32); + } + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::read_time(unsigned addr, reg_t &val) { + uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052; + if (addr == time) { + val = static_cast(time_val); + } else if (addr == timeh) { + if (sizeof(typename traits::reg_t) != 4) return iss::Err; + val = static_cast(time_val >> 32); + } + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::read_status(unsigned addr, reg_t &val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + val = state.mstatus & hart_state_type::get_mask(req_priv_lvl); + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::write_status(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + state.write_mstatus(val, req_priv_lvl); + check_interrupt(); + update_vm_info(); + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::read_ie(unsigned addr, reg_t &val) { + val = csr[mie]; + if (addr < mie) val &= csr[mideleg]; + if (addr < sie) val &= csr[sideleg]; + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::write_ie(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + auto mask = get_irq_mask(req_priv_lvl); + csr[mie] = (csr[mie] & ~mask) | (val & mask); + check_interrupt(); + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::read_ip(unsigned addr, reg_t &val) { + val = csr[mip]; + if (addr < mip) val &= csr[mideleg]; + if (addr < sip) val &= csr[sideleg]; + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::write_ip(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + auto mask = get_irq_mask(req_priv_lvl); + mask &= ~(1 << 7); // MTIP is read only + csr[mip] = (csr[mip] & ~mask) | (val & mask); + check_interrupt(); + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::read_satp(unsigned addr, reg_t &val) { + reg_t tvm = state.mstatus.TVM; + if (this->reg.PRIV == PRIV_S & tvm != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + return iss::Err; + } + val = state.satp; + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::write_satp(unsigned addr, reg_t val) { + reg_t tvm = state.mstatus.TVM; + if (this->reg.PRIV == PRIV_S & tvm != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + return iss::Err; + } + state.satp = val; + update_vm_info(); + return iss::Ok; +} +template iss::status riscv_hart_msu_vp::read_fcsr(unsigned addr, reg_t &val) { + switch (addr) { + case 1: // fflags, 4:0 + val = bit_sub<0, 5>(this->get_fcsr()); + break; + case 2: // frm, 7:5 + val = bit_sub<5, 3>(this->get_fcsr()); + break; + case 3: // fcsr + val = this->get_fcsr(); + break; + default: + return iss::Err; + } + return iss::Ok; +} + +template iss::status riscv_hart_msu_vp::write_fcsr(unsigned addr, reg_t val) { + switch (addr) { + case 1: // fflags, 4:0 + this->set_fcsr((this->get_fcsr() & 0xffffffe0) | (val & 0x1f)); + break; + case 2: // frm, 7:5 + this->set_fcsr((this->get_fcsr() & 0xffffff1f) | ((val & 0x7) << 5)); + break; + case 3: // fcsr + this->set_fcsr(val & 0xff); + break; + default: + return iss::Err; + } + return iss::Ok; +} + +template +iss::status riscv_hart_msu_vp::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) { + if ((paddr.val + length) > mem.size()) return iss::Err; + switch (paddr.val) { + case 0x0200BFF8: { // CLINT base, mtime reg + if (sizeof(reg_t) < length) return iss::Err; + reg_t time_val; + this->read_csr(time, time_val); + std::copy((uint8_t *)&time_val, ((uint8_t *)&time_val) + length, data); + } break; + case 0x10008000: { + const mem_type::page_type &p = mem(paddr.val / mem.page_size); + uint64_t offs = paddr.val & mem.page_addr_mask; + std::copy(p.data() + offs, p.data() + offs + length, data); + if (this->reg.icount > 30000) data[3] |= 0x80; + } break; + default: { + const auto &p = mem(paddr.val / mem.page_size); + auto offs = paddr.val & mem.page_addr_mask; + std::copy(p.data() + offs, p.data() + offs + length, data); + } + } + return iss::Ok; +} + +template +iss::status riscv_hart_msu_vp::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) { + if ((paddr.val + length) > mem.size()) return iss::Err; + switch (paddr.val) { + case 0x10013000: // UART0 base, TXFIFO reg + case 0x10023000: // UART1 base, TXFIFO reg + uart_buf << (char)data[0]; + if (((char)data[0]) == '\n' || data[0] == 0) { + // LOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send + // '"<::XLEN == 32 && paddr.val == (tohost + 4)) || + (traits::XLEN == 64 && paddr.val == tohost); + auto tohost_lower = + (traits::XLEN == 32 && paddr.val == tohost) || (traits::XLEN == 64 && paddr.val == tohost); + if (tohost_lower || tohost_upper) { + uint64_t hostvar = *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)); + if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { + switch (hostvar >> 48) { + case 0: + if (hostvar != 0x1) { + LOG(FATAL) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar + << "), stopping simulation"; + } else { + LOG(INFO) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar + << "), stopping simulation"; + } + this->reg.trap_state=std::numeric_limits::max(); + this->interrupt_sim=hostvar; + break; + //throw(iss::simulation_stopped(hostvar)); + case 0x0101: { + char c = static_cast(hostvar & 0xff); + if (c == '\n' || c == 0) { + LOG(INFO) << "tohost send '" << uart_buf.str() << "'"; + uart_buf.str(""); + } else + uart_buf << c; + to_host_wr_cnt = 0; + } break; + default: + break; + } + } else if (tohost_lower) + to_host_wr_cnt++; + } else if ((traits::XLEN == 32 && paddr.val == fromhost + 4) || + (traits::XLEN == 64 && paddr.val == fromhost)) { + uint64_t fhostvar = *reinterpret_cast(p.data() + (fromhost & mem.page_addr_mask)); + *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; + } + } + } + } + return iss::Ok; +} + +template inline void riscv_hart_msu_vp::reset(uint64_t address) { + BASE::reset(address); + state.mstatus = hart_state_type::mstatus_reset_val; + update_vm_info(); +} + +template inline void riscv_hart_msu_vp::update_vm_info() { + vm[1] = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); + BASE::addr_mode[3]=BASE::addr_mode[2] = vm[1].is_active()? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; + if (state.mstatus.MPRV) + vm[0] = hart_state_type::decode_vm_info(state.mstatus.MPP, state.satp); + else + vm[0] = vm[1]; + BASE::addr_mode[1] = BASE::addr_mode[0]=vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; + ptw.clear(); +} + +template void riscv_hart_msu_vp::check_interrupt() { + auto status = state.mstatus; + auto ip = csr[mip]; + auto ie = csr[mie]; + auto ideleg = csr[mideleg]; + // Multiple simultaneous interrupts and traps at the same privilege level are + // handled in the following decreasing priority order: + // external interrupts, software interrupts, timer interrupts, then finally + // any synchronous traps. + auto ena_irq = ip & ie; + + bool mie = state.mstatus.MIE; + auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie); + auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0; + + if (enabled_interrupts == 0) { + auto sie = state.mstatus.SIE; + auto s_enabled = this->reg.PRIV < PRIV_S || (this->reg.PRIV == PRIV_S && sie); + enabled_interrupts = s_enabled ? ena_irq & ideleg : 0; + } + if (enabled_interrupts != 0) { + int res = 0; + while ((enabled_interrupts & 1) == 0) enabled_interrupts >>= 1, res++; + this->reg.pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id + } +} + +template +typename riscv_hart_msu_vp::phys_addr_t riscv_hart_msu_vp::virt2phys(const iss::addr_t &addr) { + const auto type = addr.access & iss::access_type::FUNC; + auto it = ptw.find(addr.val >> 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 {addr.access, addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)}; + else + ptw.erase(it); // throw an exception +#endif + } else { + uint32_t mode = type != iss::access_type::FETCH && state.mstatus.MPRV ? // MPRV + state.mstatus.MPP : + this->reg.PRIV; + + const vm_info &vm = this->vm[static_cast(type) / 2]; + + const bool s_mode = mode == PRIV_S; + const bool sum = state.mstatus.SUM; + const bool mxr = 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) << (traits::XLEN > -(va_bits - 1))) - 1; + const reg_t masked_msbs = (addr.val >> (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.val >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1); + + // check that physical address of PTE is legal + reg_t pte = 0; + const uint8_t res = this->read(iss::address_type::PHYSICAL, addr.access, + traits::MEM, base + idx * vm.ptesize, vm.ptesize, (uint8_t *)&pte); + if (res != 0) throw trap_load_access_fault(addr.val); + 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 == 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.val >> PGSHIFT; + const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + const reg_t offset = addr.val & PGMASK; + ptw[vpn] = value | (pte & 0xff); + return {addr.access, addr.space, value | offset}; + } + } + } + switch (type) { + case access_type::FETCH: + this->fault_data = addr.val; + throw trap_instruction_page_fault(addr.val); + case access_type::READ: + this->fault_data = addr.val; + throw trap_load_page_fault(addr.val); + case access_type::WRITE: + this->fault_data = addr.val; + throw trap_store_page_fault(addr.val); + default: + abort(); + } +} + +template uint64_t riscv_hart_msu_vp::enter_trap(uint64_t flags, uint64_t addr) { + auto cur_priv = this->reg.PRIV; + // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] + // calculate and write mcause val + auto trap_id = bit_sub<0, 16>(flags); + auto cause = bit_sub<16, 15>(flags); + if (trap_id == 0 && cause == 11) cause = 0x8 + cur_priv; // adjust environment call cause + // calculate effective privilege level + auto new_priv = PRIV_M; + if (trap_id == 0) { // exception + if (cur_priv != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0) + new_priv = (csr[sedeleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; + // store ret addr in xepc register + csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception + /* + * write mtval if new_priv=M_MODE, spec says: + * When a hardware breakpoint is triggered, or an instruction-fetch, load, + * or store address-misaligned, + * access, or page-fault exception occurs, mtval is written with the + * faulting effective address. + */ + csr[utval | (new_priv << 8)] = fault_data; + fault_data = 0; + } else { + if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0) + new_priv = (csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S; + csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt + this->reg.pending_trap = 0; + } + size_t adr = ucause | (new_priv << 8); + csr[adr] = (trap_id << 31) + cause; + // update mstatus + // xPP field of mstatus is written with the active privilege mode at the time + // of the trap; the x PIE field of mstatus + // is written with the value of the active interrupt-enable bit at the time of + // the trap; and the x IE field of mstatus + // is cleared + // store the actual privilege level in yPP and store interrupt enable flags + switch (new_priv) { + case PRIV_M: + state.mstatus.MPP = cur_priv; + state.mstatus.MPIE = state.mstatus.MIE; + state.mstatus.MIE = false; + break; + case PRIV_S: + state.mstatus.SPP = cur_priv; + state.mstatus.SPIE = state.mstatus.SIE; + state.mstatus.SIE = false; + break; + case PRIV_U: + state.mstatus.UPIE = state.mstatus.UIE; + state.mstatus.UIE = false; + break; + default: + break; + } + + // get trap vector + auto ivec = csr[utvec | (new_priv << 8)]; + // calculate addr// set NEXT_PC to trap addressess to jump to based on MODE + // bits in mtvec + this->reg.NEXT_PC = ivec & ~0x1UL; + if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause; + // reset trap state + this->reg.PRIV = new_priv; + this->reg.trap_state = 0; + std::array buffer; + sprintf(buffer.data(), "0x%016lx", addr); + if((flags&0xffffffff) != 0xffffffff) + CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" + << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" + << " at address " << buffer.data() << " occurred, changing privilege level from " + << lvl[cur_priv] << " to " << lvl[new_priv]; + update_vm_info(); + return this->reg.NEXT_PC; +} + +template uint64_t riscv_hart_msu_vp::leave_trap(uint64_t flags) { + auto cur_priv = this->reg.PRIV; + auto inst_priv = flags & 0x3; + auto status = state.mstatus; + + auto tsr = state.mstatus.TSR; + if (cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + return this->reg.PC; + } + + // pop the relevant lower-privilege interrupt enable and privilege mode stack + // clear respective yIE + switch (inst_priv) { + case PRIV_M: + this->reg.PRIV = state.mstatus.MPP; + state.mstatus.MPP = 0; // clear mpp to U mode + state.mstatus.MIE = state.mstatus.MPIE; + break; + case PRIV_S: + this->reg.PRIV = state.mstatus.SPP; + state.mstatus.SPP = 0; // clear spp to U mode + state.mstatus.SIE = state.mstatus.SPIE; + break; + case PRIV_U: + this->reg.PRIV = 0; + state.mstatus.UIE = state.mstatus.UPIE; + break; + } + // sets the pc to the value stored in the x epc register. + this->reg.NEXT_PC = csr[uepc | inst_priv << 8]; + CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " + << lvl[this->reg.PRIV]; + update_vm_info(); + return this->reg.NEXT_PC; +} + +template void riscv_hart_msu_vp::wait_until(uint64_t flags) { + auto status = state.mstatus; + auto tw = status.TW; + if (this->reg.PRIV == PRIV_S && tw != 0) { + this->reg.trap_state = (1 << 31) | (2 << 16); + this->fault_data = this->reg.PC; + } +} +} +} + +#endif /* _RISCV_HART_MSU_VP_H */ diff --git a/incl/iss/arch/riscv_hart_mu_p.h b/incl/iss/arch/riscv_hart_mu_p.h index bb4f2c1..bbf9d64 100644 --- a/incl/iss/arch/riscv_hart_mu_p.h +++ b/incl/iss/arch/riscv_hart_mu_p.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2017, 2018, MINRES Technologies GmbH + * Copyright (C) 2021 MINRES Technologies GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,11 +32,11 @@ * eyck@minres.com - initial implementation ******************************************************************************/ -#ifndef _RISCV_CORE_H_ -#define _RISCV_CORE_H_ +#ifndef _RISCV_HART_MU_P_H +#define _RISCV_HART_MU_P_H +#include "riscv_hart_common.h" #include "iss/arch/traits.h" -#include "iss/arch_if.h" #include "iss/instrumentation_if.h" #include "iss/log_categories.h" #include "iss/vm_if.h" @@ -66,188 +66,33 @@ namespace iss { namespace arch { -enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; - -enum riscv_csr { - /* user-level CSR */ - // User Trap Setup - ustatus = 0x000, - uie = 0x004, - utvec = 0x005, - // User Trap Handling - uscratch = 0x040, - uepc = 0x041, - ucause = 0x042, - utval = 0x043, - uip = 0x044, - // User Floating-Point CSRs - fflags = 0x001, - frm = 0x002, - fcsr = 0x003, - // User Counter/Timers - cycle = 0xC00, - time = 0xC01, - instret = 0xC02, - hpmcounter3 = 0xC03, - hpmcounter4 = 0xC04, - /*...*/ - hpmcounter31 = 0xC1F, - cycleh = 0xC80, - timeh = 0xC81, - instreth = 0xC82, - hpmcounter3h = 0xC83, - hpmcounter4h = 0xC84, - /*...*/ - hpmcounter31h = 0xC9F, - /* supervisor-level CSR */ - // Supervisor Trap Setup - sstatus = 0x100, - sedeleg = 0x102, - sideleg = 0x103, - sie = 0x104, - stvec = 0x105, - scounteren = 0x106, - // Supervisor Trap Handling - sscratch = 0x140, - sepc = 0x141, - scause = 0x142, - stval = 0x143, - sip = 0x144, - // Supervisor Protection and Translation - satp = 0x180, - /* machine-level CSR */ - // Machine Information Registers - mvendorid = 0xF11, - marchid = 0xF12, - mimpid = 0xF13, - mhartid = 0xF14, - // Machine Trap Setup - mstatus = 0x300, - misa = 0x301, - medeleg = 0x302, - mideleg = 0x303, - mie = 0x304, - mtvec = 0x305, - mcounteren = 0x306, - // Machine Trap Handling - mscratch = 0x340, - mepc = 0x341, - mcause = 0x342, - mtval = 0x343, - mip = 0x344, - // Machine Protection and Translation - pmpcfg0 = 0x3A0, - pmpcfg1 = 0x3A1, - pmpcfg2 = 0x3A2, - pmpcfg3 = 0x3A3, - pmpaddr0 = 0x3B0, - pmpaddr1 = 0x3B1, - /*...*/ - pmpaddr15 = 0x3BF, - // Machine Counter/Timers - mcycle = 0xB00, - minstret = 0xB02, - mhpmcounter3 = 0xB03, - mhpmcounter4 = 0xB04, - /*...*/ - mhpmcounter31 = 0xB1F, - mcycleh = 0xB80, - minstreth = 0xB82, - mhpmcounter3h = 0xB83, - mhpmcounter4h = 0xB84, - /*...*/ - mhpmcounter31h = 0xB9F, - // Machine Counter Setup - mhpmevent3 = 0x323, - mhpmevent4 = 0x324, - /*...*/ - mhpmevent31 = 0x33F, - // Debug/Trace Registers (shared with Debug Mode) - tselect = 0x7A0, - tdata1 = 0x7A1, - tdata2 = 0x7A2, - tdata3 = 0x7A3, - // Debug Mode Registers - dcsr = 0x7B0, - dpc = 0x7B1, - dscratch = 0x7B2 -}; - -namespace { - -std::array trap_str = {{"" - "Instruction address misaligned", // 0 - "Instruction access fault", // 1 - "Illegal instruction", // 2 - "Breakpoint", // 3 - "Load address misaligned", // 4 - "Load access fault", // 5 - "Store/AMO address misaligned", // 6 - "Store/AMO access fault", // 7 - "Environment call from U-mode", // 8 - "Environment call from S-mode", // 9 - "Reserved", // a - "Environment call from M-mode", // b - "Instruction page fault", // c - "Load page fault", // d - "Reserved", // e - "Store/AMO page fault"}}; -std::array irq_str = { - {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", - "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", - "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; - -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 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 }; - -enum { - ISA_A = 1, - ISA_B = 1 << 1, - ISA_C = 1 << 2, - ISA_D = 1 << 3, - ISA_E = 1 << 4, - ISA_F = 1 << 5, - ISA_G = 1 << 6, - ISA_I = 1 << 8, - ISA_M = 1 << 12, - ISA_N = 1 << 13, - ISA_Q = 1 << 16, - ISA_S = 1 << 18, - ISA_U = 1 << 20 -}; - -class trap_load_access_fault : public trap_access { -public: - trap_load_access_fault(uint64_t badaddr) - : trap_access(5 << 16, badaddr) {} -}; -class illegal_instruction_fault : public trap_access { -public: - illegal_instruction_fault(uint64_t badaddr) - : trap_access(2 << 16, badaddr) {} -}; -} // namespace - -template class riscv_hart_mu_p : public BASE { +template class riscv_hart_mu_p : public BASE { +protected: + const std::array lvl = {{'U', 'S', 'H', 'M'}}; + const std::array trap_str = {{"" + "Instruction address misaligned", // 0 + "Instruction access fault", // 1 + "Illegal instruction", // 2 + "Breakpoint", // 3 + "Load address misaligned", // 4 + "Load access fault", // 5 + "Store/AMO address misaligned", // 6 + "Store/AMO access fault", // 7 + "Environment call from U-mode", // 8 + "Environment call from S-mode", // 9 + "Reserved", // a + "Environment call from M-mode", // b + "Instruction page fault", // c + "Load page fault", // d + "Reserved", // e + "Store/AMO page fault"}}; + const std::array irq_str = { + {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", + "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", + "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; public: using super = BASE; - using this_class = riscv_hart_mu_p; + using this_class = riscv_hart_mu_p; using phys_addr_t = typename super::phys_addr_t; using reg_t = typename super::reg_t; using addr_t = typename super::addr_t; @@ -301,21 +146,35 @@ public: static const reg_t mstatus_reset_val = 0; - void write_mstatus(T val) { - auto mask = get_mask(); + void write_mstatus(T val, unsigned priv_lvl) { + auto mask = get_mask(priv_lvl); auto new_val = (mstatus.backing.val & ~mask) | (val & mask); mstatus = new_val; } T satp; - static constexpr uint32_t get_mask() { - return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported + static constexpr uint32_t get_mask(unsigned priv_lvl) { +#if __cplusplus < 201402L + return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL; +#else + switch (priv_lvl) { + case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001 + default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 + } +#endif } }; + using hart_state_type = hart_state; - constexpr reg_t get_irq_mask() { - return 0b101110111011; // only machine mode is supported + constexpr reg_t get_irq_mask(size_t mode) { + std::array m = {{ + 0b000100010001, // U mode + 0b001100110011, // S mode + 0, + 0b101110111011 // M mode + }}; + return m[mode]; } riscv_hart_mu_p(); @@ -338,8 +197,8 @@ public: void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; void disass_output(uint64_t pc, const std::string instr) override { - CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [s:0x{:x};c:{}]", - pc, instr, (reg_t)state.mstatus, this->reg.icount); + CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]", + pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.icount); }; iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; } @@ -359,7 +218,7 @@ public: protected: struct riscv_instrumentation_if : public iss::instrumentation_if { - riscv_instrumentation_if(riscv_hart_mu_p &arch) + riscv_instrumentation_if(riscv_hart_mu_p &arch) : arch(arch) {} /** * get the name of this architecture @@ -374,7 +233,7 @@ protected: virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; }; - riscv_hart_mu_p &arch; + riscv_hart_mu_p &arch; }; friend struct riscv_instrumentation_if; @@ -387,7 +246,7 @@ protected: virtual iss::status read_csr(unsigned addr, reg_t &val); virtual iss::status write_csr(unsigned addr, reg_t val); - hart_state state; + hart_state_type state; uint64_t cycle_offset; reg_t fault_data; uint64_t tohost = tohost_dflt; @@ -427,8 +286,8 @@ protected: void check_interrupt(); }; -template -riscv_hart_mu_p::riscv_hart_mu_p() +template +riscv_hart_mu_p::riscv_hart_mu_p() : state() , cycle_offset(0) , instr_if(*this) { @@ -437,33 +296,39 @@ riscv_hart_mu_p::riscv_hart_mu_p() for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr; for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr; // special handling - csr_rd_cb[time] = &riscv_hart_mu_p::read_time; + csr_rd_cb[time] = &this_class::read_time; csr_wr_cb[time] = nullptr; - csr_rd_cb[timeh] = &riscv_hart_mu_p::read_time; + csr_rd_cb[timeh] = &this_class::read_time; csr_wr_cb[timeh] = nullptr; - csr_rd_cb[mcycle] = &riscv_hart_mu_p::read_cycle; - csr_rd_cb[mcycleh] = &riscv_hart_mu_p::read_cycle; - csr_rd_cb[minstret] = &riscv_hart_mu_p::read_cycle; - csr_rd_cb[minstreth] = &riscv_hart_mu_p::read_cycle; - csr_rd_cb[mstatus] = &riscv_hart_mu_p::read_status; - csr_wr_cb[mstatus] = &riscv_hart_mu_p::write_status; - csr_rd_cb[mip] = &riscv_hart_mu_p::read_ip; - csr_wr_cb[mip] = &riscv_hart_mu_p::write_ip; - csr_rd_cb[mie] = &riscv_hart_mu_p::read_ie; - csr_wr_cb[mie] = &riscv_hart_mu_p::write_ie; - csr_rd_cb[mhartid] = &riscv_hart_mu_p::read_hartid; + csr_rd_cb[mcycle] = &this_class::read_cycle; + csr_rd_cb[mcycleh] = &this_class::read_cycle; + csr_rd_cb[minstret] = &this_class::read_cycle; + csr_rd_cb[minstreth] = &this_class::read_cycle; + csr_rd_cb[mstatus] = &this_class::read_status; + csr_wr_cb[mstatus] = &this_class::write_status; + csr_rd_cb[ustatus] = &this_class::read_status; + csr_wr_cb[ustatus] = &this_class::write_status; + csr_rd_cb[mip] = &this_class::read_ip; + csr_wr_cb[mip] = &this_class::write_ip; + csr_rd_cb[uip] = &this_class::read_ip; + csr_wr_cb[uip] = &this_class::write_ip; + csr_rd_cb[mie] = &this_class::read_ie; + csr_wr_cb[mie] = &this_class::write_ie; + csr_rd_cb[uie] = &this_class::read_ie; + csr_wr_cb[uie] = &this_class::write_ie; + csr_rd_cb[mhartid] = &this_class::read_hartid; // common regs const std::array addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}}; for(auto addr: addrs) { - csr_rd_cb[addr] = &riscv_hart_mu_p::read_reg; - csr_wr_cb[addr] = &riscv_hart_mu_p::write_reg; + csr_rd_cb[addr] = &this_class::read_reg; + csr_wr_cb[addr] = &this_class::write_reg; } // read-only registers - csr_rd_cb[misa] = &riscv_hart_mu_p::read_reg; + csr_rd_cb[misa] = &this_class::read_reg; csr_wr_cb[misa] = nullptr; } -template std::pair riscv_hart_mu_p::load_file(std::string name, int type) { +template std::pair riscv_hart_mu_p::load_file(std::string name, int type) { FILE *fp = fopen(name.c_str(), "r"); if (fp) { std::array buf; @@ -507,8 +372,8 @@ template std::pair riscv_hart_mu_p::load_f throw std::runtime_error("memory load file not found"); } -template -iss::status riscv_hart_mu_p::read(const address_type type, const access_type access, const uint32_t space, +template +iss::status riscv_hart_mu_p::read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, uint8_t *const data) { #ifndef NDEBUG if (access && iss::access_type::DEBUG) { @@ -565,8 +430,8 @@ iss::status riscv_hart_mu_p::read(const address_type type, const access_ty } } -template -iss::status riscv_hart_mu_p::write(const address_type type, const access_type access, const uint32_t space, +template +iss::status riscv_hart_mu_p::write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, const uint8_t *const data) { #ifndef NDEBUG const char *prefix = (access && iss::access_type::DEBUG) ? "debug " : ""; @@ -672,7 +537,7 @@ iss::status riscv_hart_mu_p::write(const address_type type, const access_t } } -template iss::status riscv_hart_mu_p::read_csr(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_csr(unsigned addr, reg_t &val) { if (addr >= csr.size()) return iss::Err; auto req_priv_lvl = (addr >> 8) & 0x3; if (this->reg.PRIV < req_priv_lvl) // not having required privileges @@ -683,7 +548,7 @@ template iss::status riscv_hart_mu_p::read_csr(unsigned ad return (this->*(it->second))(addr, val); } -template iss::status riscv_hart_mu_p::write_csr(unsigned addr, reg_t val) { +template iss::status riscv_hart_mu_p::write_csr(unsigned addr, reg_t val) { if (addr >= csr.size()) return iss::Err; auto req_priv_lvl = (addr >> 8) & 0x3; if (this->reg.PRIV < req_priv_lvl) // not having required privileges @@ -696,17 +561,17 @@ template iss::status riscv_hart_mu_p::write_csr(unsigned a return (this->*(it->second))(addr, val); } -template iss::status riscv_hart_mu_p::read_reg(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_reg(unsigned addr, reg_t &val) { val = csr[addr]; return iss::Ok; } -template iss::status riscv_hart_mu_p::write_reg(unsigned addr, reg_t val) { +template iss::status riscv_hart_mu_p::write_reg(unsigned addr, reg_t val) { csr[addr] = val; return iss::Ok; } -template iss::status riscv_hart_mu_p::read_cycle(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_cycle(unsigned addr, reg_t &val) { auto cycle_val = this->reg.icount + cycle_offset; if (addr == mcycle) { val = static_cast(cycle_val); @@ -717,7 +582,7 @@ template iss::status riscv_hart_mu_p::read_cycle(unsigned return iss::Ok; } -template iss::status riscv_hart_mu_p::read_time(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_time(unsigned addr, reg_t &val) { uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052; if (addr == time) { val = static_cast(time_val); @@ -728,51 +593,55 @@ template iss::status riscv_hart_mu_p::read_time(unsigned a return iss::Ok; } -template iss::status riscv_hart_mu_p::read_status(unsigned addr, reg_t &val) { - val = state.mstatus & hart_state::get_mask(); +template iss::status riscv_hart_mu_p::read_status(unsigned addr, reg_t &val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + val = state.mstatus & hart_state_type::get_mask(req_priv_lvl); return iss::Ok; } -template iss::status riscv_hart_mu_p::write_status(unsigned addr, reg_t val) { - state.write_mstatus(val); +template iss::status riscv_hart_mu_p::write_status(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + state.write_mstatus(val, req_priv_lvl); check_interrupt(); return iss::Ok; } -template iss::status riscv_hart_mu_p::read_ie(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_ie(unsigned addr, reg_t &val) { val = csr[mie]; val &= csr[mideleg]; return iss::Ok; } -template iss::status riscv_hart_mu_p::read_hartid(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_hartid(unsigned addr, reg_t &val) { val = mhartid_reg; return iss::Ok; } -template iss::status riscv_hart_mu_p::write_ie(unsigned addr, reg_t val) { - auto mask = get_irq_mask(); +template iss::status riscv_hart_mu_p::write_ie(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + auto mask = get_irq_mask(req_priv_lvl); csr[mie] = (csr[mie] & ~mask) | (val & mask); check_interrupt(); return iss::Ok; } -template iss::status riscv_hart_mu_p::read_ip(unsigned addr, reg_t &val) { +template iss::status riscv_hart_mu_p::read_ip(unsigned addr, reg_t &val) { val = csr[mip]; val &= csr[mideleg]; return iss::Ok; } -template iss::status riscv_hart_mu_p::write_ip(unsigned addr, reg_t val) { - auto mask = get_irq_mask(); +template iss::status riscv_hart_mu_p::write_ip(unsigned addr, reg_t val) { + auto req_priv_lvl = (addr >> 8) & 0x3; + auto mask = get_irq_mask(req_priv_lvl); mask &= ~(1 << 7); // MTIP is read only csr[mip] = (csr[mip] & ~mask) | (val & mask); check_interrupt(); return iss::Ok; } -template -iss::status riscv_hart_mu_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) { +template +iss::status riscv_hart_mu_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) { if ((paddr.val + length) > mem.size()) return iss::Err; if(mem_read_cb) return mem_read_cb(paddr, length, data); switch (paddr.val) { @@ -797,8 +666,8 @@ iss::status riscv_hart_mu_p::read_mem(phys_addr_t paddr, unsigned length, return iss::Ok; } -template -iss::status riscv_hart_mu_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) { +template +iss::status riscv_hart_mu_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) { if ((paddr.val + length) > mem.size()) return iss::Err; if(mem_write_cb) return mem_write_cb(paddr, length, data); switch (paddr.val) { @@ -874,12 +743,12 @@ iss::status riscv_hart_mu_p::write_mem(phys_addr_t paddr, unsigned length, return iss::Ok; } -template inline void riscv_hart_mu_p::reset(uint64_t address) { +template inline void riscv_hart_mu_p::reset(uint64_t address) { BASE::reset(address); - state.mstatus = hart_state::mstatus_reset_val; + state.mstatus = hart_state_type::mstatus_reset_val; } -template void riscv_hart_mu_p::check_interrupt() { +template void riscv_hart_mu_p::check_interrupt() { auto ideleg = csr[mideleg]; // Multiple simultaneous interrupts and traps at the same privilege level are // handled in the following decreasing priority order: @@ -901,23 +770,36 @@ template void riscv_hart_mu_p::check_interrupt() { } } -template uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_t addr) { +template uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_t addr) { // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] // calculate and write mcause val auto trap_id = bit_sub<0, 16>(flags); auto cause = bit_sub<16, 15>(flags); - if (trap_id == 0 && cause == 11) cause = 0x8 + PRIV_M; // adjust environment call cause + if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause // calculate effective privilege level + auto new_priv = PRIV_M; if (trap_id == 0) { // exception + if (this->reg.PRIV != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0) + new_priv = PRIV_U; // store ret addr in xepc register - csr[mepc] = static_cast(addr); // store actual address instruction of exception - csr[mtval] = fault_data; + csr[uepc | (new_priv << 8)] = static_cast(addr); // store actual address instruction of exception + /* + * write mtval if new_priv=M_MODE, spec says: + * When a hardware breakpoint is triggered, or an instruction-fetch, load, + * or store address-misaligned, + * access, or page-fault exception occurs, mtval is written with the + * faulting effective address. + */ + csr[utval | (new_priv << 8)] = fault_data; fault_data = 0; } else { - csr[mepc] = this->reg.NEXT_PC; // store next address if interrupt + if (this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0) + new_priv = PRIV_U; + csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt this->reg.pending_trap = 0; } - csr[mcause] = (trap_id << 31) + cause; + size_t adr = ucause | (new_priv << 8); + csr[adr] = (trap_id << 31) + cause; // update mstatus // xPP field of mstatus is written with the active privilege mode at the time // of the trap; the x PIE field of mstatus @@ -925,37 +807,64 @@ template uint64_t riscv_hart_mu_p::enter_trap(uint64_t fla // the trap; and the x IE field of mstatus // is cleared // store the actual privilege level in yPP and store interrupt enable flags - state.mstatus.MPP = PRIV_M; - state.mstatus.MPIE = state.mstatus.MIE; - state.mstatus.MIE = false; + switch (new_priv) { + case PRIV_M: + state.mstatus.MPP = this->reg.PRIV; + state.mstatus.MPIE = state.mstatus.MIE; + state.mstatus.MIE = false; + break; + case PRIV_U: + state.mstatus.UPIE = state.mstatus.UIE; + state.mstatus.UIE = false; + break; + default: + break; + } // get trap vector - auto ivec = csr[mtvec]; + auto ivec = csr[utvec | (new_priv << 8)]; // calculate addr// set NEXT_PC to trap addressess to jump to based on MODE // bits in mtvec this->reg.NEXT_PC = ivec & ~0x1UL; if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause; // reset trap state - this->reg.PRIV = PRIV_M; + this->reg.PRIV = new_priv; this->reg.trap_state = 0; std::array buffer; sprintf(buffer.data(), "0x%016lx", addr); if((flags&0xffffffff) != 0xffffffff) CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" - << " at address " << buffer.data() << " occurred"; + << " at address " << buffer.data() << " occurred, changing privilege level from " + << lvl[this->reg.PRIV] << " to " << lvl[new_priv]; return this->reg.NEXT_PC; } -template uint64_t riscv_hart_mu_p::leave_trap(uint64_t flags) { - state.mstatus.MIE = state.mstatus.MPIE; +template uint64_t riscv_hart_mu_p::leave_trap(uint64_t flags) { + auto inst_priv = (flags & 0x3)? 3:0; + auto status = state.mstatus; + + // pop the relevant lower-privilege interrupt enable and privilege mode stack + // clear respective yIE + switch (inst_priv) { + case PRIV_M: + this->reg.PRIV = state.mstatus.MPP; + state.mstatus.MPP = 0; // clear mpp to U mode + state.mstatus.MIE = state.mstatus.MPIE; + break; + case PRIV_U: + this->reg.PRIV = 0; + state.mstatus.UIE = state.mstatus.UPIE; + break; + } // sets the pc to the value stored in the x epc register. - this->reg.NEXT_PC = csr[mepc]; - CLOG(INFO, disass) << "Executing xRET"; + this->reg.NEXT_PC = csr[uepc | inst_priv << 8]; + CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[this->reg.PRIV] << " to " + << lvl[this->reg.PRIV]; return this->reg.NEXT_PC; } } // namespace arch } // namespace iss -#endif /* _RISCV_CORE_H_ */ +#endif /* _RISCV_HART_MU_P_H */ diff --git a/incl/sysc/core_complex.h b/incl/sysc/core_complex.h index 9ba5df1..64d2ff3 100644 --- a/incl/sysc/core_complex.h +++ b/incl/sysc/core_complex.h @@ -48,16 +48,6 @@ class scv_tr_stream; struct _scv_tr_generator_default_data; template class scv_tr_generator; -namespace iss { -class vm_if; -namespace arch { -template class riscv_hart_m_p; -} -namespace debugger { -class target_adapter_if; -} -} // namespace iss - namespace sysc { class tlm_dmi_ext : public tlm::tlm_dmi { @@ -97,6 +87,8 @@ public: cci::cci_param reset_address{"reset_address", 0ULL}; + cci::cci_param core_type{"core_type", "tgc_c"}; + cci::cci_param backend{"backend", "interp"}; cci::cci_param gdb_server_port{"gdb_server_port", 0}; @@ -145,9 +137,7 @@ protected: tlm_utils::tlm_quantumkeeper quantum_keeper; std::vector write_buf; std::unique_ptr cpu; - std::unique_ptr vm; sc_core::sc_time curr_clk; - iss::debugger::target_adapter_if *tgt_adapter; #ifdef WITH_SCV //! transaction recording database scv_tr_db *m_db; diff --git a/src/sysc/core_complex.cpp b/src/sysc/core_complex.cpp index ae61de0..03a0733 100644 --- a/src/sysc/core_complex.cpp +++ b/src/sysc/core_complex.cpp @@ -31,17 +31,20 @@ *******************************************************************************/ #include "sysc/core_complex.h" +#ifdef CORE_TGC_B +#include "iss/arch/riscv_hart_m_p.h" +#include "iss/arch/tgc_b.h" +using tgc_b_plat_type = iss::arch::riscv_hart_m_p; +#endif #ifdef CORE_TGC_C #include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/tgc_c.h" -using core_type = iss::arch::tgc_c; -using plat_type = iss::arch::riscv_hart_m_p; +using tgc_c_plat_type = iss::arch::riscv_hart_m_p; #endif #ifdef CORE_TGC_D #include "iss/arch/riscv_hart_mu_p.h" #include "iss/arch/tgc_d.h" -using core_type = iss::arch::tgc_d; -using plat_type = iss::arch::riscv_hart_mu_p; +using tgc_d_plat_type = iss::arch::riscv_hart_mu_p; #endif #include "iss/debugger/encoderdecoder.h" #include "iss/debugger/gdb_session.h" @@ -53,6 +56,10 @@ using plat_type = iss::arch::riscv_hart_mu_p; #include #include +#define STR(X) #X +#define CREATE_CORE(CN) \ +else if (type == STR(CN)) { std::tie(cpu, vm) = create_core(backend, gdb_port, hart_id); } + #ifdef WITH_SCV #include #include @@ -67,41 +74,17 @@ using namespace sc_core; namespace { iss::debugger::encoder_decoder encdec; -} - - -namespace { std::array lvl = {{'U', 'S', 'H', 'M'}}; - -std::array trap_str = { { - "Instruction address misaligned", - "Instruction access fault", - "Illegal instruction", - "Breakpoint", - "Load address misaligned", - "Load access fault", - "Store/AMO address misaligned", - "Store/AMO access fault", - "Environment call from U-mode", - "Environment call from S-mode", - "Reserved", - "Environment call from M-mode", - "Instruction page fault", - "Load page fault", - "Reserved", - "Store/AMO page fault" -} }; -std::array irq_str = { { - "User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", - "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", - "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt" } }; } -class core_wrapper : public plat_type { +template +class core_wrapper_t : public PLAT { public: - using phys_addr_t = typename arch::traits::phys_addr_t; - core_wrapper(core_complex *owner) + using reg_t = typename arch::traits::reg_t; + using phys_addr_t = typename arch::traits::phys_addr_t; + using heart_state_t = typename PLAT::hart_state_type; + core_wrapper_t(core_complex *owner) : owner(owner) { } uint32_t get_mode() { return this->reg.PRIV; } @@ -110,10 +93,10 @@ public: inline bool get_interrupt_execution() { return this->interrupt_sim; } - plat_type::hart_state &get_state() { return this->state; } + heart_state_t &get_state() { return this->state; } - void notify_phase(exec_phase p) override { - if (p == ISTART) owner->sync(this->reg.icount + cycle_offset); + void notify_phase(iss::arch_if::exec_phase p) override { + if (p == iss::arch_if::ISTART) owner->sync(this->reg.icount); } sync_type needed_sync() const override { return PRE_SYNC; } @@ -122,7 +105,7 @@ public: if (INFO <= Log>::reporting_level() && Output2FILE::stream()) { std::stringstream s; s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0') - << std::setw(sizeof(reg_t) * 2) << (reg_t)state.mstatus << std::dec << ";c:" << this->reg.icount << "]"; + << std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:" << this->reg.icount << "]"; Log>().get(INFO, "disass") << "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40) << std::setfill(' ') << std::left << instr << s.str(); @@ -165,7 +148,7 @@ public: } return ret?Ok:Err; } else { - return plat_type::read_csr(addr, val); + return PLAT::read_csr(addr, val); } } @@ -174,11 +157,11 @@ public: do { wait(wfi_evt); } while (this->reg.pending_trap == 0); - plat_type::wait_until(flags); + PLAT::wait_until(flags); } void local_irq(short id, bool value) { - plat_type::reg_t mask = 0; + reg_t mask = 0; switch (id) { case 16: // SW mask = 1 << 3; @@ -238,11 +221,82 @@ int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func return Err; } +using cpu_ptr = std::unique_ptr; +using vm_ptr= std::unique_ptr; + +class core_wrapper { +public: + core_wrapper(core_complex *owner) : owner(owner) { } + + void reset(uint64_t addr){vm->reset(addr);} + inline void start(){vm->start();} + inline std::pair load_file(std::string const& name){ return cpu->load_file(name);}; + + std::function get_mode; + std::function get_state; + std::function get_interrupt_execution; + std::function set_interrupt_execution; + std::function local_irq; + + template + std::tuple create_core(std::string const& backend, unsigned gdb_port, uint32_t hart_id){ + auto* lcpu = new core_wrapper_t(owner); + lcpu->set_mhartid(hart_id); + get_mode = [lcpu]() { return lcpu->get_mode(); }; + get_state = [lcpu]() { return lcpu->get_state().mstatus.backing.val; }; + get_interrupt_execution = [lcpu]() { return lcpu->get_interrupt_execution(); }; + set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); }; + local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); }; + if(backend == "interp") + return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(lcpu, gdb_port)}}; +#ifdef WITH_LLVM + if(backend == "llvm") + return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}}; +#endif +#ifdef WITH_TCC + if(backend == "tcc") + s return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}}; +#endif + return {nullptr, nullptr}; + } + + void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){ + if (type == "") { + LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl; + } +#ifdef CORE_TGC_B + CREATE_CORE(tgc_c) +#endif +#ifdef CORE_TGC_C + CREATE_CORE(tgc_c) +#endif +#ifdef CORE_TGC_D + CREATE_CORE(tgc_d) +#endif + else { + LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl; + } + auto *srv = debugger::server::get(); + if (srv) tgt_adapter = srv->get_target(); + if (tgt_adapter) + tgt_adapter->add_custom_command( + {"sysc", [this](int argc, char *argv[], debugger::out_func of, + debugger::data_func df) -> int { return cmd_sysc(argc, argv, of, df, tgt_adapter); }, + "SystemC sub-commands: break