Compare commits
	
		
			45 Commits
		
	
	
		
			f4f90c5e65
			...
			fd303c8343
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fd303c8343 | |||
| 346b177a87 | |||
| d4ec131fa7 | |||
| 48370a4555 | |||
| 36b076774e | |||
| 482a4ec253 | |||
| 2fb28364c5 | |||
| 8460f4ab7f | |||
| 3fd51cc68c | |||
| 551822916c | |||
| 37db31fb4b | |||
| e2da306eee | |||
| 41051f8f34 | |||
| 2a7449fa1e | |||
|   | a6c48ceaac | ||
| 1e30b68507 | |||
|   | ed793471bb | ||
| 58fb815f32 | |||
| 3cc8bd0854 | |||
| a27850f841 | |||
| fb330cddea | |||
| b76c5bf0d6 | |||
| 001c6349f7 | |||
| ee6a11dae6 | |||
| 2e27b025cc | |||
| f0a004be9d | |||
| 3422c7cd5c | |||
| ad79a28705 | |||
| 9fdbc3ff38 | |||
| 602bc6e06a | |||
| 6cb76fc256 | |||
| fbcd389580 | |||
| b25b7848c6 | |||
| 6c986d38d8 | |||
| a1ebd83d2a | |||
| 8aed551813 | |||
| 1e6a0086e9 | |||
| 119d4a8b43 | |||
| 9841b16122 | |||
| fbda1424f3 | |||
| fe2d5cb2f9 | |||
| 3ff59ba45d | |||
| db5765b342 | |||
| 075e04249a | |||
| 207f778ee6 | 
| @@ -1,8 +1,9 @@ | ||||
| cmake_minimum_required(VERSION 3.12) | ||||
| cmake_minimum_required(VERSION 3.18) | ||||
| list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) | ||||
| ############################################################################### | ||||
|  | ||||
| # ############################################################################## | ||||
| # | ||||
| ############################################################################### | ||||
| # ############################################################################## | ||||
| project(dbt-rise-tgc VERSION 1.0.0) | ||||
|  | ||||
| include(GNUInstallDirs) | ||||
| @@ -19,33 +20,40 @@ set(LIB_SOURCES | ||||
|     src/iss/arch/tgc5c.cpp | ||||
|     src/vm/interp/vm_tgc5c.cpp | ||||
|     src/vm/fp_functions.cpp | ||||
|     src/iss/semihosting/semihosting.cpp | ||||
| ) | ||||
|  | ||||
| if(WITH_TCC) | ||||
|     list(APPEND LIB_SOURCES | ||||
|         src/vm/tcc/vm_tgc5c.cpp | ||||
|     ) | ||||
| endif() | ||||
|  | ||||
| if(WITH_LLVM) | ||||
|     list(APPEND LIB_SOURCES | ||||
|         src/vm/llvm/vm_tgc5c.cpp | ||||
|         src/vm/llvm/fp_impl.cpp | ||||
|     ) | ||||
| endif() | ||||
|  | ||||
| if(WITH_ASMJIT) | ||||
|     list(APPEND LIB_SOURCES | ||||
|         src/vm/asmjit/vm_tgc5c.cpp | ||||
|     ) | ||||
| endif() | ||||
|  | ||||
| # library files | ||||
| FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp) | ||||
| FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp) | ||||
| FILE(GLOB GEN_YAML_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/contrib/instr/*.yaml) | ||||
| list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES}) | ||||
|  | ||||
| foreach(FILEPATH ${GEN_ISS_SOURCES}) | ||||
|     get_filename_component(CORE ${FILEPATH} NAME_WE) | ||||
|     string(TOUPPER ${CORE} CORE) | ||||
|     list(APPEND LIB_DEFINES CORE_${CORE}) | ||||
| endforeach() | ||||
|  | ||||
| message(STATUS "Core defines are ${LIB_DEFINES}") | ||||
|  | ||||
| if(WITH_LLVM) | ||||
| @@ -57,16 +65,19 @@ if(WITH_TCC) | ||||
|     FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp) | ||||
|     list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES}) | ||||
| endif() | ||||
|  | ||||
| if(WITH_ASMJIT) | ||||
|     FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/asmjit/vm_*.cpp) | ||||
|     list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES}) | ||||
| endif() | ||||
|  | ||||
| if(TARGET yaml-cpp::yaml-cpp) | ||||
|     list(APPEND LIB_SOURCES | ||||
|         src/iss/plugin/cycle_estimate.cpp | ||||
|         src/iss/plugin/instruction_count.cpp | ||||
|     ) | ||||
| endif() | ||||
|  | ||||
| # Define the library | ||||
| add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES}) | ||||
|  | ||||
| @@ -75,19 +86,23 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") | ||||
| elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | ||||
|     target_compile_options(${PROJECT_NAME} PRIVATE /wd4293) | ||||
| endif() | ||||
|  | ||||
| target_include_directories(${PROJECT_NAME} PUBLIC src) | ||||
| target_include_directories(${PROJECT_NAME} PUBLIC src-gen) | ||||
|  | ||||
| target_force_link_libraries(${PROJECT_NAME} PRIVATE dbt-rise-core) | ||||
|  | ||||
| # only re-export the include paths | ||||
| get_target_property(DBT_CORE_INCL dbt-rise-core INTERFACE_INCLUDE_DIRECTORIES) | ||||
| target_include_directories(${PROJECT_NAME} INTERFACE ${DBT_CORE_INCL}) | ||||
| get_target_property(DBT_CORE_DEFS dbt-rise-core INTERFACE_COMPILE_DEFINITIONS) | ||||
| if(NOT (DBT_CORE_DEFS STREQUAL DBT_CORE_DEFS-NOTFOUND)) | ||||
|  | ||||
| if(NOT(DBT_CORE_DEFS STREQUAL DBT_CORE_DEFS-NOTFOUND)) | ||||
|     target_compile_definitions(${PROJECT_NAME} INTERFACE ${DBT_CORE_DEFS}) | ||||
| endif() | ||||
|  | ||||
| target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio softfloat scc-util Boost::coroutine) | ||||
|  | ||||
| if(TARGET yaml-cpp::yaml-cpp) | ||||
|     target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_PLUGINS) | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC yaml-cpp::yaml-cpp) | ||||
| @@ -97,8 +112,9 @@ if(WITH_LLVM) | ||||
|     find_package(LLVM) | ||||
|     target_compile_definitions(${PROJECT_NAME} PUBLIC ${LLVM_DEFINITIONS}) | ||||
|     target_include_directories(${PROJECT_NAME} PUBLIC ${LLVM_INCLUDE_DIRS}) | ||||
|  | ||||
|     if(BUILD_SHARED_LIBS) | ||||
| 		target_link_libraries( ${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES}) | ||||
|         target_link_libraries(${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES}) | ||||
|     endif() | ||||
| endif() | ||||
|  | ||||
| @@ -119,16 +135,18 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss COMPONENT ${PROJECT_NAME} | ||||
|     DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # target directory | ||||
|     FILES_MATCHING # install only matched files | ||||
|     PATTERN "*.h" # select header files | ||||
|         ) | ||||
| ) | ||||
| install(FILES ${GEN_YAML_SOURCES} DESTINATION share/tgc-vp) | ||||
| ############################################################################### | ||||
|  | ||||
| # ############################################################################## | ||||
| # | ||||
| ############################################################################### | ||||
| # ############################################################################## | ||||
| set(CMAKE_INSTALL_RPATH $ORIGIN/../${CMAKE_INSTALL_LIBDIR}) | ||||
| project(tgc-sim) | ||||
| find_package(Boost COMPONENTS program_options thread REQUIRED) | ||||
|  | ||||
| add_executable(${PROJECT_NAME} src/main.cpp) | ||||
|  | ||||
| if(TARGET ${CORE_NAME}_cpp) | ||||
|     list(APPEND TGC_SOURCES ${${CORE_NAME}_OUTPUT_FILES}) | ||||
| else() | ||||
| @@ -140,21 +158,20 @@ else() | ||||
| endif() | ||||
|  | ||||
| foreach(F IN LISTS TGC_SOURCES) | ||||
|     if (${F} MATCHES ".*/arch/([^/]*)\.cpp") | ||||
|     if(${F} MATCHES ".*/arch/([^/]*)\.cpp") | ||||
|         string(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F}) | ||||
|         string(TOUPPER ${CORE_NAME_LC} CORE_NAME) | ||||
|         target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME}) | ||||
|     endif() | ||||
| endforeach() | ||||
|  | ||||
| #if(WITH_LLVM) | ||||
| # if(WITH_LLVM) | ||||
| # target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_LLVM) | ||||
| # #target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs}) | ||||
| #endif() | ||||
| #if(WITH_TCC) | ||||
| # endif() | ||||
| # if(WITH_TCC) | ||||
| # target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC) | ||||
| #endif() | ||||
|  | ||||
| # endif() | ||||
| target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc fmt::fmt) | ||||
|  | ||||
| if(TARGET Boost::program_options) | ||||
| @@ -162,8 +179,10 @@ if(TARGET Boost::program_options) | ||||
| else() | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC ${BOOST_program_options_LIBRARY}) | ||||
| endif() | ||||
|  | ||||
| target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS}) | ||||
| if (Tcmalloc_FOUND) | ||||
|  | ||||
| if(Tcmalloc_FOUND) | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC ${Tcmalloc_LIBRARIES}) | ||||
| endif(Tcmalloc_FOUND) | ||||
|  | ||||
| @@ -181,22 +200,26 @@ if(BUILD_TESTING) | ||||
|     # ... CMake code to create tests ... | ||||
|     add_test(NAME tgc-sim-interp | ||||
|         COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend interp) | ||||
|  | ||||
|     if(WITH_TCC) | ||||
|         add_test(NAME tgc-sim-tcc | ||||
|             COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend tcc) | ||||
|     endif() | ||||
|  | ||||
|     if(WITH_LLVM) | ||||
|         add_test(NAME tgc-sim-llvm | ||||
|             COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm) | ||||
|     endif() | ||||
|  | ||||
|     if(WITH_ASMJIT) | ||||
|         add_test(NAME tgc-sim-asmjit | ||||
|             COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend asmjit) | ||||
|     endif() | ||||
| endif() | ||||
| ############################################################################### | ||||
|  | ||||
| # ############################################################################## | ||||
| # | ||||
| ############################################################################### | ||||
| # ############################################################################## | ||||
| if(TARGET scc-sysc) | ||||
|     project(dbt-rise-tgc_sc VERSION 1.0.0) | ||||
|     set(LIB_SOURCES | ||||
| @@ -208,18 +231,20 @@ if(TARGET scc-sysc) | ||||
|     add_library(${PROJECT_NAME} ${LIB_SOURCES}) | ||||
|     target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_SYSTEMC) | ||||
|     target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME}) | ||||
|  | ||||
|     foreach(F IN LISTS TGC_SOURCES) | ||||
|         if (${F} MATCHES ".*/arch/([^/]*)\.cpp") | ||||
|         if(${F} MATCHES ".*/arch/([^/]*)\.cpp") | ||||
|             string(REGEX REPLACE ".*/([^/]*)\.cpp" "\\1" CORE_NAME_LC ${F}) | ||||
|             string(TOUPPER ${CORE_NAME_LC} CORE_NAME) | ||||
|             target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME}) | ||||
|         endif() | ||||
|     endforeach() | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc scc-sysc) | ||||
| #    if(WITH_LLVM) | ||||
| #        target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs}) | ||||
| #    endif() | ||||
|  | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc scc-sysc) | ||||
|  | ||||
|     # if(WITH_LLVM) | ||||
|     # target_link_libraries(${PROJECT_NAME} PUBLIC ${llvm_libs}) | ||||
|     # endif() | ||||
|     set(LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/sysc/core_complex.h) | ||||
|     set_target_properties(${PROJECT_NAME} PROPERTIES | ||||
|         VERSION ${PROJECT_VERSION} | ||||
| @@ -236,4 +261,3 @@ if(TARGET scc-sysc) | ||||
|         INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers | ||||
|     ) | ||||
| endif() | ||||
|  | ||||
|   | ||||
| @@ -349,7 +349,7 @@ Zifencei: | ||||
|     size:   32 | ||||
|     branch:   false | ||||
|     delay:   1 | ||||
| RV32M:  | ||||
| RVM:  | ||||
|   MUL: | ||||
|     index: 49 | ||||
|     encoding: 0b00000010000000000000000000110011 | ||||
|   | ||||
| @@ -47,10 +47,10 @@ def getRegisterSizes(){ | ||||
|  | ||||
| using namespace iss::arch; | ||||
|  | ||||
| constexpr std::array<const char*, ${registers.size}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_names; | ||||
| constexpr std::array<const char*, ${registers.size}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_aliases; | ||||
| constexpr std::array<const uint32_t, ${getRegisterSizes().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_bit_widths; | ||||
| constexpr std::array<const uint32_t, ${getRegisterSizes().size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_byte_offsets; | ||||
| constexpr std::array<const char*, ${registers.size()}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_names; | ||||
| constexpr std::array<const char*, ${registers.size()}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_aliases; | ||||
| constexpr std::array<const uint32_t, ${getRegisterSizes().size()}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_bit_widths; | ||||
| constexpr std::array<const uint32_t, ${getRegisterSizes().size()}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_byte_offsets; | ||||
|  | ||||
| ${coreDef.name.toLowerCase()}::${coreDef.name.toLowerCase()}()  = default; | ||||
|  | ||||
|   | ||||
| @@ -75,10 +75,10 @@ template <> struct traits<${coreDef.name.toLowerCase()}> { | ||||
|  | ||||
|     constexpr static char const* const core_type = "${coreDef.name}"; | ||||
|      | ||||
|     static constexpr std::array<const char*, ${registers.size}> reg_names{ | ||||
|     static constexpr std::array<const char*, ${registers.size()}> reg_names{ | ||||
|         {"${registers.collect{it.name.toLowerCase()}.join('", "')}"}}; | ||||
|   | ||||
|     static constexpr std::array<const char*, ${registers.size}> reg_aliases{ | ||||
|     static constexpr std::array<const char*, ${registers.size()}> reg_aliases{ | ||||
|         {"${registers.collect{it.alias.toLowerCase()}.join('", "')}"}}; | ||||
|  | ||||
|     enum constants {${constants.collect{c -> c.name+"="+getCString(c.value)}.join(', ')}}; | ||||
| @@ -99,17 +99,17 @@ template <> struct traits<${coreDef.name.toLowerCase()}> { | ||||
|  | ||||
|     using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>; | ||||
|  | ||||
|     static constexpr std::array<const uint32_t, ${getRegisterSizes().size}> reg_bit_widths{ | ||||
|     static constexpr std::array<const uint32_t, ${getRegisterSizes().size()}> reg_bit_widths{ | ||||
|         {${getRegisterSizes().join(',')}}}; | ||||
|  | ||||
|     static constexpr std::array<const uint32_t, ${getRegisterOffsets().size}> reg_byte_offsets{ | ||||
|     static constexpr std::array<const uint32_t, ${getRegisterOffsets().size()}> reg_byte_offsets{ | ||||
|         {${getRegisterOffsets().join(',')}}}; | ||||
|  | ||||
|     static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1); | ||||
|  | ||||
|     enum sreg_flag_e { FLAGS }; | ||||
|  | ||||
|     enum mem_type_e { ${spaces.collect{it.name}.join(', ')} }; | ||||
|     enum mem_type_e { ${spaces.collect{it.name}.join(', ')}, IMEM = MEM }; | ||||
|      | ||||
|     enum class opcode_e {<%instructions.eachWithIndex{instr, index -> %> | ||||
|         ${instr.instruction.name} = ${index},<%}%> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2017, 2023 MINRES Technologies GmbH | ||||
|  * Copyright (C) 2017-2024 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
| @@ -79,21 +79,36 @@ public: | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     using vm_base<ARCH>::get_reg_ptr; | ||||
|     using super::get_ptr_for; | ||||
| using super::get_reg; | ||||
|     using super::get_reg_for; | ||||
|     using super::load_reg_from_mem; | ||||
|     using super::write_reg_to_mem; | ||||
|     using super::gen_ext; | ||||
|     using super::gen_read_mem; | ||||
|     using super::gen_write_mem; | ||||
|     using super::gen_wait; | ||||
|     using super::gen_leave; | ||||
|     using super::gen_operation; | ||||
|     | ||||
|     using this_class = vm_impl<ARCH>; | ||||
|     using compile_func = continuation_e (this_class::*)(virt_addr_t&, code_word_t, jit_holder&); | ||||
|  | ||||
|     continuation_e gen_single_inst_behavior(virt_addr_t&, unsigned int &, jit_holder&) override; | ||||
|     void gen_block_prologue(jit_holder& jh) override; | ||||
|     void gen_block_epilogue(jit_holder& jh) override; | ||||
|     inline const char *name(size_t index){return traits::reg_aliases.at(index);} | ||||
|  | ||||
|     void gen_instr_prologue(jit_holder& jh); | ||||
|     void gen_instr_epilogue(jit_holder& jh); | ||||
|     inline void gen_raise(jit_holder& jh, uint16_t trap_id, uint16_t cause); | ||||
|  | ||||
|     template<unsigned W, typename U, typename S = typename std::make_signed<U>::type> | ||||
|     inline S sext(U from) { | ||||
|         auto mask = (1ULL<<W) - 1; | ||||
|         auto sign_mask = 1ULL<<(W-1); | ||||
|         return (from & mask) | ((from & sign_mask) ? ~mask : 0); | ||||
|     }  | ||||
| #include <vm/asmjit/helper_func.h> | ||||
|  | ||||
| private: | ||||
|     /**************************************************************************** | ||||
|      * start opcode definitions | ||||
| @@ -114,7 +129,7 @@ private: | ||||
|  | ||||
|     decoding_tree_node* root {nullptr}; | ||||
|  | ||||
|     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||
|     const std::array<instruction_descriptor, ${instructions.size()}> instr_descr = {{ | ||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||
|         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ | ||||
|         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> | ||||
| @@ -127,16 +142,26 @@ private: | ||||
|         <%instr.fields.eachLine{%>${it} | ||||
|         <%}%>if(this->disass_enabled){ | ||||
|             /* generate disass */ | ||||
|             <%instr.disass.eachLine{%> | ||||
|             ${it}<%}%> | ||||
|             InvokeNode* call_print_disass; | ||||
|             char* mnemonic_ptr = strdup(mnemonic.c_str()); | ||||
|             jh.disass_collection.push_back(mnemonic_ptr); | ||||
|             jh.cc.invoke(&call_print_disass, &print_disass, FuncSignature::build<void, void *, uint64_t, char *>()); | ||||
|             call_print_disass->setArg(0, jh.arch_if_ptr); | ||||
|             call_print_disass->setArg(1, pc.val); | ||||
|             call_print_disass->setArg(2, mnemonic_ptr); | ||||
|  | ||||
|         } | ||||
|         x86::Compiler& cc = jh.cc; | ||||
|         //ideally only do this if necessary (someone / plugin needs it) | ||||
|         cc.mov(jh.pc,PC); | ||||
|         cc.comment(fmt::format("\\n${instr.name}_{:#x}:",pc.val).c_str()); | ||||
|         cc.comment(fmt::format("${instr.name}_{:#x}:",pc.val).c_str()); | ||||
|         this->gen_sync(jh, PRE_SYNC, ${idx}); | ||||
|         pc=pc+ ${instr.length/8}; | ||||
|         cc.mov(jh.pc, pc.val); | ||||
|         pc = pc+${instr.length/8}; | ||||
|         cc.mov(jh.next_pc, pc.val); | ||||
|  | ||||
|         gen_instr_prologue(jh, pc.val); | ||||
|         cc.comment("\\n//behavior:"); | ||||
|         gen_instr_prologue(jh); | ||||
|         cc.comment("//behavior:"); | ||||
|         /*generate behavior*/ | ||||
|         <%instr.behavior.eachLine{%>${it} | ||||
|         <%}%> | ||||
| @@ -149,9 +174,17 @@ private: | ||||
|      * end opcode definitions | ||||
|      ****************************************************************************/ | ||||
|     continuation_e illegal_intruction(virt_addr_t &pc, code_word_t instr, jit_holder& jh ) { | ||||
|  | ||||
|         x86::Compiler& cc = jh.cc; | ||||
|         cc.comment(fmt::format("illegal_intruction{:#x}:",pc.val).c_str()); | ||||
|         this->gen_sync(jh, PRE_SYNC, instr_descr.size()); | ||||
|         pc = pc + ((instr & 3) == 3 ? 4 : 2); | ||||
|         gen_instr_prologue(jh); | ||||
|         cc.comment("//behavior:"); | ||||
|         gen_instr_epilogue(jh); | ||||
|         this->gen_sync(jh, POST_SYNC, instr_descr.size()); | ||||
|         return BRANCH; | ||||
|     } | ||||
|       | ||||
|     //decoding functionality | ||||
|  | ||||
|     void populate_decoding_tree(decoding_tree_node* root){ | ||||
| @@ -206,11 +239,6 @@ private: | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename CODE_WORD> void debug_fn(CODE_WORD instr) { | ||||
|     volatile CODE_WORD x = instr; | ||||
|     instr = 2 * x; | ||||
| } | ||||
|  | ||||
| template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | ||||
|  | ||||
| template <typename ARCH> | ||||
| @@ -224,8 +252,7 @@ vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||
| } | ||||
|  | ||||
| template <typename ARCH> | ||||
| continuation_e | ||||
| vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, jit_holder& jh) { | ||||
| continuation_e vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, jit_holder& jh) { | ||||
|     enum {TRAP_ID=1<<16}; | ||||
|     code_word_t instr = 0; | ||||
|     phys_addr_t paddr(pc); | ||||
| @@ -243,10 +270,78 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | ||||
|         f = &this_class::illegal_intruction; | ||||
|     return (this->*f)(pc, instr, jh); | ||||
| } | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_instr_prologue(jit_holder& jh) { | ||||
|     auto& cc = jh.cc; | ||||
|  | ||||
|     cc.comment("//gen_instr_prologue"); | ||||
|     cc.inc(get_ptr_for(jh, traits::ICOUNT)); | ||||
|  | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_ptr_for(jh, traits::TRAP_STATE)); | ||||
|     cc.mov(get_ptr_for(jh, traits::PENDING_TRAP), current_trap_state); | ||||
|  | ||||
| } // namespace ${coreDef.name.toLowerCase()} | ||||
| } | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_instr_epilogue(jit_holder& jh) { | ||||
|     auto& cc = jh.cc; | ||||
|  | ||||
|     cc.comment("//gen_instr_epilogue"); | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_ptr_for(jh, traits::TRAP_STATE)); | ||||
|     cc.cmp(current_trap_state, 0); | ||||
|     cc.jne(jh.trap_entry); | ||||
| } | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_block_prologue(jit_holder& jh){ | ||||
|  | ||||
|     jh.pc = load_reg_from_mem(jh, traits::PC); | ||||
|     jh.next_pc = load_reg_from_mem(jh, traits::NEXT_PC); | ||||
| } | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_block_epilogue(jit_holder& jh){ | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     cc.comment("//gen_block_epilogue"); | ||||
|     cc.ret(jh.next_pc); | ||||
|  | ||||
|     cc.bind(jh.trap_entry); | ||||
|     this->write_back(jh); | ||||
|     this->gen_sync(jh, POST_SYNC, -1); | ||||
|  | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_ptr_for(jh, traits::TRAP_STATE)); | ||||
|  | ||||
|     x86::Gp current_pc = get_reg_for(jh, traits::PC); | ||||
|     cc.mov(current_pc, get_ptr_for(jh, traits::PC)); | ||||
|  | ||||
|     x86::Gp instr = cc.newInt32("instr"); | ||||
|     cc.mov(instr, 0); // FIXME:this is not correct | ||||
|     cc.comment("//enter trap call;"); | ||||
|     InvokeNode* call_enter_trap; | ||||
|     cc.invoke(&call_enter_trap, &enter_trap, FuncSignature::build<uint64_t, void*, uint64_t, uint64_t, uint64_t>()); | ||||
|     call_enter_trap->setArg(0, jh.arch_if_ptr); | ||||
|     call_enter_trap->setArg(1, current_trap_state); | ||||
|     call_enter_trap->setArg(2, current_pc); | ||||
|     call_enter_trap->setArg(3, instr); | ||||
|  | ||||
|     x86::Gp current_next_pc = get_reg_for(jh, traits::NEXT_PC); | ||||
|     cc.mov(current_next_pc, get_ptr_for(jh, traits::NEXT_PC)); | ||||
|     cc.mov(jh.next_pc, current_next_pc); | ||||
|  | ||||
|     cc.mov(get_ptr_for(jh, traits::LAST_BRANCH), std::numeric_limits<uint32_t>::max()); | ||||
|     cc.ret(jh.next_pc); | ||||
| } | ||||
| template <typename ARCH> | ||||
| inline void vm_impl<ARCH>::gen_raise(jit_holder& jh, uint16_t trap_id, uint16_t cause) { | ||||
|     auto& cc = jh.cc; | ||||
|     cc.comment("//gen_raise"); | ||||
|     auto tmp1 = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(tmp1, 0x80ULL << 24 | (cause << 16) | trap_id); | ||||
|     cc.mov(get_ptr_for(jh, traits::TRAP_STATE), tmp1); | ||||
|     cc.mov(jh.next_pc, std::numeric_limits<uint32_t>::max()); | ||||
| } | ||||
|  | ||||
| } // namespace tgc5c | ||||
|  | ||||
| template <> | ||||
| std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) { | ||||
| @@ -257,22 +352,30 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD | ||||
| } // namespace asmjit | ||||
| } // namespace iss | ||||
|  | ||||
| #include <iss/factory.h> | ||||
| #include <iss/arch/riscv_hart_m_p.h> | ||||
| #include <iss/arch/riscv_hart_mu_p.h> | ||||
| #include <iss/factory.h> | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|asmjit", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|asmjit", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
|             auto* vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    auto vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|asmjit", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|asmjit", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
|             auto* vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    auto vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }) | ||||
| }; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2021 MINRES Technologies GmbH | ||||
|  * Copyright (C) 20217-2024 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
| @@ -117,6 +117,8 @@ protected: | ||||
|         this->core.wait_until(type); | ||||
|     } | ||||
|  | ||||
|     uint64_t fetch_count{0}; | ||||
|  | ||||
|     using yield_t = boost::coroutines2::coroutine<void>::push_type; | ||||
|     using coro_t = boost::coroutines2::coroutine<void>::pull_type; | ||||
|     std::vector<coro_t> spawn_blocks; | ||||
| @@ -160,7 +162,7 @@ private: | ||||
|     }; | ||||
|  | ||||
|     decoding_tree_node* root {nullptr}; | ||||
|     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||
|     const std::array<instruction_descriptor, ${instructions.size()}> instr_descr = {{ | ||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||
|         {${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%> | ||||
|     }}; | ||||
| @@ -219,6 +221,7 @@ private: | ||||
|             });  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     typename arch::traits<ARCH>::opcode_e  decode_instr(decoding_tree_node* node, code_word_t word){ | ||||
|         if(!node->children.size()){ | ||||
|             if(node->instrs.size() == 1) return node->instrs[0].op; | ||||
| @@ -268,8 +271,12 @@ vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||
|     populate_decoding_tree(root); | ||||
| } | ||||
|  | ||||
| inline bool is_count_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT; | ||||
| inline bool is_icount_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::ICOUNT_LIMIT) == finish_cond_e::ICOUNT_LIMIT; | ||||
| } | ||||
|  | ||||
| inline bool is_fcount_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::FCOUNT_LIMIT) == finish_cond_e::FCOUNT_LIMIT; | ||||
| } | ||||
|  | ||||
| inline bool is_jump_to_self_enabled(finish_cond_e cond){ | ||||
| @@ -277,7 +284,7 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){ | ||||
| } | ||||
|  | ||||
| template <typename ARCH> | ||||
| typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){ | ||||
| typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t count_limit){ | ||||
|     auto pc=start; | ||||
|     auto* PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]); | ||||
|     auto* NEXT_PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]); | ||||
| @@ -290,7 +297,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|     auto *const data = reinterpret_cast<uint8_t*>(&instr); | ||||
|  | ||||
|     while(!this->core.should_stop() && | ||||
|             !(is_count_limit_enabled(cond) && icount >= icount_limit)){ | ||||
|             !(is_icount_limit_enabled(cond) && icount >= count_limit) && | ||||
|             !(is_fcount_limit_enabled(cond) && fetch_count >= count_limit)){ | ||||
|         fetch_count++; | ||||
|         if(fetch_ins(pc, data)!=iss::Ok){ | ||||
|             this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max()); | ||||
|             pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0); | ||||
| @@ -363,16 +372,24 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|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::${coreDef.name.toLowerCase()}>(); | ||||
| 		    auto vm = new interp::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
| 		    auto vm = new interp::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }) | ||||
| }; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2017, 2018 MINRES Technologies GmbH | ||||
|  * Copyright (C) 2017-2024 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
| @@ -99,16 +99,11 @@ protected: | ||||
|     std::tuple<continuation_e, BasicBlock *> gen_single_inst_behavior(virt_addr_t &, unsigned int &, BasicBlock *) override; | ||||
|  | ||||
|     void gen_leave_behavior(BasicBlock *leave_blk) override; | ||||
|  | ||||
|     void gen_raise_trap(uint16_t trap_id, uint16_t cause); | ||||
|  | ||||
|     void gen_leave_trap(unsigned lvl); | ||||
|  | ||||
|     void gen_wait(unsigned type); | ||||
|  | ||||
|     void gen_trap_behavior(BasicBlock *) override; | ||||
|  | ||||
|     void gen_trap_check(BasicBlock *bb); | ||||
|     void gen_instr_epilogue(BasicBlock *bb); | ||||
|  | ||||
|     inline Value *gen_reg_load(unsigned i, unsigned level = 0) { | ||||
|         return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false); | ||||
| @@ -153,7 +148,7 @@ private: | ||||
|  | ||||
|     decoding_tree_node* root {nullptr}; | ||||
|  | ||||
|     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||
|     const std::array<instruction_descriptor, ${instructions.size()}> instr_descr = {{ | ||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||
|         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ | ||||
|         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> | ||||
| @@ -162,20 +157,22 @@ private: | ||||
|     /* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %> | ||||
|     /* instruction ${idx}: ${instr.name} */ | ||||
|     std::tuple<continuation_e, BasicBlock*> __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, BasicBlock* bb){ | ||||
|         bb->setName(fmt::format("${instr.name}_0x{:X}",pc.val)); | ||||
|         this->gen_sync(PRE_SYNC,${idx}); | ||||
|         uint64_t PC = pc.val; | ||||
|         <%instr.fields.eachLine{%>${it} | ||||
|         <%}%>if(this->disass_enabled){ | ||||
|             /* generate console output when executing the command */<%instr.disass.eachLine{%> | ||||
|             ${it}<%}%> | ||||
|         } | ||||
|         bb->setName(fmt::format("${instr.name}_0x{:X}",pc.val)); | ||||
|         this->gen_sync(PRE_SYNC,${idx}); | ||||
|         auto cur_pc_val = this->gen_const(32,pc.val); | ||||
|         pc=pc+ ${instr.length/8}; | ||||
|         this->gen_set_pc(pc, traits::NEXT_PC); | ||||
|  | ||||
|         /*generate behavior*/ | ||||
|         <%instr.behavior.eachLine{%>${it} | ||||
|         <%}%> | ||||
|         this->gen_trap_check(bb); | ||||
|         this->gen_instr_epilogue(bb); | ||||
|     	this->gen_sync(POST_SYNC, ${idx}); | ||||
|         this->builder.CreateBr(bb); | ||||
|     	return returnValue;         | ||||
| @@ -195,7 +192,7 @@ private: | ||||
|         pc = pc + ((instr & 3) == 3 ? 4 : 2); | ||||
|         this->gen_raise_trap(0, 2);     // illegal instruction trap | ||||
| 		this->gen_sync(iss::POST_SYNC, instr_descr.size()); | ||||
|         this->gen_trap_check(this->leave_blk); | ||||
|         this->gen_instr_epilogue(this->leave_blk); | ||||
|         return std::make_tuple(BRANCH, nullptr); | ||||
|     }     | ||||
|     //decoding functionality | ||||
| @@ -301,18 +298,21 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | ||||
|     return (this->*f)(pc, instr, this_block); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | ||||
|     this->builder.SetInsertPoint(leave_blk); | ||||
|     this->builder.CreateRet(this->builder.CreateLoad(this->get_typeptr(traits::NEXT_PC),get_reg_ptr(traits::NEXT_PC), false)); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | ||||
|     auto *TRAP_val = this->gen_const(32, 0x80 << 24 | (cause << 16) | trap_id); | ||||
|     this->builder.CreateStore(TRAP_val, get_reg_ptr(traits::TRAP_STATE), true); | ||||
|     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), get_reg_ptr(traits::LAST_BRANCH), false); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_leave_trap(unsigned lvl) { | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_leave_trap(unsigned lvl) { | ||||
|     std::vector<Value *> args{ this->core_ptr, ConstantInt::get(getContext(), APInt(64, lvl)) }; | ||||
|     this->builder.CreateCall(this->mod->getFunction("leave_trap"), args); | ||||
|     auto *PC_val = this->gen_read_mem(traits::CSR, (lvl << 8) + 0x41, traits::XLEN / 8); | ||||
| @@ -320,12 +320,14 @@ template <typename ARCH> void vm_impl<ARCH>::gen_leave_trap(unsigned lvl) { | ||||
|     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), get_reg_ptr(traits::LAST_BRANCH), false); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_wait(unsigned type) { | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_wait(unsigned type) { | ||||
|     std::vector<Value *> args{ this->core_ptr, ConstantInt::get(getContext(), APInt(64, type)) }; | ||||
|     this->builder.CreateCall(this->mod->getFunction("wait"), args); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | ||||
| template <typename ARCH>  | ||||
| void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | ||||
|     this->builder.SetInsertPoint(trap_blk); | ||||
|     this->gen_sync(POST_SYNC, -1); //TODO get right InstrId | ||||
|     auto *trap_state_val = this->builder.CreateLoad(this->get_typeptr(traits::TRAP_STATE), get_reg_ptr(traits::TRAP_STATE), true); | ||||
| @@ -338,7 +340,8 @@ template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_ | ||||
|     this->builder.CreateRet(trap_addr_val); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) { | ||||
| template <typename ARCH> | ||||
| void vm_impl<ARCH>::gen_instr_epilogue(BasicBlock *bb) { | ||||
|     auto* target_bb = BasicBlock::Create(this->mod->getContext(), "", this->func, bb); | ||||
|     auto *v = this->builder.CreateLoad(this->get_typeptr(traits::TRAP_STATE), get_reg_ptr(traits::TRAP_STATE), true); | ||||
|     this->gen_cond_branch(this->builder.CreateICmp( | ||||
| @@ -359,22 +362,30 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD | ||||
| } // namespace llvm | ||||
| } // namespace iss | ||||
|  | ||||
| #include <iss/factory.h> | ||||
| #include <iss/arch/riscv_hart_m_p.h> | ||||
| #include <iss/arch/riscv_hart_mu_p.h> | ||||
| #include <iss/factory.h> | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|llvm", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|llvm", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
|             auto* vm = new llvm::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    auto vm = new llvm::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||
|             if(init_data){ | ||||
|                 auto* cb = reinterpret_cast<std::function<void(arch_if*, arch::traits<arch::${coreDef.name.toLowerCase()}>::reg_t*, arch::traits<arch::${coreDef.name.toLowerCase()}>::reg_t*)>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|llvm", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|llvm", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
|             auto* vm = new llvm::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    auto vm = new llvm::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||
|             if(init_data){ | ||||
|                 auto* cb = reinterpret_cast<std::function<void(arch_if*, arch::traits<arch::${coreDef.name.toLowerCase()}>::reg_t*, arch::traits<arch::${coreDef.name.toLowerCase()}>::reg_t*)>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }) | ||||
| }; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2020 MINRES Technologies GmbH | ||||
|  * Copyright (C) 2020-2024 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
| @@ -148,7 +148,7 @@ private: | ||||
|  | ||||
|     decoding_tree_node* root {nullptr}; | ||||
|  | ||||
|     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||
|     const std::array<instruction_descriptor, ${instructions.size()}> instr_descr = {{ | ||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||
|         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ | ||||
|         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> | ||||
| @@ -292,7 +292,7 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) { | ||||
|     tu("  *trap_state = {:#x};", 0x80 << 24 | (cause << 16) | trap_id); | ||||
|     tu.store(traits::LAST_BRANCH, tu.constant(std::numeric_limits<uint32_t>::max(), 32)); | ||||
|     tu.store(traits::NEXT_PC, tu.constant(std::numeric_limits<uint32_t>::max(), 32)); | ||||
| } | ||||
|  | ||||
| template <typename ARCH> void vm_impl<ARCH>::gen_leave_trap(tu_builder& tu, unsigned lvl) { | ||||
| @@ -323,22 +323,30 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD | ||||
| } // namesapce tcc | ||||
| } // namespace iss | ||||
|  | ||||
| #include <iss/factory.h> | ||||
| #include <iss/arch/riscv_hart_m_p.h> | ||||
| #include <iss/arch/riscv_hart_mu_p.h> | ||||
| #include <iss/factory.h> | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
| 		    auto vm = new tcc::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||
| 		    auto vm = new tcc::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*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::${coreDef.name.toLowerCase()}>::reg_t>*>(init_data); | ||||
|                 cpu->set_semihosting_callback(*cb); | ||||
|             } | ||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||
|         }) | ||||
| }; | ||||
|   | ||||
| @@ -327,7 +327,7 @@ set(OTHERS | ||||
|  | ||||
| set(LIB_SOURCES ${PRIMITIVES} ${SPECIALIZE} ${OTHERS}) | ||||
|  | ||||
| add_library(softfloat ${LIB_SOURCES}) | ||||
| add_library(softfloat STATIC ${LIB_SOURCES}) | ||||
| set_property(TARGET softfloat PROPERTY C_STANDARD 99) | ||||
| target_compile_definitions(softfloat PRIVATE  | ||||
| 	SOFTFLOAT_ROUND_ODD  | ||||
| @@ -347,7 +347,7 @@ set_target_properties(softfloat PROPERTIES | ||||
|  | ||||
| install(TARGETS softfloat | ||||
|   EXPORT ${PROJECT_NAME}Targets            # for downstream dependencies | ||||
|   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs   # static lib | ||||
|   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/static COMPONENT libs   # static lib | ||||
|   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs   # shared lib | ||||
|   FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libs # for mac | ||||
|   PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel   # headers for mac (note the different component -> different package) | ||||
|   | ||||
| @@ -35,8 +35,14 @@ | ||||
| #ifndef _RISCV_HART_COMMON | ||||
| #define _RISCV_HART_COMMON | ||||
|  | ||||
| #include "iss/arch_if.h" | ||||
| #include <cstdint> | ||||
| #include <elfio/elfio.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include <iss/arch_if.h> | ||||
| #include <iss/log_categories.h> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <util/logging.h> | ||||
|  | ||||
| namespace iss { | ||||
| namespace arch { | ||||
| @@ -296,6 +302,61 @@ inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| struct riscv_hart_common { | ||||
|     riscv_hart_common(){}; | ||||
|     ~riscv_hart_common(){}; | ||||
|     std::unordered_map<std::string, uint64_t> symbol_table; | ||||
|  | ||||
|     std::unordered_map<std::string, uint64_t> get_sym_table(std::string name) { | ||||
|         if(!symbol_table.empty()) | ||||
|             return symbol_table; | ||||
|         FILE* fp = fopen(name.c_str(), "r"); | ||||
|         if(fp) { | ||||
|             std::array<char, 5> buf; | ||||
|             auto n = fread(buf.data(), 1, 4, fp); | ||||
|             fclose(fp); | ||||
|             if(n != 4) | ||||
|                 throw std::runtime_error("input file has insufficient size"); | ||||
|             buf[4] = 0; | ||||
|             if(strcmp(buf.data() + 1, "ELF") == 0) { | ||||
|                 // 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_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"); | ||||
|                 const auto sym_sec = reader.sections[".symtab"]; | ||||
|                 if(SHT_SYMTAB == sym_sec->get_type() || SHT_DYNSYM == sym_sec->get_type()) { | ||||
|                     ELFIO::symbol_section_accessor symbols(reader, sym_sec); | ||||
|                     auto sym_no = symbols.get_symbols_num(); | ||||
|                     std::string name; | ||||
|                     ELFIO::Elf64_Addr value = 0; | ||||
|                     ELFIO::Elf_Xword size = 0; | ||||
|                     unsigned char bind = 0; | ||||
|                     unsigned char type = 0; | ||||
|                     ELFIO::Elf_Half section = 0; | ||||
|                     unsigned char other = 0; | ||||
|                     for(auto i = 0U; i < sym_no; ++i) { | ||||
|                         symbols.get_symbol(i, name, value, size, bind, type, section, other); | ||||
|                         if(name != "") { | ||||
|                             this->symbol_table[name] = value; | ||||
| #ifndef NDEBUG | ||||
|                             CPPLOG(DEBUG) << "Found Symbol " << name; | ||||
| #endif | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 return symbol_table; | ||||
|             } | ||||
|             throw std::runtime_error(fmt::format("memory load file {} is not a valid elf file", name)); | ||||
|         } else | ||||
|             throw std::runtime_error(fmt::format("memory load file not found, check if {} is a valid file", name)); | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| } // namespace arch | ||||
| } // namespace iss | ||||
|   | ||||
| @@ -40,6 +40,7 @@ | ||||
| #include "iss/log_categories.h" | ||||
| #include "iss/vm_if.h" | ||||
| #include "riscv_hart_common.h" | ||||
| #include <stdexcept> | ||||
| #ifndef FMT_HEADER_ONLY | ||||
| #define FMT_HEADER_ONLY | ||||
| #endif | ||||
| @@ -55,6 +56,8 @@ | ||||
| #include <util/ities.h> | ||||
| #include <util/sparse_array.h> | ||||
|  | ||||
| #include <iss/semihosting/semihosting.h> | ||||
|  | ||||
| #if defined(__GNUC__) | ||||
| #define likely(x) __builtin_expect(!!(x), 1) | ||||
| #define unlikely(x) __builtin_expect(!!(x), 0) | ||||
| @@ -66,7 +69,7 @@ | ||||
| namespace iss { | ||||
| namespace arch { | ||||
|  | ||||
| template <typename BASE, features_e FEAT = FEAT_NONE> class riscv_hart_m_p : public BASE { | ||||
| template <typename BASE, features_e FEAT = FEAT_NONE> class riscv_hart_m_p : public BASE, public riscv_hart_common { | ||||
| protected: | ||||
|     const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}}; | ||||
|     const std::array<const char*, 16> trap_str = {{"" | ||||
| @@ -290,6 +293,8 @@ public: | ||||
|  | ||||
|     void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } | ||||
|  | ||||
|     void set_semihosting_callback(semihosting_cb_t<reg_t> cb) { semihosting_cb = cb; }; | ||||
|  | ||||
| protected: | ||||
|     struct riscv_instrumentation_if : public iss::instrumentation_if { | ||||
|  | ||||
| @@ -322,6 +327,8 @@ protected: | ||||
|  | ||||
|         unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; } | ||||
|  | ||||
|         std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); } | ||||
|  | ||||
|         riscv_hart_m_p<BASE, FEAT>& arch; | ||||
|     }; | ||||
|  | ||||
| @@ -344,9 +351,11 @@ protected: | ||||
|     reg_t fault_data; | ||||
|     uint64_t tohost = tohost_dflt; | ||||
|     uint64_t fromhost = fromhost_dflt; | ||||
|     unsigned to_host_wr_cnt = 0; | ||||
|     bool tohost_lower_written = false; | ||||
|     riscv_instrumentation_if instr_if; | ||||
|  | ||||
|     semihosting_cb_t<reg_t> semihosting_cb; | ||||
|  | ||||
|     using mem_type = util::sparse_array<uint8_t, 1ULL << 32>; | ||||
|     using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>; | ||||
|     using csr_page_type = typename csr_type::page_type; | ||||
| @@ -419,6 +428,7 @@ protected: | ||||
|     feature_config cfg; | ||||
|     unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U}; | ||||
|     inline bool debug_mode_active() { return this->reg.PRIV & 0x4; } | ||||
|  | ||||
|     std::pair<std::function<mem_read_f>, std::function<mem_write_f>> replace_mem_access(std::function<mem_read_f> rd, | ||||
|                                                                                         std::function<mem_write_f> wr) { | ||||
|         std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate}; | ||||
| @@ -563,6 +573,12 @@ riscv_hart_m_p<BASE, FEAT>::riscv_hart_m_p(feature_config cfg) | ||||
| } | ||||
|  | ||||
| template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m_p<BASE, FEAT>::load_file(std::string name, int type) { | ||||
|     get_sym_table(name); | ||||
|     try { | ||||
|         tohost = symbol_table.at("tohost"); | ||||
|         fromhost = symbol_table.at("fromhost"); | ||||
|     } catch(std::out_of_range& e) { | ||||
|     } | ||||
|     FILE* fp = fopen(name.c_str(), "r"); | ||||
|     if(fp) { | ||||
|         std::array<char, 5> buf; | ||||
| @@ -593,31 +609,11 @@ template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m | ||||
|                     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) | ||||
|                         LOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                         CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                 } | ||||
|             } | ||||
|             for(const auto sec : reader.sections) { | ||||
|                 if(sec->get_name() == ".symtab") { | ||||
|                     if(SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type()) { | ||||
|                         ELFIO::symbol_section_accessor symbols(reader, sec); | ||||
|                         auto sym_no = symbols.get_symbols_num(); | ||||
|                         std::string name; | ||||
|                         ELFIO::Elf64_Addr value = 0; | ||||
|                         ELFIO::Elf_Xword size = 0; | ||||
|                         unsigned char bind = 0; | ||||
|                         unsigned char type = 0; | ||||
|                         ELFIO::Elf_Half section = 0; | ||||
|                         unsigned char other = 0; | ||||
|                         for(auto i = 0U; i < sym_no; ++i) { | ||||
|                             symbols.get_symbol(i, name, value, size, bind, type, section, other); | ||||
|                             if(name == "tohost") { | ||||
|                                 tohost = value; | ||||
|                             } else if(name == "fromhost") { | ||||
|                                 fromhost = value; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if(sec->get_name() == ".tohost") { | ||||
|                 if(sec->get_name() == ".tohost") { | ||||
|                     tohost = sec->get_address(); | ||||
|                     fromhost = tohost + 0x40; | ||||
|                 } | ||||
| @@ -647,11 +643,11 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read(const address_type type, const acce | ||||
|                                              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; | ||||
|         CPPLOG(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; | ||||
|         CPPLOG(TRACEALL) << "fetch of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } else { | ||||
|         LOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|         CPPLOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -733,23 +729,23 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc | ||||
|     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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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; | ||||
|         CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -784,7 +780,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc | ||||
|                     res = write_mem(phys_addr, length, data); | ||||
|                 } | ||||
|                 if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) { | ||||
|                     this->reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault) | ||||
|                     this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault) | ||||
|                     fault_data = addr; | ||||
|                 } | ||||
|                 return res; | ||||
| @@ -801,7 +797,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc | ||||
|             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 | ||||
|                     // CPPLOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send | ||||
|                     // '"<<uart_buf.str()<<"'"; | ||||
|                     std::cout << uart_buf.str(); | ||||
|                     uart_buf.str(""); | ||||
| @@ -1098,9 +1094,10 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned len | ||||
| template <typename BASE, features_e FEAT> | ||||
| iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) { | ||||
|     switch(paddr.val) { | ||||
|     // TODO remove UART, Peripherals should not be part of the ISS | ||||
|     case 0xFFFF0000: // UART0 base, TXFIFO reg | ||||
|         if(((char)data[0]) == '\n' || data[0] == 0) { | ||||
|             LOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             CPPLOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             uart_buf.str(""); | ||||
|         } else if(((char)data[0]) != '\r') | ||||
|             uart_buf << (char)data[0]; | ||||
| @@ -1115,36 +1112,34 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned le | ||||
|             auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost); | ||||
|             if(tohost_lower || tohost_upper) { | ||||
|                 uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)); | ||||
|                 if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { | ||||
|                 // in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write | ||||
|                 if(tohost_upper && (tohost_lower || tohost_lower_written)) { | ||||
|                     switch(hostvar >> 48) { | ||||
|                     case 0: | ||||
|                         if(hostvar != 0x1) { | ||||
|                             LOG(FATAL) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar | ||||
|                             CPPLOG(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 | ||||
|                             CPPLOG(INFO) << "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; | ||||
|                     case 0x0101: { | ||||
|                         char c = static_cast<char>(hostvar & 0xff); | ||||
|                         if(c == '\n' || c == 0) { | ||||
|                             LOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             uart_buf.str(""); | ||||
|                         } else | ||||
|                             uart_buf << c; | ||||
|                         to_host_wr_cnt = 0; | ||||
|                     } break; | ||||
|                     default: | ||||
|                         break; | ||||
|                     } | ||||
|                     tohost_lower_written = false; | ||||
|                 } else if(tohost_lower) | ||||
|                     to_host_wr_cnt++; | ||||
|                     tohost_lower_written = true; | ||||
|             } else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) { | ||||
|                 uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask)); | ||||
|                 *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; | ||||
| @@ -1253,6 +1248,31 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_m_p<BASE, FEAT>::e | ||||
|             } else { | ||||
|                 csr[mtval] = addr; | ||||
|             } | ||||
|             if(semihosting_cb) { | ||||
|                 // Check for semihosting call | ||||
|                 phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4); | ||||
|                 std::array<uint8_t, 8> data; | ||||
|                 // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 | ||||
|                 this->read_mem(p_addr, 4, data.data()); | ||||
|                 p_addr.val += 8; | ||||
|                 this->read_mem(p_addr, 4, data.data() + 4); | ||||
|  | ||||
|                 const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; | ||||
|                 if(data == ref_data) { | ||||
|                     this->reg.NEXT_PC = addr + 8; | ||||
|  | ||||
|                     std::array<char, 32> buffer; | ||||
| #if defined(_MSC_VER) | ||||
|                     sprintf(buffer.data(), "0x%016llx", addr); | ||||
| #else | ||||
|                     sprintf(buffer.data(), "0x%016lx", addr); | ||||
| #endif | ||||
|                     CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; | ||||
|  | ||||
|                     semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); | ||||
|                     return this->reg.NEXT_PC; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case 4: | ||||
|         case 6: | ||||
|   | ||||
| @@ -55,6 +55,8 @@ | ||||
| #include <util/ities.h> | ||||
| #include <util/sparse_array.h> | ||||
|  | ||||
| #include <iss/semihosting/semihosting.h> | ||||
|  | ||||
| #if defined(__GNUC__) | ||||
| #define likely(x) __builtin_expect(!!(x), 1) | ||||
| #define unlikely(x) __builtin_expect(!!(x), 0) | ||||
| @@ -66,7 +68,7 @@ | ||||
| namespace iss { | ||||
| namespace arch { | ||||
|  | ||||
| template <typename BASE> class riscv_hart_msu_vp : public BASE { | ||||
| template <typename BASE> class riscv_hart_msu_vp : public BASE, public riscv_hart_common { | ||||
| protected: | ||||
|     const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}}; | ||||
|     const std::array<const char*, 16> trap_str = {{"" | ||||
| @@ -341,6 +343,8 @@ public: | ||||
|  | ||||
|     void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } | ||||
|  | ||||
|     void set_semihosting_callback(std::function<void(arch_if*, reg_t, reg_t)>& cb) { semihosting_cb = cb; }; | ||||
|  | ||||
| protected: | ||||
|     struct riscv_instrumentation_if : public iss::instrumentation_if { | ||||
|  | ||||
| @@ -373,6 +377,8 @@ protected: | ||||
|  | ||||
|         unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; } | ||||
|  | ||||
|         std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); } | ||||
|  | ||||
|         riscv_hart_msu_vp<BASE>& arch; | ||||
|     }; | ||||
|  | ||||
| @@ -395,9 +401,11 @@ protected: | ||||
|     std::array<vm_info, 2> vm; | ||||
|     uint64_t tohost = tohost_dflt; | ||||
|     uint64_t fromhost = fromhost_dflt; | ||||
|     unsigned to_host_wr_cnt = 0; | ||||
|     bool tohost_lower_written = false; | ||||
|     riscv_instrumentation_if instr_if; | ||||
|  | ||||
|     std::function<void(arch_if*, reg_t, reg_t)> semihosting_cb; | ||||
|  | ||||
|     using mem_type = util::sparse_array<uint8_t, 1ULL << 32>; | ||||
|     using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>; | ||||
|     using csr_page_type = typename csr_type::page_type; | ||||
| @@ -585,7 +593,7 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_msu_vp<BASE>::load | ||||
|                     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) | ||||
|                         LOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                         CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                 } | ||||
|             } | ||||
|             for(const auto sec : reader.sections) { | ||||
| @@ -626,11 +634,11 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_ | ||||
|                                           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; | ||||
|         CPPLOG(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; | ||||
|         CPPLOG(TRACEALL) << "fetch of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } else { | ||||
|         LOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|         CPPLOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -720,23 +728,23 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access | ||||
|     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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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; | ||||
|         CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -781,7 +789,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access | ||||
|             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 | ||||
|                     // CPPLOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send | ||||
|                     // '"<<uart_buf.str()<<"'"; | ||||
|                     std::cout << uart_buf.str(); | ||||
|                     uart_buf.str(""); | ||||
| @@ -1077,7 +1085,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add | ||||
|     switch(paddr.val) { | ||||
|     case 0xFFFF0000: // UART0 base, TXFIFO reg | ||||
|         if(((char)data[0]) == '\n' || data[0] == 0) { | ||||
|             LOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             CPPLOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             uart_buf.str(""); | ||||
|         } else if(((char)data[0]) != '\r') | ||||
|             uart_buf << (char)data[0]; | ||||
| @@ -1092,34 +1100,37 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add | ||||
|             auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost); | ||||
|             if(tohost_lower || tohost_upper) { | ||||
|                 uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)); | ||||
|                 if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { | ||||
|                 // in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write | ||||
|                 if(tohost_upper && (tohost_lower || tohost_lower_written)) { | ||||
|                     switch(hostvar >> 48) { | ||||
|                     case 0: | ||||
|                         if(hostvar != 0x1) { | ||||
|                             LOG(FATAL) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar | ||||
|                             CPPLOG(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 | ||||
|                             CPPLOG(INFO) << "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; | ||||
|                         // throw(iss::simulation_stopped(hostvar)); | ||||
|                     case 0x0101: { | ||||
|                         char c = static_cast<char>(hostvar & 0xff); | ||||
|                         if(c == '\n' || c == 0) { | ||||
|                             LOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             uart_buf.str(""); | ||||
|                         } else | ||||
|                             uart_buf << c; | ||||
|                         to_host_wr_cnt = 0; | ||||
|                     } break; | ||||
|                     default: | ||||
|                         break; | ||||
|                     } | ||||
|                     tohost_lower_written = false; | ||||
|                 } else if(tohost_lower) | ||||
|                     to_host_wr_cnt++; | ||||
|                     tohost_lower_written = true; | ||||
|             } else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) { | ||||
|                 uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask)); | ||||
|                 *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; | ||||
| @@ -1304,6 +1315,31 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f | ||||
|             // csr[dpc] = addr; | ||||
|             // csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi) | ||||
|             csr[utval | (new_priv << 8)] = addr; | ||||
|             if(semihosting_cb) { | ||||
|                 // Check for semihosting call | ||||
|                 phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4); | ||||
|                 std::array<uint8_t, 8> data; | ||||
|                 // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 | ||||
|                 this->read_mem(p_addr, 4, data.data()); | ||||
|                 p_addr.val += 8; | ||||
|                 this->read_mem(p_addr, 4, data.data() + 4); | ||||
|  | ||||
|                 const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; | ||||
|                 if(data == ref_data) { | ||||
|                     this->reg.NEXT_PC = addr + 8; | ||||
|  | ||||
|                     std::array<char, 32> buffer; | ||||
| #if defined(_MSC_VER) | ||||
|                     sprintf(buffer.data(), "0x%016llx", addr); | ||||
| #else | ||||
|                     sprintf(buffer.data(), "0x%016lx", addr); | ||||
| #endif | ||||
|                     CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; | ||||
|  | ||||
|                     semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/); | ||||
|                     return this->reg.NEXT_PC; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case 4: | ||||
|         case 6: | ||||
| @@ -1321,7 +1357,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f | ||||
|         this->reg.pending_trap = 0; | ||||
|     } | ||||
|     size_t adr = ucause | (new_priv << 8); | ||||
|     csr[adr] = (trap_id << 31) + cause; | ||||
|     csr[adr] = (trap_id << (traits<BASE>::XLEN - 1)) + cause; | ||||
|     // update mstatus | ||||
|     // xPP field of mstatus is written with the active privilege mode at the time | ||||
|     // of the trap; the x PIE field of mstatus | ||||
|   | ||||
| @@ -55,6 +55,8 @@ | ||||
| #include <util/ities.h> | ||||
| #include <util/sparse_array.h> | ||||
|  | ||||
| #include <iss/semihosting/semihosting.h> | ||||
|  | ||||
| #if defined(__GNUC__) | ||||
| #define likely(x) __builtin_expect(!!(x), 1) | ||||
| #define unlikely(x) __builtin_expect(!!(x), 0) | ||||
| @@ -66,7 +68,7 @@ | ||||
| namespace iss { | ||||
| namespace arch { | ||||
|  | ||||
| template <typename BASE, features_e FEAT = FEAT_NONE> class riscv_hart_mu_p : public BASE { | ||||
| template <typename BASE, features_e FEAT = FEAT_NONE> class riscv_hart_mu_p : public BASE, public riscv_hart_common { | ||||
| protected: | ||||
|     const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}}; | ||||
|     const std::array<const char*, 16> trap_str = {{"" | ||||
| @@ -317,6 +319,8 @@ public: | ||||
|  | ||||
|     void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } | ||||
|  | ||||
|     void set_semihosting_callback(semihosting_cb_t<reg_t> cb) { semihosting_cb = cb; }; | ||||
|  | ||||
| protected: | ||||
|     struct riscv_instrumentation_if : public iss::instrumentation_if { | ||||
|  | ||||
| @@ -349,6 +353,8 @@ protected: | ||||
|  | ||||
|         unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; } | ||||
|  | ||||
|         std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); } | ||||
|  | ||||
|         riscv_hart_mu_p<BASE, FEAT>& arch; | ||||
|     }; | ||||
|  | ||||
| @@ -371,9 +377,11 @@ protected: | ||||
|     reg_t fault_data; | ||||
|     uint64_t tohost = tohost_dflt; | ||||
|     uint64_t fromhost = fromhost_dflt; | ||||
|     unsigned to_host_wr_cnt = 0; | ||||
|     bool tohost_lower_written = false; | ||||
|     riscv_instrumentation_if instr_if; | ||||
|  | ||||
|     semihosting_cb_t<reg_t> semihosting_cb; | ||||
|  | ||||
|     using mem_type = util::sparse_array<uint8_t, 1ULL << 32>; | ||||
|     using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>; | ||||
|     using csr_page_type = typename csr_type::page_type; | ||||
| @@ -672,7 +680,7 @@ template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m | ||||
|                     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) | ||||
|                         LOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                         CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address(); | ||||
|                 } | ||||
|             } | ||||
|             for(const auto sec : reader.sections) { | ||||
| @@ -812,11 +820,11 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc | ||||
|                                               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; | ||||
|         CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr; | ||||
|     } else if(is_fetch(access)) { | ||||
|         LOG(TRACEALL) << "fetch of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|         CPPLOG(TRACEALL) << "fetch of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } else { | ||||
|         LOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|         CPPLOG(TRACE) << "read of " << length << " bytes  @addr 0x" << std::hex << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -907,23 +915,23 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac | ||||
|     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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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" | ||||
|         CPPLOG(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; | ||||
|         CPPLOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; | ||||
|     } | ||||
| #endif | ||||
|     try { | ||||
| @@ -984,7 +992,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac | ||||
|             case 0x10023000: // UART1 base, TXFIFO reg | ||||
|                 uart_buf << (char)data[0]; | ||||
|                 if(((char)data[0]) == '\n' || data[0] == 0) { | ||||
|                     // LOG(INFO)<<"UART"<<((addr>>16)&0x3)<<" send | ||||
|                     // CPPLOG(INFO)<<"UART"<<((addr>>16)&0x3)<<" send | ||||
|                     // '"<<uart_buf.str()<<"'"; | ||||
|                     std::cout << uart_buf.str(); | ||||
|                     uart_buf.str(""); | ||||
| @@ -1317,9 +1325,10 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le | ||||
| template <typename BASE, features_e FEAT> | ||||
| iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) { | ||||
|     switch(paddr.val) { | ||||
|     // TODO remove UART, Peripherals should not be part of the ISS | ||||
|     case 0xFFFF0000: // UART0 base, TXFIFO reg | ||||
|         if(((char)data[0]) == '\n' || data[0] == 0) { | ||||
|             LOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             CPPLOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'"; | ||||
|             uart_buf.str(""); | ||||
|         } else if(((char)data[0]) != '\r') | ||||
|             uart_buf << (char)data[0]; | ||||
| @@ -1334,34 +1343,37 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l | ||||
|             auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost); | ||||
|             if(tohost_lower || tohost_upper) { | ||||
|                 uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)); | ||||
|                 if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { | ||||
|                 // in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write | ||||
|                 if(tohost_upper && (tohost_lower || tohost_lower_written)) { | ||||
|                     switch(hostvar >> 48) { | ||||
|                     case 0: | ||||
|                         if(hostvar != 0x1) { | ||||
|                             LOG(FATAL) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar | ||||
|                             CPPLOG(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 | ||||
|                             CPPLOG(INFO) << "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; | ||||
|                         // throw(iss::simulation_stopped(hostvar)); | ||||
|                     case 0x0101: { | ||||
|                         char c = static_cast<char>(hostvar & 0xff); | ||||
|                         if(c == '\n' || c == 0) { | ||||
|                             LOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             CPPLOG(INFO) << "tohost send '" << uart_buf.str() << "'"; | ||||
|                             uart_buf.str(""); | ||||
|                         } else | ||||
|                             uart_buf << c; | ||||
|                         to_host_wr_cnt = 0; | ||||
|                     } break; | ||||
|                     default: | ||||
|                         break; | ||||
|                     } | ||||
|                     tohost_lower_written = false; | ||||
|                 } else if(tohost_lower) | ||||
|                     to_host_wr_cnt++; | ||||
|                     tohost_lower_written = true; | ||||
|             } else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) { | ||||
|                 uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask)); | ||||
|                 *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; | ||||
| @@ -1474,6 +1486,31 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>:: | ||||
|             } else { | ||||
|                 csr[utval | (new_priv << 8)] = addr; | ||||
|             } | ||||
|             if(semihosting_cb) { | ||||
|                 // Check for semihosting call | ||||
|                 phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4); | ||||
|                 std::array<uint8_t, 8> data; | ||||
|                 // check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07 | ||||
|                 this->read_mem(p_addr, 4, data.data()); | ||||
|                 p_addr.val += 8; | ||||
|                 this->read_mem(p_addr, 4, data.data() + 4); | ||||
|  | ||||
|                 const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40}; | ||||
|                 if(data == ref_data) { | ||||
|                     this->reg.NEXT_PC = addr + 8; | ||||
|  | ||||
|                     std::array<char, 32> buffer; | ||||
| #if defined(_MSC_VER) | ||||
|                     sprintf(buffer.data(), "0x%016llx", addr); | ||||
| #else | ||||
|                     sprintf(buffer.data(), "0x%016lx", addr); | ||||
| #endif | ||||
|                     CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; | ||||
|  | ||||
|                     semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); | ||||
|                     return this->reg.NEXT_PC; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case 4: | ||||
|         case 6: | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -174,7 +174,7 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::current_thread_query | ||||
| } | ||||
|  | ||||
| template <typename ARCH> status riscv_target_adapter<ARCH>::read_registers(std::vector<uint8_t>& data, std::vector<uint8_t>& avail) { | ||||
|     LOG(TRACE) << "reading target registers"; | ||||
|     CPPLOG(TRACE) << "reading target registers"; | ||||
|     // return idx<0?:; | ||||
|     data.clear(); | ||||
|     avail.clear(); | ||||
| @@ -328,9 +328,9 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::add_break(break_type | ||||
|         auto saddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr}); | ||||
|         auto eaddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr + length}); | ||||
|         target_adapter_base::bp_lut.addEntry(++target_adapter_base::bp_count, saddr.val, eaddr.val - saddr.val); | ||||
|         LOG(TRACE) << "Adding breakpoint with handle " << target_adapter_base::bp_count << " for addr 0x" << std::hex << saddr.val | ||||
|         CPPLOG(TRACE) << "Adding breakpoint with handle " << target_adapter_base::bp_count << " for addr 0x" << std::hex << saddr.val | ||||
|                       << std::dec; | ||||
|         LOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|         CPPLOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|         return Ok; | ||||
|     } | ||||
|     } | ||||
| @@ -345,13 +345,13 @@ template <typename ARCH> status riscv_target_adapter<ARCH>::remove_break(break_t | ||||
|         auto saddr = map_addr({iss::access_type::FETCH, iss::address_type::PHYSICAL, 0, addr}); | ||||
|         unsigned handle = target_adapter_base::bp_lut.getEntry(saddr.val); | ||||
|         if(handle) { | ||||
|             LOG(TRACE) << "Removing breakpoint with handle " << handle << " for addr 0x" << std::hex << saddr.val << std::dec; | ||||
|             CPPLOG(TRACE) << "Removing breakpoint with handle " << handle << " for addr 0x" << std::hex << saddr.val << std::dec; | ||||
|             // TODO: check length of addr range | ||||
|             target_adapter_base::bp_lut.removeEntry(handle); | ||||
|             LOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|             CPPLOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|             return Ok; | ||||
|         } | ||||
|         LOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|         CPPLOG(TRACE) << "Now having " << target_adapter_base::bp_lut.size() << " breakpoints"; | ||||
|         return Err; | ||||
|     } | ||||
|     } | ||||
|   | ||||
| @@ -61,7 +61,7 @@ bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& | ||||
|             try { | ||||
|                 auto root = YAML::LoadAll(is); | ||||
|                 if(root.size() != 1) { | ||||
|                     LOG(ERR) << "Too many root nodes in YAML file " << config_file_name; | ||||
|                     CPPLOG(ERR) << "Too many root nodes in YAML file " << config_file_name; | ||||
|                 } | ||||
|                 for(auto p : root[0]) { | ||||
|                     auto isa_subset = p.first; | ||||
| @@ -87,11 +87,11 @@ bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& | ||||
|                     } | ||||
|                 } | ||||
|             } catch(YAML::ParserException& e) { | ||||
|                 LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what(); | ||||
|                 CPPLOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what(); | ||||
|                 return false; | ||||
|             } | ||||
|         } else { | ||||
|             LOG(ERR) << "Could not open input file " << config_file_name; | ||||
|             CPPLOG(ERR) << "Could not open input file " << config_file_name; | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -47,7 +47,7 @@ iss::plugin::instruction_count::instruction_count(std::string config_file_name) | ||||
|             try { | ||||
|                 auto root = YAML::LoadAll(is); | ||||
|                 if(root.size() != 1) { | ||||
|                     LOG(ERR) << "Too many rro nodes in YAML file " << config_file_name; | ||||
|                     CPPLOG(ERR) << "Too many rro nodes in YAML file " << config_file_name; | ||||
|                 } | ||||
|                 for(auto p : root[0]) { | ||||
|                     auto isa_subset = p.first; | ||||
| @@ -69,10 +69,10 @@ iss::plugin::instruction_count::instruction_count(std::string config_file_name) | ||||
|                 } | ||||
|                 rep_counts.resize(delays.size()); | ||||
|             } catch(YAML::ParserException& e) { | ||||
|                 LOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what(); | ||||
|                 CPPLOG(ERR) << "Could not parse input file " << config_file_name << ", reason: " << e.what(); | ||||
|             } | ||||
|         } else { | ||||
|             LOG(ERR) << "Could not open input file " << config_file_name; | ||||
|             CPPLOG(ERR) << "Could not open input file " << config_file_name; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -81,7 +81,7 @@ iss::plugin::instruction_count::~instruction_count() { | ||||
|     size_t idx = 0; | ||||
|     for(auto it : delays) { | ||||
|         if(rep_counts[idx] > 0 && it.instr_name.find("__" != 0)) | ||||
|             LOG(INFO) << it.instr_name << ";" << rep_counts[idx]; | ||||
|             CPPLOG(INFO) << it.instr_name << ";" << rep_counts[idx]; | ||||
|         idx++; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										297
									
								
								src/iss/semihosting/semihosting.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/iss/semihosting/semihosting.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | ||||
| #include "semihosting.h" | ||||
| #include <chrono> | ||||
| #include <cstdint> | ||||
| #include <iss/vm_types.h> | ||||
| #include <map> | ||||
| #include <stdexcept> | ||||
| // explanation of syscalls can be found at https://github.com/SpinalHDL/openocd_riscv/blob/riscv_spinal/src/target/semihosting_common.h | ||||
|  | ||||
| const char* SYS_OPEN_MODES_STRS[] = {"r", "rb", "r+", "r+b", "w", "wb", "w+", "w+b", "a", "ab", "a+", "a+b"}; | ||||
|  | ||||
| template <typename T> T sh_read_field(iss::arch_if* arch_if_ptr, T addr, int len = 4) { | ||||
|     uint8_t bytes[4]; | ||||
|     auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, addr, 4, &bytes[0]); | ||||
|     // auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); | ||||
|  | ||||
|     if(res != iss::Ok) { | ||||
|         return 0; // TODO THROW ERROR | ||||
|     } else | ||||
|         return static_cast<T>(bytes[0]) | (static_cast<T>(bytes[1]) << 8) | (static_cast<T>(bytes[2]) << 16) | | ||||
|                (static_cast<T>(bytes[3]) << 24); | ||||
| } | ||||
|  | ||||
| template <typename T> std::string sh_read_string(iss::arch_if* arch_if_ptr, T addr, T str_len) { | ||||
|     std::vector<uint8_t> buffer(str_len); | ||||
|     for(int i = 0; i < str_len; i++) { | ||||
|         buffer[i] = sh_read_field(arch_if_ptr, addr + i, 1); | ||||
|     } | ||||
|     std::string str(buffer.begin(), buffer.end()); | ||||
|     return str; | ||||
| } | ||||
|  | ||||
| template <typename T> void semihosting_callback<T>::operator()(iss::arch_if* arch_if_ptr, T* call_number, T* parameter) { | ||||
|     static std::map<T, FILE*> openFiles; | ||||
|     static T file_count = 3; | ||||
|     static T semihostingErrno; | ||||
|  | ||||
|     switch(static_cast<semihosting_syscalls>(*call_number)) { | ||||
|     case semihosting_syscalls::SYS_CLOCK: { | ||||
|         auto end = std::chrono::high_resolution_clock::now(); // end measurement | ||||
|         auto elapsed = end - timeVar; | ||||
|         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); | ||||
|         *call_number = millis; // TODO get time now | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_CLOSE: { | ||||
|         T file_handle = *parameter; | ||||
|         if(openFiles.size() <= file_handle && file_handle < 0) { | ||||
|             semihostingErrno = EBADF; | ||||
|             return; | ||||
|         } | ||||
|         auto file = openFiles[file_handle]; | ||||
|         openFiles.erase(file_handle); | ||||
|         if(!(file == stdin || file == stdout || file == stderr)) { | ||||
|             int i = fclose(file); | ||||
|             *call_number = i; | ||||
|         } else { | ||||
|             *call_number = -1; | ||||
|             semihostingErrno = EINTR; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_ELAPSED: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_ERRNO: { | ||||
|         *call_number = semihostingErrno; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_EXIT: { | ||||
|  | ||||
|         throw std::runtime_error("ISS terminated by Semihost: SYS_EXIT"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_EXIT_EXTENDED: { | ||||
|         throw std::runtime_error("ISS terminated by Semihost: SYS_EXIT_EXTENDED"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_FLEN: { | ||||
|         T file_handle = *parameter; | ||||
|         auto file = openFiles[file_handle]; | ||||
|  | ||||
|         size_t currentPos = ftell(file); | ||||
|         if(currentPos < 0) | ||||
|             throw std::runtime_error("SYS_FLEN negative value"); | ||||
|         fseek(file, 0, SEEK_END); | ||||
|         size_t length = ftell(file); | ||||
|         fseek(file, currentPos, SEEK_SET); | ||||
|         *call_number = (T)length; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_GET_CMDLINE: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_HEAPINFO: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_ISERROR: { | ||||
|         T value = *parameter; | ||||
|         *call_number = (value != 0); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_ISTTY: { | ||||
|         T file_handle = *parameter; | ||||
|         *call_number = (file_handle == 0 || file_handle == 1 || file_handle == 2); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_OPEN: { | ||||
|         T path_str_addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T mode = sh_read_field<T>(arch_if_ptr, 4 + (*parameter)); | ||||
|         T path_len = sh_read_field<T>(arch_if_ptr, 8 + (*parameter)); | ||||
|  | ||||
|         std::string path_str = sh_read_string<T>(arch_if_ptr, path_str_addr, path_len); | ||||
|  | ||||
|         // TODO LOG INFO | ||||
|  | ||||
|         if(mode >= 12) { | ||||
|             // TODO throw ERROR | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         FILE* file = nullptr; | ||||
|         if(path_str == ":tt") { | ||||
|             if(mode < 4) | ||||
|                 file = stdin; | ||||
|             else if(mode < 8) | ||||
|                 file = stdout; | ||||
|             else | ||||
|                 file = stderr; | ||||
|         } else { | ||||
|             file = fopen(path_str.c_str(), SYS_OPEN_MODES_STRS[mode]); | ||||
|             if(file == nullptr) { | ||||
|                 // TODO throw error | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         T file_handle = file_count++; | ||||
|         openFiles[file_handle] = file; | ||||
|         *call_number = file_handle; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_READ: { | ||||
|         T file_handle = sh_read_field<T>(arch_if_ptr, (*parameter) + 4); | ||||
|         T addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T count = sh_read_field<T>(arch_if_ptr, (*parameter) + 8); | ||||
|  | ||||
|         auto file = openFiles[file_handle]; | ||||
|  | ||||
|         std::vector<uint8_t> buffer(count); | ||||
|         size_t num_read = 0; | ||||
|         if(file == stdin) { | ||||
|             // when reading from stdin: mimic behaviour from read syscall | ||||
|             // and return on newline. | ||||
|             while(num_read < count) { | ||||
|                 char c = fgetc(file); | ||||
|                 buffer[num_read] = c; | ||||
|                 num_read++; | ||||
|                 if(c == '\n') | ||||
|                     break; | ||||
|             } | ||||
|         } else { | ||||
|             num_read = fread(buffer.data(), 1, count, file); | ||||
|         } | ||||
|         buffer.resize(num_read); | ||||
|         for(int i = 0; i < num_read; i++) { | ||||
|             auto res = arch_if_ptr->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, addr + i, 1, &buffer[i]); | ||||
|             if(res != iss::Ok) | ||||
|                 return; | ||||
|         } | ||||
|         *call_number = count - num_read; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_READC: { | ||||
|         uint8_t character = getchar(); | ||||
|         // character = getchar(); | ||||
|         /*if(character != iss::Ok) | ||||
|             std::cout << "Not OK"; | ||||
|             return;*/ | ||||
|         *call_number = character; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_REMOVE: { | ||||
|         T path_str_addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T path_len = sh_read_field<T>(arch_if_ptr, (*parameter) + 4); | ||||
|         std::string path_str = sh_read_string<T>(arch_if_ptr, path_str_addr, path_len); | ||||
|  | ||||
|         if(remove(path_str.c_str()) < 0) | ||||
|             *call_number = -1; | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_RENAME: { | ||||
|         T path_str_addr_old = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T path_len_old = sh_read_field<T>(arch_if_ptr, (*parameter) + 4); | ||||
|         T path_str_addr_new = sh_read_field<T>(arch_if_ptr, (*parameter) + 8); | ||||
|         T path_len_new = sh_read_field<T>(arch_if_ptr, (*parameter) + 12); | ||||
|  | ||||
|         std::string path_str_old = sh_read_string<T>(arch_if_ptr, path_str_addr_old, path_len_old); | ||||
|         std::string path_str_new = sh_read_string<T>(arch_if_ptr, path_str_addr_new, path_len_new); | ||||
|         rename(path_str_old.c_str(), path_str_new.c_str()); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_SEEK: { | ||||
|         T file_handle = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T pos = sh_read_field<T>(arch_if_ptr, (*parameter) + 1); | ||||
|         auto file = openFiles[file_handle]; | ||||
|  | ||||
|         int retval = fseek(file, pos, SEEK_SET); | ||||
|         if(retval < 0) | ||||
|             throw std::runtime_error("SYS_SEEK negative return value"); | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_SYSTEM: { | ||||
|         T cmd_addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T cmd_len = sh_read_field<T>(arch_if_ptr, (*parameter) + 1); | ||||
|         std::string cmd = sh_read_string<T>(arch_if_ptr, cmd_addr, cmd_len); | ||||
|         system(cmd.c_str()); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_TICKFREQ: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_TIME: { | ||||
|         // returns time in seconds scince 01.01.1970 00:00 | ||||
|         *call_number = time(NULL); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_TMPNAM: { | ||||
|         T buffer_addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T identifier = sh_read_field<T>(arch_if_ptr, (*parameter) + 1); | ||||
|         T buffer_len = sh_read_field<T>(arch_if_ptr, (*parameter) + 2); | ||||
|  | ||||
|         if(identifier > 255) { | ||||
|             *call_number = -1; | ||||
|             return; | ||||
|         } | ||||
|         std::stringstream ss; | ||||
|         ss << "tmp/file-" << std::setfill('0') << std::setw(3) << identifier; | ||||
|         std::string filename = ss.str(); | ||||
|  | ||||
|         for(int i = 0; i < buffer_len; i++) { | ||||
|             uint8_t character = filename[i]; | ||||
|             auto res = arch_if_ptr->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, (*parameter) + i, 1, &character); | ||||
|             if(res != iss::Ok) | ||||
|                 return; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_WRITE: { | ||||
|         T file_handle = sh_read_field<T>(arch_if_ptr, (*parameter) + 4); | ||||
|         T addr = sh_read_field<T>(arch_if_ptr, *parameter); | ||||
|         T count = sh_read_field<T>(arch_if_ptr, (*parameter) + 8); | ||||
|  | ||||
|         auto file = openFiles[file_handle]; | ||||
|         std::string str = sh_read_string<T>(arch_if_ptr, addr, count); | ||||
|         fwrite(&str[0], 1, count, file); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_WRITEC: { | ||||
|         uint8_t character; | ||||
|         auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); | ||||
|         if(res != iss::Ok) | ||||
|             return; | ||||
|         putchar(character); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::SYS_WRITE0: { | ||||
|         uint8_t character; | ||||
|         while(1) { | ||||
|             auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); | ||||
|             if(res != iss::Ok) | ||||
|                 return; | ||||
|             if(character == 0) | ||||
|                 break; | ||||
|             putchar(character); | ||||
|             (*parameter)++; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::USER_CMD_0x100: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     case semihosting_syscalls::USER_CMD_0x1FF: { | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         throw std::runtime_error("Semihosting Call not Implemented"); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| template class semihosting_callback<uint32_t>; | ||||
| template class semihosting_callback<uint64_t>; | ||||
							
								
								
									
										61
									
								
								src/iss/semihosting/semihosting.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/iss/semihosting/semihosting.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #ifndef _SEMIHOSTING_H_ | ||||
| #define _SEMIHOSTING_H_ | ||||
| #include <chrono> | ||||
| #include <functional> | ||||
| #include <iss/arch_if.h> | ||||
| /* | ||||
|  * According to: | ||||
|  * "Semihosting for AArch32 and AArch64, Release 2.0" | ||||
|  * https://static.docs.arm.com/100863/0200/semihosting.pdf | ||||
|  * from ARM Ltd. | ||||
|  * | ||||
|  * The available semihosting operation numbers passed in A0 are allocated | ||||
|  * as follows: | ||||
|  * - 0x00-0x31 Used by ARM. | ||||
|  * - 0x32-0xFF Reserved for future use by ARM. | ||||
|  * - 0x100-0x1FF Reserved for user applications. These are not used by ARM. | ||||
|  *   However, if you are writing your own SVC operations, you are advised | ||||
|  *   to use a different SVC number rather than using the semihosted | ||||
|  *   SVC number and these operation type numbers. | ||||
|  * - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended | ||||
|  *   that you do not use these. | ||||
|  */ | ||||
| enum class semihosting_syscalls { | ||||
|  | ||||
|     SYS_OPEN = 0x01, | ||||
|     SYS_CLOSE = 0x02, | ||||
|     SYS_WRITEC = 0x03, | ||||
|     SYS_WRITE0 = 0x04, | ||||
|     SYS_WRITE = 0x05, | ||||
|     SYS_READ = 0x06, | ||||
|     SYS_READC = 0x07, | ||||
|     SYS_ISERROR = 0x08, | ||||
|     SYS_ISTTY = 0x09, | ||||
|     SYS_SEEK = 0x0A, | ||||
|     SYS_FLEN = 0x0C, | ||||
|     SYS_TMPNAM = 0x0D, | ||||
|     SYS_REMOVE = 0x0E, | ||||
|     SYS_RENAME = 0x0F, | ||||
|     SYS_CLOCK = 0x10, | ||||
|     SYS_TIME = 0x11, | ||||
|     SYS_SYSTEM = 0x12, | ||||
|     SYS_ERRNO = 0x13, | ||||
|     SYS_GET_CMDLINE = 0x15, | ||||
|     SYS_HEAPINFO = 0x16, | ||||
|     SYS_EXIT = 0x18, | ||||
|     SYS_EXIT_EXTENDED = 0x20, | ||||
|     SYS_ELAPSED = 0x30, | ||||
|     SYS_TICKFREQ = 0x31, | ||||
|     USER_CMD_0x100 = 0x100, | ||||
|     USER_CMD_0x1FF = 0x1FF, | ||||
| }; | ||||
|  | ||||
| template <typename T> struct semihosting_callback { | ||||
|     std::chrono::high_resolution_clock::time_point timeVar; | ||||
|     semihosting_callback() | ||||
|     : timeVar(std::chrono::high_resolution_clock::now()) {} | ||||
|     void operator()(iss::arch_if* arch_if_ptr, T* call_number, T* parameter); | ||||
| }; | ||||
|  | ||||
| template <typename T> using semihosting_cb_t = std::function<void(iss::arch_if*, T*, T*)>; | ||||
| #endif | ||||
							
								
								
									
										59
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -31,8 +31,12 @@ | ||||
|  *******************************************************************************/ | ||||
|  | ||||
| #include <array> | ||||
| #include <cstdint> | ||||
| #include <iostream> | ||||
| #include <iss/factory.h> | ||||
| #include <iss/semihosting/semihosting.h> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
|  | ||||
| #include "iss/arch/tgc_mapper.h" | ||||
| @@ -52,7 +56,6 @@ | ||||
| #endif | ||||
|  | ||||
| namespace po = boost::program_options; | ||||
|  | ||||
| int main(int argc, char* argv[]) { | ||||
|     /* | ||||
|      *  Define and parse the program options | ||||
| @@ -72,7 +75,7 @@ int main(int argc, char* argv[]) { | ||||
|         ("elf,f", po::value<std::vector<std::string>>(), "ELF file(s) to load") | ||||
|         ("mem,m", po::value<std::string>(), "the memory input file") | ||||
|         ("plugin,p", po::value<std::vector<std::string>>(), "plugin to activate") | ||||
|         ("backend", po::value<std::string>()->default_value("interp"), "the ISS backend to use, options are: interp, tcc") | ||||
|         ("backend", po::value<std::string>()->default_value("interp"), "the ISS backend to use, options are: interp, llvm, tcc, asmjit") | ||||
|         ("isa", po::value<std::string>()->default_value("tgc5c"), "core or isa name to use for simulation, use '?' to get list"); | ||||
|     // clang-format on | ||||
|     auto parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); | ||||
| @@ -116,6 +119,8 @@ int main(int argc, char* argv[]) { | ||||
|         // instantiate the simulator | ||||
|         iss::vm_ptr vm{nullptr}; | ||||
|         iss::cpu_ptr cpu{nullptr}; | ||||
|         semihosting_callback<uint32_t> cb{}; | ||||
|         semihosting_cb_t<uint32_t> semihosting_cb = [&cb](iss::arch_if* i, uint32_t* a0, uint32_t* a1) { cb(i, a0, a1); }; | ||||
|         std::string isa_opt(clim["isa"].as<std::string>()); | ||||
|         if(isa_opt.size() == 0 || isa_opt == "?") { | ||||
|             auto list = f.get_names(); | ||||
| @@ -123,7 +128,8 @@ int main(int argc, char* argv[]) { | ||||
|             std::cout << "Available implementations (core|platform|backend):\n  - " << util::join(list, "\n  - ") << std::endl; | ||||
|             return 0; | ||||
|         } else if(isa_opt.find('|') != std::string::npos) { | ||||
|             std::tie(cpu, vm) = f.create(isa_opt + "|" + clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>()); | ||||
|             std::tie(cpu, vm) = | ||||
|                 f.create(isa_opt + "|" + clim["backend"].as<std::string>(), clim["gdb-port"].as<unsigned>(), &semihosting_cb); | ||||
|         } else { | ||||
|             auto base_isa = isa_opt.substr(0, 5); | ||||
|             if(base_isa == "tgc5d" || base_isa == "tgc5e") { | ||||
| @@ -131,14 +137,14 @@ int main(int argc, char* argv[]) { | ||||
|             } else { | ||||
|                 isa_opt += "|m_p|" + clim["backend"].as<std::string>(); | ||||
|             } | ||||
|             std::tie(cpu, vm) = f.create(isa_opt, clim["gdb-port"].as<unsigned>()); | ||||
|             std::tie(cpu, vm) = f.create(isa_opt, clim["gdb-port"].as<unsigned>(), &semihosting_cb); | ||||
|         } | ||||
|         if(!cpu) { | ||||
|             LOG(ERR) << "Could not create cpu for isa " << isa_opt << " and backend " << clim["backend"].as<std::string>() << std::endl; | ||||
|             CPPLOG(ERR) << "Could not create cpu for isa " << isa_opt << " and backend " << clim["backend"].as<std::string>() << std::endl; | ||||
|             return 127; | ||||
|         } | ||||
|         if(!vm) { | ||||
|             LOG(ERR) << "Could not create vm for isa " << isa_opt << " and backend " << clim["backend"].as<std::string>() << std::endl; | ||||
|             CPPLOG(ERR) << "Could not create vm for isa " << isa_opt << " and backend " << clim["backend"].as<std::string>() << std::endl; | ||||
|             return 127; | ||||
|         } | ||||
|         if(clim.count("plugin")) { | ||||
| @@ -174,7 +180,7 @@ int main(int argc, char* argv[]) { | ||||
|                     } else | ||||
| #endif | ||||
|                     { | ||||
|                         LOG(ERR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl; | ||||
|                         CPPLOG(ERR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl; | ||||
|                         return 127; | ||||
|                     } | ||||
|                 } | ||||
| @@ -196,12 +202,12 @@ int main(int argc, char* argv[]) { | ||||
|         if(clim.count("elf")) | ||||
|             for(std::string input : clim["elf"].as<std::vector<std::string>>()) { | ||||
|                 auto start_addr = vm->get_arch()->load_file(input); | ||||
|                 if(start_addr.second) | ||||
|                 if(start_addr.second) // FIXME: this always evaluates to true as load file always returns <sth, true> | ||||
|                     start_address = start_addr.first; | ||||
|             } | ||||
|         for(std::string input : args) { | ||||
|             auto start_addr = vm->get_arch()->load_file(input); // treat remaining arguments as elf files | ||||
|             if(start_addr.second) | ||||
|             if(start_addr.second) // FIXME: this always evaluates to true as load file always returns <sth, true> | ||||
|                 start_address = start_addr.first; | ||||
|         } | ||||
|         if(clim.count("reset")) { | ||||
| @@ -211,11 +217,42 @@ int main(int argc, char* argv[]) { | ||||
|         vm->reset(start_address); | ||||
|         auto cycles = clim["instructions"].as<uint64_t>(); | ||||
|         res = vm->start(cycles, dump); | ||||
|  | ||||
|         auto instr_if = vm->get_arch()->get_instrumentation_if(); | ||||
|         // this assumes a single input file | ||||
|         std::unordered_map<std::string, uint64_t> sym_table; | ||||
|         if(args.empty()) | ||||
|             sym_table = instr_if->get_symbol_table(clim["elf"].as<std::vector<std::string>>()[0]); | ||||
|         else | ||||
|             sym_table = instr_if->get_symbol_table(args[0]); | ||||
|         if(sym_table.find("begin_signature") != std::end(sym_table) && sym_table.find("end_signature") != std::end(sym_table)) { | ||||
|             auto start_addr = sym_table["begin_signature"]; | ||||
|             auto end_addr = sym_table["end_signature"]; | ||||
|             std::array<uint8_t, 4> data; | ||||
|             std::ofstream file; | ||||
|             std::string filename = fmt::format("{}.signature", isa_opt); | ||||
|             std::replace(std::begin(filename), std::end(filename), '|', '_'); | ||||
|             // default riscof requires this filename | ||||
|             filename = "DUT-tgc.signature"; | ||||
|             file.open(filename, std::ios::out); | ||||
|             if(!file.is_open()) { | ||||
|                 LOG(ERR) << "Error opening file " << filename << std::endl; | ||||
|                 return 1; | ||||
|             } | ||||
|             for(auto addr = start_addr; addr < end_addr; addr += data.size()) { | ||||
|                 vm->get_arch()->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0 /*MEM*/, addr, data.size(), | ||||
|                                      data.data()); // FIXME: get space from iss::arch::traits<ARCH>::mem_type_e::MEM | ||||
|  | ||||
|                 // TODO : obey Target endianess | ||||
|                 uint32_t to_print = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; | ||||
|                 file << std::hex << fmt::format("{:08x}", to_print) << std::dec << std::endl; | ||||
|             } | ||||
|         } | ||||
|     } catch(std::exception& e) { | ||||
|         LOG(ERR) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit" << std::endl; | ||||
|         CPPLOG(ERR) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit" << std::endl; | ||||
|         res = 2; | ||||
|     } | ||||
|     // cleanup to let plugins report of needed | ||||
|     // cleanup to let plugins report if needed | ||||
|     for(auto* p : plugin_list) { | ||||
|         delete p; | ||||
|     } | ||||
|   | ||||
| @@ -1,537 +0,0 @@ | ||||
|  | ||||
|  | ||||
| x86::Mem get_reg_ptr(jit_holder& jh, unsigned idx) { | ||||
|  | ||||
|     x86::Gp tmp_ptr = jh.cc.newUIntPtr("tmp_ptr"); | ||||
|     jh.cc.mov(tmp_ptr, jh.regs_base_ptr); | ||||
|     jh.cc.add(tmp_ptr, traits::reg_byte_offsets[idx]); | ||||
|     switch(traits::reg_bit_widths[idx]) { | ||||
|     case 8: | ||||
|         return x86::ptr_8(tmp_ptr); | ||||
|     case 16: | ||||
|         return x86::ptr_16(tmp_ptr); | ||||
|     case 32: | ||||
|         return x86::ptr_32(tmp_ptr); | ||||
|     case 64: | ||||
|         return x86::ptr_64(tmp_ptr); | ||||
|     default: | ||||
|         throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||
|     } | ||||
| } | ||||
| x86::Gp get_reg_for(jit_holder& jh, unsigned idx) { | ||||
|     // TODO can check for regs in jh and return them instead of creating new ones | ||||
|     switch(traits::reg_bit_widths[idx]) { | ||||
|     case 8: | ||||
|         return jh.cc.newInt8(); | ||||
|     case 16: | ||||
|         return jh.cc.newInt16(); | ||||
|     case 32: | ||||
|         return jh.cc.newInt32(); | ||||
|     case 64: | ||||
|         return jh.cc.newInt64(); | ||||
|     default: | ||||
|         throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||
|     } | ||||
| } | ||||
| x86::Gp get_reg_for(jit_holder& jh, unsigned size, bool is_signed) { | ||||
|     if(is_signed) | ||||
|         switch(size) { | ||||
|         case 8: | ||||
|             return jh.cc.newInt8(); | ||||
|         case 16: | ||||
|             return jh.cc.newInt16(); | ||||
|         case 32: | ||||
|             return jh.cc.newInt32(); | ||||
|         case 64: | ||||
|             return jh.cc.newInt64(); | ||||
|         default: | ||||
|             throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||
|         } | ||||
|     else | ||||
|         switch(size) { | ||||
|         case 8: | ||||
|             return jh.cc.newUInt8(); | ||||
|         case 16: | ||||
|             return jh.cc.newUInt16(); | ||||
|         case 32: | ||||
|             return jh.cc.newUInt32(); | ||||
|         case 64: | ||||
|             return jh.cc.newUInt64(); | ||||
|         default: | ||||
|             throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||
|         } | ||||
| } | ||||
| inline x86::Gp load_reg_from_mem(jit_holder& jh, unsigned idx) { | ||||
|     auto ptr = get_reg_ptr(jh, idx); | ||||
|     auto reg = get_reg_for(jh, idx); | ||||
|     jh.cc.mov(reg, ptr); | ||||
|     return reg; | ||||
| } | ||||
| inline void write_reg_to_mem(jit_holder& jh, x86::Gp reg, unsigned idx) { | ||||
|     auto ptr = get_reg_ptr(jh, idx); | ||||
|     jh.cc.mov(ptr, reg); | ||||
| } | ||||
|  | ||||
| void gen_instr_prologue(jit_holder& jh, addr_t pc) { | ||||
|     auto& cc = jh.cc; | ||||
|  | ||||
|     cc.comment("\n//(*icount)++;"); | ||||
|     cc.inc(get_reg_ptr(jh, traits::ICOUNT)); | ||||
|  | ||||
|     cc.comment("\n//*pc=*next_pc;"); | ||||
|     cc.mov(get_reg_ptr(jh, traits::PC), jh.next_pc); | ||||
|  | ||||
|     cc.comment("\n//*trap_state=*pending_trap;"); | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_reg_ptr(jh, traits::TRAP_STATE)); | ||||
|     cc.mov(get_reg_ptr(jh, traits::PENDING_TRAP), current_trap_state); | ||||
|  | ||||
|     cc.comment("\n//increment *next_pc"); | ||||
|     cc.mov(jh.next_pc, pc); | ||||
| } | ||||
| void gen_instr_epilogue(jit_holder& jh) { | ||||
|     auto& cc = jh.cc; | ||||
|  | ||||
|     cc.comment("\n//if(*trap_state!=0) goto trap_entry;"); | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_reg_ptr(jh, traits::TRAP_STATE)); | ||||
|     cc.cmp(current_trap_state, 0); | ||||
|     cc.jne(jh.trap_entry); | ||||
|  | ||||
|     // TODO: Does not need to be done for every instruction, only when needed | ||||
|     cc.comment("\n//write back regs to mem"); | ||||
|     write_reg_to_mem(jh, jh.pc, traits::PC); | ||||
|     write_reg_to_mem(jh, jh.next_pc, traits::NEXT_PC); | ||||
| } | ||||
| void gen_block_prologue(jit_holder& jh) override { | ||||
|  | ||||
|     jh.pc = load_reg_from_mem(jh, traits::PC); | ||||
|     jh.next_pc = load_reg_from_mem(jh, traits::NEXT_PC); | ||||
| } | ||||
| void gen_block_epilogue(jit_holder& jh) override { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     cc.comment("\n//return *next_pc;"); | ||||
|     cc.ret(jh.next_pc); | ||||
|  | ||||
|     cc.bind(jh.trap_entry); | ||||
|     cc.comment("\n//Prepare for enter_trap;"); | ||||
|     // Make sure cached values are written back | ||||
|     cc.comment("\n//write back regs to mem"); | ||||
|     write_reg_to_mem(jh, jh.pc, traits::PC); | ||||
|     write_reg_to_mem(jh, jh.next_pc, traits::NEXT_PC); | ||||
|     this->gen_sync(jh, POST_SYNC, -1); | ||||
|  | ||||
|     x86::Gp current_trap_state = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(current_trap_state, get_reg_ptr(jh, traits::TRAP_STATE)); | ||||
|  | ||||
|     x86::Gp current_pc = get_reg_for(jh, traits::PC); | ||||
|     cc.mov(current_pc, get_reg_ptr(jh, traits::PC)); | ||||
|  | ||||
|     x86::Gp instr = cc.newInt32("instr"); | ||||
|     cc.mov(instr, 0); // this is not correct | ||||
|     cc.comment("\n//enter trap call;"); | ||||
|     InvokeNode* call_enter_trap; | ||||
|     cc.invoke(&call_enter_trap, &enter_trap, FuncSignatureT<uint64_t, void*, uint64_t, uint64_t, uint64_t>()); | ||||
|     call_enter_trap->setArg(0, jh.arch_if_ptr); | ||||
|     call_enter_trap->setArg(1, current_trap_state); | ||||
|     call_enter_trap->setArg(2, current_pc); | ||||
|     call_enter_trap->setArg(3, instr); | ||||
|  | ||||
|     x86::Gp current_next_pc = get_reg_for(jh, traits::NEXT_PC); | ||||
|     cc.mov(current_next_pc, get_reg_ptr(jh, traits::NEXT_PC)); | ||||
|     cc.mov(jh.next_pc, current_next_pc); | ||||
|  | ||||
|     cc.comment("\n//*last_branch = std::numeric_limits<uint32_t>::max();"); | ||||
|     cc.mov(get_reg_ptr(jh, traits::LAST_BRANCH), std::numeric_limits<uint32_t>::max()); | ||||
|     cc.comment("\n//return *next_pc;"); | ||||
|     cc.ret(jh.next_pc); | ||||
| } | ||||
| /* | ||||
|     inline void raise(uint16_t trap_id, uint16_t cause){ | ||||
|         auto trap_val =  0x80ULL << 24 | (cause << 16) | trap_id; | ||||
|         this->core.reg.trap_state = trap_val; | ||||
|         this->template get_reg<uint32_t>(traits::NEXT_PC) = std::numeric_limits<uint32_t>::max(); | ||||
|     } | ||||
| */ | ||||
| inline void gen_raise(jit_holder& jh, uint16_t trap_id, uint16_t cause) { | ||||
|     auto& cc = jh.cc; | ||||
|     cc.comment("//gen_raise"); | ||||
|     auto tmp1 = get_reg_for(jh, traits::TRAP_STATE); | ||||
|     cc.mov(tmp1, 0x80ULL << 24 | (cause << 16) | trap_id); | ||||
|     cc.mov(get_reg_ptr(jh, traits::TRAP_STATE), tmp1); | ||||
|     auto tmp2 = get_reg_for(jh, traits::NEXT_PC); | ||||
|     cc.mov(tmp2, std::numeric_limits<uint32_t>::max()); | ||||
|     cc.mov(get_reg_ptr(jh, traits::NEXT_PC), tmp2); | ||||
| } | ||||
| inline void gen_wait(jit_holder& jh, unsigned type) { jh.cc.comment("//gen_wait"); } | ||||
| inline void gen_leave(jit_holder& jh, unsigned lvl) { jh.cc.comment("//gen_leave"); } | ||||
|  | ||||
| enum operation { add, sub, band, bor, bxor, shl, sar, shr }; | ||||
|  | ||||
| template <typename T, typename = std::enable_if_t<std::is_integral<T>::value || std::is_same<T, x86::Gp>::value>> | ||||
| x86::Gp gen_operation(jit_holder& jh, operation op, x86::Gp a, T b) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     switch(op) { | ||||
|     case add: { | ||||
|         cc.add(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case sub: { | ||||
|         cc.sub(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case band: { | ||||
|         cc.and_(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case bor: { | ||||
|         cc.or_(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case bxor: { | ||||
|         cc.xor_(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case shl: { | ||||
|         cc.shl(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case sar: { | ||||
|         cc.sar(a, b); | ||||
|         break; | ||||
|     } | ||||
|     case shr: { | ||||
|         cc.shr(a, b); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (operation)", op)); | ||||
|     } | ||||
|     return a; | ||||
| } | ||||
|  | ||||
| enum three_operand_operation { imul, mul, idiv, div, srem, urem }; | ||||
|  | ||||
| x86::Gp gen_operation(jit_holder& jh, three_operand_operation op, x86::Gp a, x86::Gp b) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     switch(op) { | ||||
|     case imul: { | ||||
|         x86::Gp dummy = cc.newInt64(); | ||||
|         cc.imul(dummy, a.r64(), b.r64()); | ||||
|         return a; | ||||
|     } | ||||
|     case mul: { | ||||
|         x86::Gp dummy = cc.newInt64(); | ||||
|         cc.mul(dummy, a.r64(), b.r64()); | ||||
|         return a; | ||||
|     } | ||||
|     case idiv: { | ||||
|         x86::Gp dummy = cc.newInt64(); | ||||
|         cc.mov(dummy, 0); | ||||
|         cc.idiv(dummy, a.r64(), b.r64()); | ||||
|         return a; | ||||
|     } | ||||
|     case div: { | ||||
|         x86::Gp dummy = cc.newInt64(); | ||||
|         cc.mov(dummy, 0); | ||||
|         cc.div(dummy, a.r64(), b.r64()); | ||||
|         return a; | ||||
|     } | ||||
|     case srem: { | ||||
|         x86::Gp rem = cc.newInt32(); | ||||
|         cc.mov(rem, 0); | ||||
|         auto a_reg = cc.newInt32(); | ||||
|         cc.mov(a_reg, a.r32()); | ||||
|         cc.idiv(rem, a_reg, b.r32()); | ||||
|         return rem; | ||||
|     } | ||||
|     case urem: { | ||||
|         x86::Gp rem = cc.newInt32(); | ||||
|         cc.mov(rem, 0); | ||||
|         auto a_reg = cc.newInt32(); | ||||
|         cc.mov(a_reg, a.r32()); | ||||
|         cc.div(rem, a_reg, b.r32()); | ||||
|         return rem; | ||||
|     } | ||||
|  | ||||
|     default: | ||||
|         throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (three_operand)", op)); | ||||
|     } | ||||
|     return a; | ||||
| } | ||||
| template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> | ||||
| x86::Gp gen_operation(jit_holder& jh, three_operand_operation op, x86::Gp a, T b) { | ||||
|     x86::Gp b_reg = jh.cc.newInt32(); | ||||
|     /*     switch(a.size()){ | ||||
|             case 1: b_reg = jh.cc.newInt8(); break; | ||||
|             case 2: b_reg = jh.cc.newInt16(); break; | ||||
|             case 4: b_reg = jh.cc.newInt32(); break; | ||||
|             case 8: b_reg = jh.cc.newInt64(); break; | ||||
|             default: throw std::runtime_error(fmt::format("Invalid size ({}) in gen operation", a.size())); | ||||
|         } */ | ||||
|     jh.cc.mov(b_reg, b); | ||||
|     return gen_operation(jh, op, a, b_reg); | ||||
| } | ||||
| enum comparison_operation { land, lor, eq, ne, lt, ltu, gt, gtu, lte, lteu, gte, gteu }; | ||||
|  | ||||
| template <typename T, typename = std::enable_if_t<std::is_integral<T>::value || std::is_same<T, x86::Gp>::value>> | ||||
| x86::Gp gen_operation(jit_holder& jh, comparison_operation op, x86::Gp a, T b) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     x86::Gp tmp = cc.newInt8(); | ||||
|     cc.mov(tmp, 1); | ||||
|     Label label_then = cc.newLabel(); | ||||
|     cc.cmp(a, b); | ||||
|     switch(op) { | ||||
|     case eq: | ||||
|         cc.je(label_then); | ||||
|         break; | ||||
|     case ne: | ||||
|         cc.jne(label_then); | ||||
|         break; | ||||
|     case lt: | ||||
|         cc.jl(label_then); | ||||
|         break; | ||||
|     case ltu: | ||||
|         cc.jb(label_then); | ||||
|         break; | ||||
|     case gt: | ||||
|         cc.jg(label_then); | ||||
|         break; | ||||
|     case gtu: | ||||
|         cc.ja(label_then); | ||||
|         break; | ||||
|     case lte: | ||||
|         cc.jle(label_then); | ||||
|         break; | ||||
|     case lteu: | ||||
|         cc.jbe(label_then); | ||||
|         break; | ||||
|     case gte: | ||||
|         cc.jge(label_then); | ||||
|         break; | ||||
|     case gteu: | ||||
|         cc.jae(label_then); | ||||
|         break; | ||||
|     case land: { | ||||
|         Label label_false = cc.newLabel(); | ||||
|         cc.cmp(a, 0); | ||||
|         cc.je(label_false); | ||||
|         auto b_reg = cc.newInt8(); | ||||
|         cc.mov(b_reg, b); | ||||
|         cc.cmp(b_reg, 0); | ||||
|         cc.je(label_false); | ||||
|         cc.jmp(label_then); | ||||
|         cc.bind(label_false); | ||||
|         break; | ||||
|     } | ||||
|     case lor: { | ||||
|         cc.cmp(a, 0); | ||||
|         cc.jne(label_then); | ||||
|         auto b_reg = cc.newInt8(); | ||||
|         cc.mov(b_reg, b); | ||||
|         cc.cmp(b_reg, 0); | ||||
|         cc.jne(label_then); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (comparison)", op)); | ||||
|     } | ||||
|     cc.mov(tmp, 0); | ||||
|     cc.bind(label_then); | ||||
|     return tmp; | ||||
| } | ||||
| enum binary_operation { lnot, inc, dec, bnot, neg }; | ||||
|  | ||||
| x86::Gp gen_operation(jit_holder& jh, binary_operation op, x86::Gp a) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     switch(op) { | ||||
|     case lnot: | ||||
|         throw std::runtime_error("Current operation not supported in gen_operation(lnot)"); | ||||
|     case inc: { | ||||
|         cc.inc(a); | ||||
|         break; | ||||
|     } | ||||
|     case dec: { | ||||
|         cc.dec(a); | ||||
|         break; | ||||
|     } | ||||
|     case bnot: { | ||||
|         cc.not_(a); | ||||
|         break; | ||||
|     } | ||||
|     case neg: { | ||||
|         cc.neg(a); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (unary)", op)); | ||||
|     } | ||||
|     return a; | ||||
| } | ||||
|  | ||||
| template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> | ||||
| inline x86::Gp gen_ext(jit_holder& jh, T val, unsigned size, bool is_signed) { | ||||
|     auto val_reg = get_reg_for(jh, sizeof(val) * 8, is_signed); | ||||
|     jh.cc.mov(val_reg, val); | ||||
|     return gen_ext(jh, val_reg, size, is_signed); | ||||
| } | ||||
| inline x86::Gp gen_ext(jit_holder& jh, x86::Gp val, unsigned size, bool is_signed) { | ||||
|     auto& cc = jh.cc; | ||||
|     if(is_signed) { | ||||
|         switch(val.size()) { | ||||
|         case 1: | ||||
|             cc.cbw(val); | ||||
|             break; | ||||
|         case 2: | ||||
|             cc.cwde(val); | ||||
|             break; | ||||
|         case 4: | ||||
|             cc.cdqe(val); | ||||
|             break; | ||||
|         case 8: | ||||
|             break; | ||||
|         default: | ||||
|             throw std::runtime_error("Invalid register size in gen_ext"); | ||||
|         } | ||||
|     } | ||||
|     switch(size) { | ||||
|     case 8: | ||||
|         cc.and_(val, std::numeric_limits<uint8_t>::max()); | ||||
|         return val.r8(); | ||||
|     case 16: | ||||
|         cc.and_(val, std::numeric_limits<uint16_t>::max()); | ||||
|         return val.r16(); | ||||
|     case 32: | ||||
|         cc.and_(val, std::numeric_limits<uint32_t>::max()); | ||||
|         return val.r32(); | ||||
|     case 64: | ||||
|         cc.and_(val, std::numeric_limits<uint64_t>::max()); | ||||
|         return val.r64(); | ||||
|     case 128: | ||||
|         return val.r64(); | ||||
|     default: | ||||
|         throw std::runtime_error("Invalid size in gen_ext"); | ||||
|     } | ||||
| } | ||||
| inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, uint32_t length) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     auto ret_reg = cc.newInt32(); | ||||
|  | ||||
|     auto mem_type_reg = cc.newInt32(); | ||||
|     cc.mov(mem_type_reg, type); | ||||
|  | ||||
|     auto space_reg = cc.newInt32(); | ||||
|     cc.mov(space_reg, static_cast<uint16_t>(iss::address_type::VIRTUAL)); | ||||
|  | ||||
|     auto val_ptr = cc.newUIntPtr(); | ||||
|     cc.mov(val_ptr, read_mem_buf); | ||||
|  | ||||
|     InvokeNode* invokeNode; | ||||
|     uint64_t mask = 0; | ||||
|     x86::Gp val_reg = cc.newInt64(); | ||||
|  | ||||
|     switch(length) { | ||||
|     case 1: { | ||||
|         cc.invoke(&invokeNode, &read_mem1, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||
|         mask = std::numeric_limits<uint8_t>::max(); | ||||
|         break; | ||||
|     } | ||||
|     case 2: { | ||||
|         cc.invoke(&invokeNode, &read_mem2, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||
|         mask = std::numeric_limits<uint16_t>::max(); | ||||
|         break; | ||||
|     } | ||||
|     case 4: { | ||||
|         cc.invoke(&invokeNode, &read_mem4, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||
|         mask = std::numeric_limits<uint32_t>::max(); | ||||
|         break; | ||||
|     } | ||||
|     case 8: { | ||||
|         cc.invoke(&invokeNode, &read_mem8, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||
|         mask = std::numeric_limits<uint64_t>::max(); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         throw std::runtime_error(fmt::format("Invalid length ({}) in gen_read_mem", length)); | ||||
|     } | ||||
|  | ||||
|     invokeNode->setRet(0, ret_reg); | ||||
|     invokeNode->setArg(0, jh.arch_if_ptr); | ||||
|     invokeNode->setArg(1, space_reg); | ||||
|     invokeNode->setArg(2, mem_type_reg); | ||||
|     invokeNode->setArg(3, addr); | ||||
|     invokeNode->setArg(4, val_ptr); | ||||
|     cc.cmp(ret_reg, 0); | ||||
|     cc.jne(jh.trap_entry); | ||||
|  | ||||
|     cc.mov(val_reg, x86::ptr_64(val_ptr)); | ||||
|     cc.and_(val_reg, mask); | ||||
|     return val_reg; | ||||
| } | ||||
| inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, x86::Gp length) { | ||||
|     throw std::runtime_error("Invalid gen_read_mem"); | ||||
| } | ||||
| inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, uint64_t addr, x86::Gp length) { | ||||
|     throw std::runtime_error("Invalid gen_read_mem"); | ||||
| } | ||||
| inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, uint64_t addr, uint32_t length) { | ||||
|     auto addr_reg = jh.cc.newInt64(); | ||||
|     jh.cc.mov(addr_reg, addr); | ||||
|  | ||||
|     return gen_read_mem(jh, type, addr_reg, length); | ||||
| } | ||||
| inline void gen_write_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, int64_t val, uint32_t length) { | ||||
|     auto val_reg = get_reg_for(jh, length * 8, true); | ||||
|     jh.cc.mov(val_reg, val); | ||||
|     gen_write_mem(jh, type, addr, val_reg, length); | ||||
| } | ||||
| inline void gen_write_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, x86::Gp val, uint32_t length) { | ||||
|     x86::Compiler& cc = jh.cc; | ||||
|     assert(val.size() == length); | ||||
|     auto mem_type_reg = cc.newInt32(); | ||||
|     jh.cc.mov(mem_type_reg, type); | ||||
|     auto space_reg = cc.newInt32(); | ||||
|     jh.cc.mov(space_reg, static_cast<uint16_t>(iss::address_type::VIRTUAL)); | ||||
|     auto ret_reg = cc.newInt32(); | ||||
|     InvokeNode* invokeNode; | ||||
|     switch(length) { | ||||
|     case 1: | ||||
|         cc.invoke(&invokeNode, &write_mem1, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint8_t>()); | ||||
|  | ||||
|         break; | ||||
|     case 2: | ||||
|         cc.invoke(&invokeNode, &write_mem2, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint16_t>()); | ||||
|         break; | ||||
|     case 4: | ||||
|         cc.invoke(&invokeNode, &write_mem4, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint32_t>()); | ||||
|         break; | ||||
|     case 8: | ||||
|         cc.invoke(&invokeNode, &write_mem8, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint64_t>()); | ||||
|  | ||||
|         break; | ||||
|     default: | ||||
|         throw std::runtime_error("Invalid register size in gen_ext"); | ||||
|     } | ||||
|     invokeNode->setRet(0, ret_reg); | ||||
|     invokeNode->setArg(0, jh.arch_if_ptr); | ||||
|     invokeNode->setArg(1, space_reg); | ||||
|     invokeNode->setArg(2, mem_type_reg); | ||||
|     invokeNode->setArg(3, addr); | ||||
|     invokeNode->setArg(4, val); | ||||
|  | ||||
|     cc.cmp(ret_reg, 0); | ||||
|     cc.jne(jh.trap_entry); | ||||
| } | ||||
| inline void gen_write_mem(jit_holder& jh, mem_type_e type, uint64_t addr, x86::Gp val, uint32_t length) { | ||||
|     auto addr_reg = jh.cc.newUInt64(); | ||||
|     jh.cc.mov(addr_reg, addr); | ||||
|     gen_write_mem(jh, type, addr_reg, val, length); | ||||
| } | ||||
| inline void gen_write_mem(jit_holder& jh, mem_type_e type, uint64_t addr, int64_t val, uint32_t length) { | ||||
|     auto val_reg = get_reg_for(jh, length * 8, true); | ||||
|     jh.cc.mov(val_reg, val); | ||||
|  | ||||
|     auto addr_reg = jh.cc.newUInt64(); | ||||
|     jh.cc.mov(addr_reg, addr); | ||||
|     gen_write_mem(jh, type, addr_reg, val_reg, length); | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,5 +1,5 @@ | ||||
| /******************************************************************************* | ||||
|  * Copyright (C) 2021 MINRES Technologies GmbH | ||||
|  * Copyright (C) 20217-2024 MINRES Technologies GmbH | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
| @@ -113,6 +113,8 @@ protected: | ||||
|         this->core.wait_until(type); | ||||
|     } | ||||
|  | ||||
|     uint64_t fetch_count{0}; | ||||
|  | ||||
|     using yield_t = boost::coroutines2::coroutine<void>::push_type; | ||||
|     using coro_t = boost::coroutines2::coroutine<void>::pull_type; | ||||
|     std::vector<coro_t> spawn_blocks; | ||||
| @@ -299,6 +301,7 @@ private: | ||||
|             });  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     typename arch::traits<ARCH>::opcode_e  decode_instr(decoding_tree_node* node, code_word_t word){ | ||||
|         if(!node->children.size()){ | ||||
|             if(node->instrs.size() == 1) return node->instrs[0].op; | ||||
| @@ -348,8 +351,12 @@ vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||
|     populate_decoding_tree(root); | ||||
| } | ||||
|  | ||||
| inline bool is_count_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT; | ||||
| inline bool is_icount_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::ICOUNT_LIMIT) == finish_cond_e::ICOUNT_LIMIT; | ||||
| } | ||||
|  | ||||
| inline bool is_fcount_limit_enabled(finish_cond_e cond){ | ||||
|     return (cond & finish_cond_e::FCOUNT_LIMIT) == finish_cond_e::FCOUNT_LIMIT; | ||||
| } | ||||
|  | ||||
| inline bool is_jump_to_self_enabled(finish_cond_e cond){ | ||||
| @@ -357,7 +364,7 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){ | ||||
| } | ||||
|  | ||||
| template <typename ARCH> | ||||
| typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){ | ||||
| typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t count_limit){ | ||||
|     auto pc=start; | ||||
|     auto* PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]); | ||||
|     auto* NEXT_PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]); | ||||
| @@ -370,7 +377,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|     auto *const data = reinterpret_cast<uint8_t*>(&instr); | ||||
|  | ||||
|     while(!this->core.should_stop() && | ||||
|             !(is_count_limit_enabled(cond) && icount >= icount_limit)){ | ||||
|             !(is_icount_limit_enabled(cond) && icount >= count_limit) && | ||||
|             !(is_fcount_limit_enabled(cond) && fetch_count >= count_limit)){ | ||||
|         fetch_count++; | ||||
|         if(fetch_ins(pc, data)!=iss::Ok){ | ||||
|             this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max()); | ||||
|             pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0); | ||||
| @@ -399,7 +408,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -425,11 +434,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(*PC + (int32_t)imm); | ||||
|                                             *(X+rd) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int32_t)imm )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -451,7 +460,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(imm % traits::INSTR_ALIGNMENT) { | ||||
| @@ -459,9 +468,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                                         } | ||||
|                                         else { | ||||
|                                             if(rd != 0) { | ||||
|                                                 *(X+rd) = (uint32_t)(*PC +  4); | ||||
|                                                 *(X+rd) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(4 )); | ||||
|                                             } | ||||
|                                             *NEXT_PC = (uint32_t)(*PC + (int32_t)sext<21>(imm)); | ||||
|                                             *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int32_t)sext<21>(imm) )); | ||||
|                                             this->core.reg.last_branch = 1; | ||||
|                                         } | ||||
|                                     } | ||||
| @@ -485,17 +494,17 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t addr_mask = (uint32_t)- 2; | ||||
|                                         uint32_t new_pc = (uint32_t)((*(X+rs1) + (int16_t)sext<12>(imm)) & addr_mask); | ||||
|                                         uint32_t new_pc = (uint32_t)(((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )) & (int64_t)(addr_mask )); | ||||
|                                         if(new_pc % traits::INSTR_ALIGNMENT) { | ||||
|                                             raise(0, 0); | ||||
|                                         } | ||||
|                                         else { | ||||
|                                             if(rd != 0) { | ||||
|                                                 *(X+rd) = (uint32_t)(*PC +  4); | ||||
|                                                 *(X+rd) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(4 )); | ||||
|                                             } | ||||
|                                             *NEXT_PC = new_pc; | ||||
|                                             this->core.reg.last_branch = 1; | ||||
| @@ -521,15 +530,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs1) == *(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -554,15 +563,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs1) != *(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -587,15 +596,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if((int32_t)*(X+rs1) < (int32_t)*(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -620,15 +629,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if((int32_t)*(X+rs1) >= (int32_t)*(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -653,15 +662,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs1) < *(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -686,15 +695,15 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs1) >= *(X+rs2)) { | ||||
|                                             if(imm % traits::INSTR_ALIGNMENT) { | ||||
|                                             if((uint32_t)(imm ) % traits::INSTR_ALIGNMENT) { | ||||
|                                                 raise(0, 0); | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); | ||||
|                                                 *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<13>(imm) )); | ||||
|                                                 this->core.reg.last_branch = 1; | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -719,10 +728,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t load_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         int8_t res_27 = super::template read_mem<int8_t>(traits::MEM, load_address); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                         int8_t res = (int8_t)res_27; | ||||
| @@ -750,10 +759,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t load_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         int16_t res_28 = super::template read_mem<int16_t>(traits::MEM, load_address); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                         int16_t res = (int16_t)res_28; | ||||
| @@ -781,10 +790,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t load_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         int32_t res_29 = super::template read_mem<int32_t>(traits::MEM, load_address); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                         int32_t res = (int32_t)res_29; | ||||
| @@ -812,10 +821,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t load_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         uint8_t res_30 = super::template read_mem<uint8_t>(traits::MEM, load_address); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                         uint8_t res = res_30; | ||||
| @@ -843,10 +852,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t load_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         uint16_t res_31 = super::template read_mem<uint16_t>(traits::MEM, load_address); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                         uint16_t res = res_31; | ||||
| @@ -874,10 +883,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t store_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         super::template write_mem<uint8_t>(traits::MEM, store_address, (uint8_t)*(X+rs2)); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                     } | ||||
| @@ -901,10 +910,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t store_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         super::template write_mem<uint16_t>(traits::MEM, store_address, (uint16_t)*(X+rs2)); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                     } | ||||
| @@ -928,10 +937,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                         uint32_t store_address = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         super::template write_mem<uint32_t>(traits::MEM, store_address, (uint32_t)*(X+rs2)); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                     } | ||||
| @@ -955,11 +964,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); | ||||
|                                             *(X+rd) = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -982,7 +991,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1009,7 +1018,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1036,7 +1045,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1063,7 +1072,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1090,7 +1099,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1117,7 +1126,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1144,7 +1153,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1171,11 +1180,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> shamt); | ||||
|                                             *(X+rd) = ((uint32_t)((int32_t)*(X+rs1) >> shamt)); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1198,11 +1207,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(*(X+rs1) + *(X+rs2)); | ||||
|                                             *(X+rd) = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)(*(X+rs2) )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1225,11 +1234,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(*(X+rs1) - *(X+rs2)); | ||||
|                                             *(X+rd) = (uint32_t)((uint64_t)(*(X+rs1) ) - (uint64_t)(*(X+rs2) )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1252,11 +1261,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = *(X+rs1) << (*(X+rs2) & (traits::XLEN -  1)); | ||||
|                                             *(X+rd) = *(X+rs1) << ((uint64_t)(*(X+rs2) ) & ((uint64_t)(traits::XLEN ) - (uint64_t)(1 ))); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1279,7 +1288,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1306,7 +1315,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1333,7 +1342,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1360,11 +1369,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = *(X+rs1) >> (*(X+rs2) & (traits::XLEN -  1)); | ||||
|                                             *(X+rd) = *(X+rs1) >> ((uint64_t)(*(X+rs2) ) & ((uint64_t)(traits::XLEN ) - (uint64_t)(1 ))); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1387,11 +1396,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> (*(X+rs2) & (traits::XLEN -  1))); | ||||
|                                             *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> ((uint64_t)(*(X+rs2) ) & ((uint64_t)(traits::XLEN ) - (uint64_t)(1 )))); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -1414,7 +1423,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1441,7 +1450,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -1542,7 +1551,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t xrs1 = *(X+rs1); | ||||
| @@ -1579,7 +1588,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t res_33 = super::template read_mem<uint32_t>(traits::CSR, csr); | ||||
| @@ -1614,7 +1623,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t res_34 = super::template read_mem<uint32_t>(traits::CSR, csr); | ||||
| @@ -1649,7 +1658,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t res_35 = super::template read_mem<uint32_t>(traits::CSR, csr); | ||||
| @@ -1681,7 +1690,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t res_36 = super::template read_mem<uint32_t>(traits::CSR, csr); | ||||
| @@ -1715,7 +1724,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t res_37 = super::template read_mem<uint32_t>(traits::CSR, csr); | ||||
| @@ -1739,7 +1748,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     if(this->disass_enabled){ | ||||
|                         /* generate console output when executing the command */ | ||||
|                         auto mnemonic = fmt::format( | ||||
|                             "{mnemonic:10} {rs1}, {rd}, {imm}", fmt::arg("mnemonic", "fence_i"), | ||||
|                             "{mnemonic:10} {rs1}, {rd}, {imm}", fmt::arg("mnemonic", "fence.i"), | ||||
|                             fmt::arg("rs1", name(rs1)), fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); | ||||
|                         this->core.disass_output(pc.val, mnemonic); | ||||
|                     } | ||||
| @@ -1769,10 +1778,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         int64_t res = (int64_t)((int64_t)(int32_t)*(X+rs1) * (int64_t)(int32_t)*(X+rs2)); | ||||
|                                         int64_t res = (int64_t)((int32_t)*(X+rs1) ) * (int64_t)((int32_t)*(X+rs2) ); | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)res; | ||||
|                                         } | ||||
| @@ -1797,10 +1806,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         int64_t res = (int64_t)((int64_t)(int32_t)*(X+rs1) * (int64_t)(int32_t)*(X+rs2)); | ||||
|                                         int64_t res = (int64_t)((int32_t)*(X+rs1) ) * (int64_t)((int32_t)*(X+rs2) ); | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(res >> traits::XLEN); | ||||
|                                         } | ||||
| @@ -1825,10 +1834,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         int64_t res = (int64_t)((int64_t)(int32_t)*(X+rs1) * (uint64_t)*(X+rs2)); | ||||
|                                         int64_t res = (int64_t)((int32_t)*(X+rs1) ) * (int64_t)(*(X+rs2) ); | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(res >> traits::XLEN); | ||||
|                                         } | ||||
| @@ -1853,10 +1862,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint64_t res = (uint64_t)((uint64_t)*(X+rs1) * (uint64_t)*(X+rs2)); | ||||
|                                         uint64_t res = (uint64_t)(*(X+rs1) ) * (uint64_t)(*(X+rs2) ); | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(res >> traits::XLEN); | ||||
|                                         } | ||||
| @@ -1881,14 +1890,14 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         int32_t dividend = (int32_t)*(X+rs1); | ||||
|                                         int32_t divisor = (int32_t)*(X+rs2); | ||||
|                                         if(rd != 0) { | ||||
|                                             if(divisor != 0) { | ||||
|                                                 uint32_t MMIN = ((uint32_t)1) << (traits::XLEN - 1); | ||||
|                                                 uint32_t MMIN = ((uint32_t)1) << ((uint64_t)(traits::XLEN ) - (uint64_t)(1 )); | ||||
|                                                 if(*(X+rs1) == MMIN && divisor == - 1) { | ||||
|                                                     *(X+rd) = MMIN; | ||||
|                                                 } | ||||
| @@ -1921,12 +1930,12 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs2) != 0) { | ||||
|                                             if(rd != 0) { | ||||
|                                                 *(X+rd) = (uint32_t)(*(X+rs1) / *(X+rs2)); | ||||
|                                                 *(X+rd) = *(X+rs1) / *(X+rs2); | ||||
|                                             } | ||||
|                                         } | ||||
|                                         else { | ||||
| @@ -1955,11 +1964,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs2) != 0) { | ||||
|                                             uint32_t MMIN = (uint32_t)1 << (traits::XLEN - 1); | ||||
|                                             uint32_t MMIN = (uint32_t)1 << ((uint64_t)(traits::XLEN ) - (uint64_t)(1 )); | ||||
|                                             if(*(X+rs1) == MMIN && (int32_t)*(X+rs2) == - 1) { | ||||
|                                                 if(rd != 0) { | ||||
|                                                     *(X+rd) = 0; | ||||
| @@ -1967,7 +1976,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                                             } | ||||
|                                             else { | ||||
|                                                 if(rd != 0) { | ||||
|                                                     *(X+rd) = (uint32_t)((int32_t)*(X+rs1) % (int32_t)*(X+rs2)); | ||||
|                                                     *(X+rd) = ((uint32_t)((int32_t)*(X+rs1) % (int32_t)*(X+rs2))); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         } | ||||
| @@ -1997,7 +2006,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(*(X+rs2) != 0) { | ||||
| @@ -2030,10 +2039,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(imm) { | ||||
|                                         *(X+rd +  8) = (uint32_t)(*(X+2) + imm); | ||||
|                                         *(X+rd + 8) = (uint32_t)((uint64_t)(*(X+2) ) + (uint64_t)(imm )); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                 } | ||||
|                     break; | ||||
| @@ -2054,7 +2063,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         uint32_t offs = (uint32_t)(*(X+rs1 +  8) + uimm); | ||||
|                         uint32_t offs = (uint32_t)((uint64_t)(*(X+rs1 + 8) ) + (uint64_t)(uimm )); | ||||
|                         int32_t res_38 = super::template read_mem<int32_t>(traits::MEM, offs); | ||||
|                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                         *(X+rd + 8) = (uint32_t)(int32_t)res_38; | ||||
| @@ -2077,7 +2086,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         uint32_t offs = (uint32_t)(*(X+rs1 +  8) + uimm); | ||||
|                         uint32_t offs = (uint32_t)((uint64_t)(*(X+rs1 + 8) ) + (uint64_t)(uimm )); | ||||
|                         super::template write_mem<uint32_t>(traits::MEM, offs, (uint32_t)*(X+rs2 + 8)); | ||||
|                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                     } | ||||
| @@ -2099,11 +2108,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rs1 != 0) { | ||||
|                                             *(X+rs1) = (uint32_t)(*(X+rs1) + (int8_t)sext<6>(imm)); | ||||
|                                             *(X+rs1) = (uint32_t)((uint64_t)(*(X+rs1) ) + (uint64_t)((int8_t)sext<6>(imm) )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -2113,7 +2122,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     uint8_t nzimm = ((bit_sub<2,5>(instr)) | (bit_sub<12,1>(instr) << 5)); | ||||
|                     if(this->disass_enabled){ | ||||
|                         /* generate console output when executing the command */ | ||||
|                         this->core.disass_output(pc.val, "c__nop"); | ||||
|                         this->core.disass_output(pc.val, "c.nop"); | ||||
|                     } | ||||
|                     // used registers// calculate next pc value | ||||
|                     *NEXT_PC = *PC + 2; | ||||
| @@ -2136,8 +2145,8 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         *(X+1) = (uint32_t)(*PC +  2); | ||||
|                         *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<12>(imm)); | ||||
|                         *(X+1) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(2 )); | ||||
|                         *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                         this->core.reg.last_branch = 1; | ||||
|                     } | ||||
|                     break; | ||||
| @@ -2158,7 +2167,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -2184,7 +2193,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         if(imm == 0 || rd >= traits::RFS) { | ||||
|                             raise(0,  2); | ||||
|                             raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                         } | ||||
|                         if(rd != 0) { | ||||
|                             *(X+rd) = (uint32_t)((int32_t)sext<18>(imm)); | ||||
| @@ -2207,10 +2216,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(nzimm) { | ||||
|                                         *(X+2) = (uint32_t)(*(X+2) + (int16_t)sext<10>(nzimm)); | ||||
|                                         *(X+2) = (uint32_t)((uint64_t)(*(X+2) ) + (uint64_t)((int16_t)sext<10>(nzimm) )); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                 } | ||||
|                     break; | ||||
| @@ -2219,13 +2228,13 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     uint8_t rd = ((bit_sub<7,5>(instr))); | ||||
|                     if(this->disass_enabled){ | ||||
|                         /* generate console output when executing the command */ | ||||
|                         this->core.disass_output(pc.val, "__reserved_clui"); | ||||
|                         this->core.disass_output(pc.val, ".reserved_clui"); | ||||
|                     } | ||||
|                     // used registers// calculate next pc value | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     raise(0,  2); | ||||
|                                     raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                 } | ||||
|                     break; | ||||
|                 }// @suppress("No break at end of case") | ||||
| @@ -2289,7 +2298,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         *(X+rs1 +  8) = (uint32_t)(*(X+rs1 +  8) & (int8_t)sext<6>(imm)); | ||||
|                         *(X+rs1 + 8) = (uint32_t)(*(X+rs1 + 8) & (uint32_t)((int8_t)sext<6>(imm) )); | ||||
|                     } | ||||
|                     break; | ||||
|                 }// @suppress("No break at end of case") | ||||
| @@ -2308,7 +2317,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         *(X+rd +  8) = (uint32_t)(*(X+rd +  8) - *(X+rs2 +  8)); | ||||
|                         *(X+rd + 8) = (uint32_t)((uint64_t)(*(X+rd + 8) ) - (uint64_t)(*(X+rs2 + 8) )); | ||||
|                     } | ||||
|                     break; | ||||
|                 }// @suppress("No break at end of case") | ||||
| @@ -2382,7 +2391,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<12>(imm)); | ||||
|                                     *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<12>(imm) )); | ||||
|                                     this->core.reg.last_branch = 1; | ||||
|                                 } | ||||
|                     break; | ||||
| @@ -2403,7 +2412,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(*(X+rs1 + 8) == 0) { | ||||
|                                         *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<9>(imm)); | ||||
|                                         *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<9>(imm) )); | ||||
|                                         this->core.reg.last_branch = 1; | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -2425,7 +2434,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(*(X+rs1 + 8) != 0) { | ||||
|                                         *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<9>(imm)); | ||||
|                                         *NEXT_PC = (uint32_t)((uint64_t)(*PC ) + (uint64_t)((int16_t)sext<9>(imm) )); | ||||
|                                         this->core.reg.last_branch = 1; | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -2447,7 +2456,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rs1 != 0) { | ||||
| @@ -2473,10 +2482,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                         if(rd >= traits::RFS || rd == 0) { | ||||
|                             raise(0,  2); | ||||
|                             raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                         } | ||||
|                         else { | ||||
|                             uint32_t offs = (uint32_t)(*(X+2) + uimm); | ||||
|                             uint32_t offs = (uint32_t)((uint64_t)(*(X+2) ) + (uint64_t)(uimm )); | ||||
|                             int32_t res_39 = super::template read_mem<int32_t>(traits::MEM, offs); | ||||
|                             if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                             *(X+rd) = (uint32_t)(int32_t)res_39; | ||||
| @@ -2500,7 +2509,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
| @@ -2525,7 +2534,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs1 && rs1 < traits::RFS) { | ||||
|                                         *NEXT_PC = *(X+rs1 % traits::RFS) & ~ 0x1; | ||||
|                                         *NEXT_PC = *(X+(uint32_t)(rs1 ) % traits::RFS) & (uint32_t)(~ 1 ); | ||||
|                                         this->core.reg.last_branch = 1; | ||||
|                                     } | ||||
|                                     else { | ||||
| @@ -2537,7 +2546,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                 case arch::traits<ARCH>::opcode_e::__reserved_cmv: { | ||||
|                     if(this->disass_enabled){ | ||||
|                         /* generate console output when executing the command */ | ||||
|                         this->core.disass_output(pc.val, "__reserved_cmv"); | ||||
|                         this->core.disass_output(pc.val, ".reserved_cmv"); | ||||
|                     } | ||||
|                     // used registers// calculate next pc value | ||||
|                     *NEXT_PC = *PC + 2; | ||||
| @@ -2563,11 +2572,11 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rd >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         if(rd != 0) { | ||||
|                                             *(X+rd) = (uint32_t)(*(X+rd) + *(X+rs2)); | ||||
|                                             *(X+rd) = (uint32_t)((uint64_t)(*(X+rd) ) + (uint64_t)(*(X+rs2) )); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -2588,12 +2597,12 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs1 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t new_pc = *(X+rs1); | ||||
|                                         *(X+1) = (uint32_t)(*PC +  2); | ||||
|                                         *NEXT_PC = new_pc & ~ 0x1; | ||||
|                                         *(X+1) = (uint32_t)((uint64_t)(*PC ) + (uint64_t)(2 )); | ||||
|                                         *NEXT_PC = new_pc & (uint32_t)(~ 1 ); | ||||
|                                         this->core.reg.last_branch = 1; | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -2602,7 +2611,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                 case arch::traits<ARCH>::opcode_e::C__EBREAK: { | ||||
|                     if(this->disass_enabled){ | ||||
|                         /* generate console output when executing the command */ | ||||
|                         this->core.disass_output(pc.val, "c__ebreak"); | ||||
|                         this->core.disass_output(pc.val, "c.ebreak"); | ||||
|                     } | ||||
|                     // used registers// calculate next pc value | ||||
|                     *NEXT_PC = *PC + 2; | ||||
| @@ -2628,10 +2637,10 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     if(rs2 >= traits::RFS) { | ||||
|                                         raise(0,  2); | ||||
|                                         raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         uint32_t offs = (uint32_t)(*(X+2) + uimm); | ||||
|                                         uint32_t offs = (uint32_t)((uint64_t)(*(X+2) ) + (uint64_t)(uimm )); | ||||
|                                         super::template write_mem<uint32_t>(traits::MEM, offs, (uint32_t)*(X+rs2)); | ||||
|                                         if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); | ||||
|                                     } | ||||
| @@ -2647,7 +2656,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
|                     *NEXT_PC = *PC + 2; | ||||
|                     // execute instruction | ||||
|                     { | ||||
|                                     raise(0,  2); | ||||
|                                     raise(0, traits::RV_CAUSE_ILLEGAL_INSTRUCTION); | ||||
|                                 } | ||||
|                     break; | ||||
|                 }// @suppress("No break at end of case") | ||||
| @@ -2695,16 +2704,24 @@ std::unique_ptr<vm_if> create<arch::tgc5c>(arch::tgc5c *core, unsigned short por | ||||
| namespace iss { | ||||
| namespace { | ||||
| volatile std::array<bool, 2> dummy = { | ||||
|         core_factory::instance().register_creator("tgc5c|m_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         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); | ||||
| 		    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}}; | ||||
|         }), | ||||
|         core_factory::instance().register_creator("tgc5c|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|         core_factory::instance().register_creator("tgc5c|mu_p|interp", [](unsigned port, void* init_data) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<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}}; | ||||
|         }) | ||||
| }; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user